diff --git a/.gitattributes b/.gitattributes index 07f430e944..a217cb7bfe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ *.c.inc diff=c *.h.inc diff=c +*.m diff=objc *.py diff=python diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 0aa70213fb..0aea7ab84c 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -515,8 +515,6 @@ build-oss-fuzz: echo Testing ${fuzzer} ... ; "${fuzzer}" -runs=1 -seed=1 || exit 1 ; done - # Unrelated to fuzzer: run some tests with -fsanitize=address - - cd build-oss-fuzz && make check-qtest-i386 check-unit build-tci: extends: .native_build_job_template diff --git a/.gitlab-ci.d/cirrus/freebsd-12.vars b/.gitlab-ci.d/cirrus/freebsd-12.vars index 07f313aa3a..b4842271b2 100644 --- a/.gitlab-ci.d/cirrus/freebsd-12.vars +++ b/.gitlab-ci.d/cirrus/freebsd-12.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/freebsd-13.vars b/.gitlab-ci.d/cirrus/freebsd-13.vars index 8a648dda1e..546a82dd75 100644 --- a/.gitlab-ci.d/cirrus/freebsd-13.vars +++ b/.gitlab-ci.d/cirrus/freebsd-13.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-11.vars b/.gitlab-ci.d/cirrus/macos-11.vars index 08183f8793..cfe9181fd4 100644 --- a/.gitlab-ci.d/cirrus/macos-11.vars +++ b/.gitlab-ci.d/cirrus/macos-11.vars @@ -5,12 +5,12 @@ # https://gitlab.com/libvirt/libvirt-ci CCACHE='/usr/local/bin/ccache' -CPAN_PKGS='Test::Harness' +CPAN_PKGS='' CROSS_PKGS='' MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/usr/local/bin/pip3' -PKGS='bash bc bzip2 capstone ccache cpanminus ctags curl dbus diffutils dtc gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd' +PKGS='bash bc bzip2 capstone ccache ctags curl dbus diffutils dtc gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd' PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme virtualenv' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index a3b5b90552..e622ac2d21 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -21,18 +21,10 @@ amd64-debian-user-cross-container: arm64-debian-cross-container: extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] + stage: containers variables: NAME: debian-arm64-cross -arm64-test-debian-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian11-container'] - variables: - NAME: debian-arm64-test-cross - armel-debian-cross-container: extends: .container_job_template stage: containers-layer2 @@ -139,10 +131,16 @@ riscv64-debian-cross-container: variables: NAME: debian-riscv64-cross -s390x-debian-cross-container: +# we can however build TCG tests using a non-sid base +riscv64-debian-test-cross-container: extends: .container_job_template stage: containers-layer2 - needs: ['amd64-debian10-container'] + variables: + NAME: debian-riscv64-test-cross + +s390x-debian-cross-container: + extends: .container_job_template + stage: containers variables: NAME: debian-s390x-cross diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 056c374619..15aaccc481 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -14,6 +14,7 @@ variables: GIT_STRATEGY: clone include: - - local: '/.gitlab-ci.d/custom-runners/ubuntu-18.04-s390x.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch64.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml' - local: '/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml b/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml new file mode 100644 index 0000000000..9c589bc4cf --- /dev/null +++ b/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml @@ -0,0 +1,23 @@ +# All ubuntu-20.04 jobs should run successfully in an environment +# setup by the scripts/ci/setup/qemu/build-environment.yml task +# "Install basic packages to build QEMU on Ubuntu 18.04/20.04" + +ubuntu-20.04-aarch32-all: + needs: [] + stage: build + tags: + - ubuntu_20.04 + - aarch32 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH32_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure --cross-prefix=arm-linux-gnueabihf- + - make --output-sync -j`nproc` + - make --output-sync -j`nproc` check V=1 diff --git a/.gitlab-ci.d/custom-runners/ubuntu-18.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml similarity index 82% rename from .gitlab-ci.d/custom-runners/ubuntu-18.04-s390x.yml rename to .gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml index f39d874a1e..4f292a8a5b 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-18.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml @@ -1,13 +1,15 @@ -# All ubuntu-18.04 jobs should run successfully in an environment +# All ubuntu-20.04 jobs should run successfully in an environment # setup by the scripts/ci/setup/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 18.04/20.04" +# "Install basic packages to build QEMU on Ubuntu 20.04/20.04" -ubuntu-18.04-s390x-all-linux-static: +ubuntu-20.04-s390x-all-linux-static: needs: [] stage: build tags: - - ubuntu_18.04 + - ubuntu_20.04 - s390x + variables: + DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$S390X_RUNNER_AVAILABLE" @@ -21,12 +23,14 @@ ubuntu-18.04-s390x-all-linux-static: - make --output-sync -j`nproc` check V=1 - make --output-sync -j`nproc` check-tcg V=1 -ubuntu-18.04-s390x-all: +ubuntu-20.04-s390x-all: needs: [] stage: build tags: - - ubuntu_18.04 + - ubuntu_20.04 - s390x + variables: + DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$S390X_RUNNER_AVAILABLE" @@ -37,12 +41,14 @@ ubuntu-18.04-s390x-all: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check V=1 -ubuntu-18.04-s390x-alldbg: +ubuntu-20.04-s390x-alldbg: needs: [] stage: build tags: - - ubuntu_18.04 + - ubuntu_20.04 - s390x + variables: + DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -58,12 +64,14 @@ ubuntu-18.04-s390x-alldbg: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check V=1 -ubuntu-18.04-s390x-clang: +ubuntu-20.04-s390x-clang: needs: [] stage: build tags: - - ubuntu_18.04 + - ubuntu_20.04 - s390x + variables: + DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -78,12 +86,14 @@ ubuntu-18.04-s390x-clang: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check V=1 -ubuntu-18.04-s390x-tci: +ubuntu-20.04-s390x-tci: needs: [] stage: build tags: - - ubuntu_18.04 + - ubuntu_20.04 - s390x + variables: + DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -97,12 +107,14 @@ ubuntu-18.04-s390x-tci: - ../configure --disable-libssh --enable-tcg-interpreter - make --output-sync -j`nproc` -ubuntu-18.04-s390x-notcg: +ubuntu-20.04-s390x-notcg: needs: [] stage: build tags: - - ubuntu_18.04 + - ubuntu_20.04 - s390x + variables: + DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual diff --git a/.gitlab-ci.d/edk2/Dockerfile b/.gitlab-ci.d/edk2/Dockerfile index 13029310f6..bbe50ff832 100644 --- a/.gitlab-ci.d/edk2/Dockerfile +++ b/.gitlab-ci.d/edk2/Dockerfile @@ -1,7 +1,7 @@ # # Docker image to cross-compile EDK2 firmware binaries # -FROM ubuntu:16.04 +FROM ubuntu:18.04 MAINTAINER Philippe Mathieu-Daudé @@ -20,7 +20,7 @@ RUN apt update \ iasl \ make \ nasm \ - python \ + python3 \ uuid-dev \ && \ \ diff --git a/.mailmap b/.mailmap index 5113f55b3a..2976a675ea 100644 --- a/.mailmap +++ b/.mailmap @@ -28,7 +28,11 @@ Thiemo Seufer ths malc malc # Corrupted Author fields +Aaron Larson alarson@ddci.com +Andreas Färber Andreas Färber +Jason Wang Jason Wang Marek Dolata mkdolata@us.ibm.com +Michael Ellerman michael@ozlabs.org Nick Hudson hnick@vmware.com # There is also a: @@ -52,7 +56,8 @@ Alexander Graf Anthony Liguori Anthony Liguori Christian Borntraeger Filip Bozuta -Frederic Konrad +Frederic Konrad +Frederic Konrad Greg Kurz Huacai Chen Huacai Chen @@ -70,6 +75,7 @@ Yongbok Kim # Also list preferred name forms where people have changed their # git author config, or had utf8/latin1 encoding issues. Aaron Lindsay +Aaron Larson Alexey Gerasimenko Alex Chen Alex Ivanov @@ -144,6 +150,7 @@ Pan Nengyuan Pavel Dovgaluk Pavel Dovgaluk Pavel Dovgaluk +Peter Chubb Peter Crosthwaite Peter Crosthwaite Peter Crosthwaite diff --git a/.travis.yml b/.travis.yml index 41010ebe6b..9afc4a54b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,3 @@ -# The current Travis default is a VM based 16.04 Xenial on GCE -# Additional builds with specific requirements for a full VM need to -# be added as additional matrix: entries later on os: linux dist: focal language: c @@ -190,7 +187,7 @@ jobs: - name: "[s390x] GCC check-tcg" arch: s390x - dist: bionic + dist: focal addons: apt_packages: - libaio-dev @@ -221,6 +218,7 @@ jobs: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" - UNRELIABLE=true + - DFLTCC=0 script: - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? - | @@ -233,7 +231,7 @@ jobs: - name: "[s390x] GCC (other-softmmu)" arch: s390x - dist: bionic + dist: focal addons: apt_packages: - libaio-dev @@ -260,21 +258,22 @@ jobs: env: - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" - + - DFLTCC=0 - name: "[s390x] GCC (user)" arch: s390x - dist: bionic + dist: focal addons: apt_packages: - libgcrypt20-dev + - libglib2.0-dev - libgnutls28-dev - ninja-build env: - CONFIG="--disable-containers --disable-system" - + - DFLTCC=0 - name: "[s390x] Clang (disable-tcg)" arch: s390x - dist: bionic + dist: focal compiler: clang addons: apt_packages: @@ -305,3 +304,4 @@ jobs: - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools --host-cc=clang --cxx=clang++" - UNRELIABLE=true + - DFLTCC=0 diff --git a/MAINTAINERS b/MAINTAINERS index 37c0110d88..218c9459b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -305,7 +305,6 @@ S: Maintained F: target/s390x/ F: target/s390x/tcg F: hw/s390x/ -F: disas/s390.c F: tests/tcg/s390x/ L: qemu-s390x@nongnu.org @@ -548,6 +547,12 @@ F: include/*/*win32* X: qga/*win32* F: qemu.nsi +Darwin (macOS, iOS) +M: Philippe Mathieu-Daudé +S: Odd Fixes +F: .gitlab-ci.d/cirrus/macos-* +F: */*.m + Alpha Machines -------------- M: Richard Henderson @@ -642,7 +647,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/exynos* -F: include/hw/arm/exynos4210.h +F: include/hw/*/exynos* Calxeda Highbank M: Rob Herring @@ -936,6 +941,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 Xilinx Zynq M: Edgar E. Iglesias @@ -1527,7 +1533,7 @@ F: include/hw/rtc/sun4v-rtc.h Leon3 M: Fabien Chouteau -M: KONRAD Frederic +M: Frederic Konrad S: Maintained F: hw/sparc/leon3.c F: hw/*/grlib* @@ -1819,7 +1825,6 @@ F: docs/specs/acpi_hw_reduced_hotplug.rst ACPI/VIOT M: Jean-Philippe Brucker -R: Ani Sinha S: Supported F: hw/acpi/viot.c F: hw/acpi/viot.h @@ -2415,6 +2420,7 @@ F: audio/alsaaudio.c Core Audio framework backend M: Gerd Hoffmann +M: Philippe Mathieu-Daudé R: Christian Schoenebeck R: Akihiko Odaki S: Odd Fixes @@ -2501,7 +2507,7 @@ F: scsi/* Block Jobs M: John Snow -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Supported F: blockjob.c @@ -2515,9 +2521,12 @@ F: block/stream.c F: block/mirror.c F: qapi/job.json F: block/block-copy.c -F: include/block/block-copy.c +F: include/block/block-copy.h +F: block/reqlist.c +F: include/block/reqlist.h F: block/copy-before-write.h F: block/copy-before-write.c +F: block/snapshot-access.c F: include/block/aio_task.h F: block/aio_task.c F: util/qemu-co-shared-resource.c @@ -2537,7 +2546,7 @@ T: git https://repo.or.cz/qemu/armbru.git block-next Dirty Bitmaps M: Eric Blake -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy R: John Snow L: qemu-block@nongnu.org S: Supported @@ -2669,6 +2678,7 @@ F: util/drm.c Cocoa graphics M: Peter Maydell +M: Philippe Mathieu-Daudé R: Akihiko Odaki S: Odd Fixes F: ui/cocoa.m @@ -2747,26 +2757,26 @@ F: backends/cryptodev*.c Python library M: John Snow M: Cleber Rosa -R: Eduardo Habkost +R: Beraldo Leal S: Maintained F: python/ T: git https://gitlab.com/jsnow/qemu.git python Python scripts -M: Eduardo Habkost +M: John Snow M: Cleber Rosa S: Odd Fixes F: scripts/*.py F: tests/*.py Benchmark util -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy S: Maintained F: scripts/simplebench/ T: git https://src.openvz.org/scm/~vsementsov/qemu.git simplebench Transactions helper -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy S: Maintained F: include/qemu/transactions.h F: util/transactions.c @@ -3135,6 +3145,19 @@ F: include/hw/i2c/smbus_master.h F: include/hw/i2c/smbus_slave.h F: include/hw/i2c/smbus_eeprom.h +PMBus +M: Titus Rwantare +S: Maintained +F: hw/i2c/pmbus_device.c +F: hw/sensor/adm1272.c +F: hw/sensor/isl_pmbus_vr.c +F: hw/sensor/max34451.c +F: include/hw/i2c/pmbus_device.h +F: include/hw/sensor/isl_pmbus_vr.h +F: tests/qtest/adm1272-test.c +F: tests/qtest/max34451-test.c +F: tests/qtest/isl_pmbus_vr-test.c + Firmware schema specifications M: Philippe Mathieu-Daudé R: Daniel P. Berrange @@ -3144,7 +3167,7 @@ F: docs/interop/firmware.json EDK2 Firmware M: Philippe Mathieu-Daudé -R: Gerd Hoffmann +M: Gerd Hoffmann S: Supported F: hw/i386/*ovmf* F: pc-bios/descriptors/??-edk2-*.json @@ -3284,7 +3307,6 @@ S390 TCG target M: Richard Henderson S: Maintained F: tcg/s390/ -F: disas/s390.c L: qemu-s390x@nongnu.org SPARC TCG target @@ -3337,7 +3359,7 @@ F: block/iscsi-opts.c Network Block Device (NBD) M: Eric Blake -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Maintained F: block/nbd* @@ -3433,7 +3455,7 @@ F: block/dmg.c parallels M: Stefan Hajnoczi M: Denis V. Lunev -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Supported F: block/parallels.c @@ -3549,6 +3571,7 @@ S: Maintained F: semihosting/ F: include/semihosting/ F: tests/tcg/multiarch/arm-compat-semi/ +F: tests/tcg/aarch64/system/semiheap.c Multi-process QEMU M: Elena Ufimtseva @@ -3591,7 +3614,7 @@ M: Thomas Huth R: Wainer dos Santos Moschetta R: Beraldo Leal S: Maintained -F: .github/lockdown.yml +F: .github/workflows/lockdown.yml F: .gitlab-ci.yml F: .gitlab-ci.d/ F: .travis.yml @@ -3607,7 +3630,8 @@ FreeBSD Hosted Continuous Integration M: Ed Maste M: Li-Wen Hsu S: Maintained -F: .cirrus.yml +F: .gitlab-ci.d/cirrus/freebsd* +F: tests/vm/freebsd W: https://cirrus-ci.com/github/qemu/qemu Windows Hosted Continuous Integration diff --git a/VERSION b/VERSION index 8e8e37d75b..59f2fcbfc0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.2.50 +7.0.50 diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 54457c76c2..a70e2eb375 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -122,7 +122,7 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) MemoryRegion *area = section->mr; bool writeable = !area->readonly && !area->rom_device; hv_memory_flags_t flags; - uint64_t page_size = qemu_real_host_page_size; + uint64_t page_size = qemu_real_host_page_size(); if (!memory_region_is_ram(area)) { if (writeable) { diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index f185b0830a..0043f4d308 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 7516c67a3f..c4244a23c6 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -74,11 +74,23 @@ static void kvm_start_vcpu_thread(CPUState *cpu) cpu, QEMU_THREAD_JOINABLE); } +static bool kvm_vcpu_thread_is_idle(CPUState *cpu) +{ + return !kvm_halt_in_kernel(); +} + +static bool kvm_cpus_are_resettable(void) +{ + return !kvm_enabled() || kvm_cpu_check_are_resettable(); +} + static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); ops->create_vcpu_thread = kvm_start_vcpu_thread; + ops->cpu_thread_is_idle = kvm_vcpu_thread_is_idle; + ops->cpus_are_resettable = kvm_cpus_are_resettable; ops->synchronize_post_reset = kvm_cpu_synchronize_post_reset; ops->synchronize_post_init = kvm_cpu_synchronize_post_init; ops->synchronize_state = kvm_cpu_synchronize_state; diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 0e66ebb497..32e177bd26 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -59,7 +59,7 @@ #ifdef PAGE_SIZE #undef PAGE_SIZE #endif -#define PAGE_SIZE qemu_real_host_page_size +#define PAGE_SIZE qemu_real_host_page_size() #ifndef KVM_GUESTDBG_BLOCKIRQ #define KVM_GUESTDBG_BLOCKIRQ 0 @@ -324,14 +324,14 @@ static hwaddr kvm_align_section(MemoryRegionSection *section, with sub-page size and unaligned start address. Pad the start address to next and truncate size to previous page boundary. */ aligned = ROUND_UP(section->offset_within_address_space, - qemu_real_host_page_size); + qemu_real_host_page_size()); delta = aligned - section->offset_within_address_space; *start = aligned; if (delta > size) { return 0; } - return (size - delta) & qemu_real_host_page_mask; + return (size - delta) & qemu_real_host_page_mask(); } int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, @@ -626,7 +626,7 @@ static void kvm_log_stop(MemoryListener *listener, static void kvm_slot_sync_dirty_pages(KVMSlot *slot) { ram_addr_t start = slot->ram_start_offset; - ram_addr_t pages = slot->memory_size / qemu_real_host_page_size; + ram_addr_t pages = slot->memory_size / qemu_real_host_page_size(); cpu_physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages); } @@ -662,7 +662,7 @@ static void kvm_slot_init_dirty_bitmap(KVMSlot *mem) * And mem->memory_size is aligned to it (otherwise this mem can't * be registered to KVM). */ - hwaddr bitmap_size = ALIGN(mem->memory_size / qemu_real_host_page_size, + hwaddr bitmap_size = ALIGN(mem->memory_size / qemu_real_host_page_size(), /*HOST_LONG_BITS*/ 64) / 8; mem->dirty_bmap = g_malloc0(bitmap_size); mem->dirty_bmap_size = bitmap_size; @@ -707,7 +707,7 @@ static void kvm_dirty_ring_mark_page(KVMState *s, uint32_t as_id, mem = &kml->slots[slot_id]; if (!mem->memory_size || offset >= - (mem->memory_size / qemu_real_host_page_size)) { + (mem->memory_size / qemu_real_host_page_size())) { return; } @@ -895,7 +895,7 @@ static void kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, /* Alignment requirement for KVM_CLEAR_DIRTY_LOG - 64 pages */ #define KVM_CLEAR_LOG_SHIFT 6 -#define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size << KVM_CLEAR_LOG_SHIFT) +#define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size() << KVM_CLEAR_LOG_SHIFT) #define KVM_CLEAR_LOG_MASK (-KVM_CLEAR_LOG_ALIGN) static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start, @@ -904,7 +904,7 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start, KVMState *s = kvm_state; uint64_t end, bmap_start, start_delta, bmap_npages; struct kvm_clear_dirty_log d; - unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size; + unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size(); int ret; /* @@ -1202,8 +1202,8 @@ void kvm_hwpoison_page_add(ram_addr_t ram_addr) static uint32_t adjust_ioeventfd_endianness(uint32_t val, uint32_t size) { -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) - /* The kernel expects ioeventfd values in HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN + /* The kernel expects ioeventfd values in HOST_BIG_ENDIAN * endianness, but the memory core hands them in target endianness. * For example, PPC is always treated as big-endian even if running * on KVM and on PPC64LE. Correct here. @@ -1335,7 +1335,7 @@ kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list) void kvm_set_max_memslot_size(hwaddr max_slot_size) { g_assert( - ROUND_UP(max_slot_size, qemu_real_host_page_size) == max_slot_size + ROUND_UP(max_slot_size, qemu_real_host_page_size()) == max_slot_size ); kvm_max_slot_size = max_slot_size; } @@ -1646,7 +1646,7 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, { int i; - kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot)); + kml->slots = g_new0(KVMSlot, s->nr_slots); kml->as_id = as_id; for (i = 0; i < s->nr_slots; i++) { @@ -1941,7 +1941,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) return virq; } - route = g_malloc0(sizeof(KVMMSIRoute)); + route = g_new0(KVMMSIRoute, 1); route->kroute.gsi = virq; route->kroute.type = KVM_IRQ_ROUTING_MSI; route->kroute.flags = 0; @@ -1961,10 +1961,11 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) return kvm_set_irq(s, route->kroute.gsi, 1); } -int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) +int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) { struct kvm_irq_routing_entry kroute = {}; int virq; + KVMState *s = c->s; MSIMessage msg = {0, 0}; if (pci_available && dev) { @@ -2004,7 +2005,7 @@ int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) kvm_add_routing_entry(s, &kroute); kvm_arch_add_msi_route_post(&kroute, vector, dev); - kvm_irqchip_commit_routes(s); + c->changes++; return virq; } @@ -2162,7 +2163,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) abort(); } -int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) +int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) { return -ENOSYS; } @@ -2340,7 +2341,7 @@ static int kvm_init(MachineState *ms) * even with KVM. TARGET_PAGE_SIZE is assumed to be the minimum * page size for the system though. */ - assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size); + assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size()); s->sigmask_len = 8; @@ -3243,7 +3244,7 @@ int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, return 0; } - bp = g_malloc(sizeof(struct kvm_sw_breakpoint)); + bp = g_new(struct kvm_sw_breakpoint, 1); bp->pc = addr; bp->use_count = 1; err = kvm_arch_insert_sw_breakpoint(cpu, bp); diff --git a/accel/meson.build b/accel/meson.build index dfd808d2c8..b9a963cf80 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -2,12 +2,14 @@ specific_ss.add(files('accel-common.c')) softmmu_ss.add(files('accel-softmmu.c')) user_ss.add(files('accel-user.c')) -subdir('hvf') -subdir('qtest') -subdir('kvm') subdir('tcg') -subdir('xen') -subdir('stubs') +if have_system + subdir('hvf') + subdir('qtest') + subdir('kvm') + subdir('xen') + subdir('stubs') +endif dummy_ss = ss.source_set() dummy_ss.add(files( diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 7e6b8110d5..f6056ac836 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -20,7 +20,6 @@ #include "qemu/accel.h" #include "sysemu/qtest.h" #include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" diff --git a/accel/stubs/hax-stub.c b/accel/stubs/hax-stub.c index 49077f88e3..2fe31aaa9a 100644 --- a/accel/stubs/hax-stub.c +++ b/accel/stubs/hax-stub.c @@ -16,6 +16,8 @@ #include "qemu/osdep.h" #include "sysemu/hax.h" +bool hax_allowed; + int hax_sync_vcpus(void) { return 0; diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 5319573e00..3345882d85 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -12,10 +12,7 @@ #include "qemu/osdep.h" #include "sysemu/kvm.h" - -#ifndef CONFIG_USER_ONLY #include "hw/pci/msi.h" -#endif KVMState *kvm_state; bool kvm_kernel_irqchip; @@ -80,8 +77,7 @@ int kvm_on_sigbus(int code, void *addr) return 1; } -#ifndef CONFIG_USER_ONLY -int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) +int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) { return -ENOSYS; } @@ -152,4 +148,3 @@ bool kvm_dirty_ring_enabled(void) { return false; } -#endif diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 12dd1539af..0249b9258f 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -1,4 +1,7 @@ -specific_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c')) -specific_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) -specific_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) -specific_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) +sysemu_stubs_ss = ss.source_set() +sysemu_stubs_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c')) +sysemu_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) +sysemu_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) +sysemu_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) + +specific_ss.add_all(when: ['CONFIG_SOFTMMU'], if_true: sysemu_stubs_ss) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index d8162673ae..ea4a0dd2fb 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -28,12 +28,12 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, g_assert_not_reached(); } -void QEMU_NORETURN cpu_loop_exit(CPUState *cpu) +G_NORETURN void cpu_loop_exit(CPUState *cpu) { g_assert_not_reached(); } -void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) +G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) { g_assert_not_reached(); } diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index fc165031e8..404a530f7c 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -63,7 +63,7 @@ the ATOMIC_NAME macro, and redefined below. */ #if DATA_SIZE == 1 # define END -#elif defined(HOST_WORDS_BIGENDIAN) +#elif HOST_BIG_ENDIAN # define END _be #else # define END _le @@ -196,7 +196,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) /* Define reverse-host-endian atomic operations. Note that END is used within the ATOMIC_NAME macro. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN # define END _le #else # define END _be diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 45a6891fa3..eaa712b163 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/qemu-print.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" @@ -215,17 +214,19 @@ static inline void log_cpu_exec(target_ulong pc, CPUState *cpu, #if defined(DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { - FILE *logfile = qemu_log_lock(); - int flags = 0; + FILE *logfile = qemu_log_trylock(); + if (logfile) { + int flags = 0; - if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { - flags |= CPU_DUMP_FPU; - } + if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { + flags |= CPU_DUMP_FPU; + } #if defined(TARGET_I386) - flags |= CPU_DUMP_CCOP; + flags |= CPU_DUMP_CCOP; #endif - log_cpu_state(cpu, flags); - qemu_log_unlock(logfile); + cpu_dump_state(cpu, logfile, flags); + qemu_log_unlock(logfile); + } } #endif /* DEBUG_DISAS */ } @@ -422,7 +423,7 @@ static void cpu_exec_exit(CPUState *cpu) void cpu_exec_step_atomic(CPUState *cpu) { - CPUArchState *env = (CPUArchState *)cpu->env_ptr; + CPUArchState *env = cpu->env_ptr; TranslationBlock *tb; target_ulong cs_base, pc; uint32_t flags, cflags; @@ -532,7 +533,7 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, struct tb_desc desc; uint32_t h; - desc.env = (CPUArchState *)cpu->env_ptr; + desc.env = cpu->env_ptr; desc.cs_base = cs_base; desc.flags = flags; desc.cflags = cflags; @@ -811,8 +812,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * raised when single-stepping so that GDB doesn't miss the * next instruction. */ - cpu->exception_index = - (cpu->singlestep_enabled ? EXCP_DEBUG : -1); + if (unlikely(cpu->singlestep_enabled)) { + cpu->exception_index = EXCP_DEBUG; + qemu_mutex_unlock_iothread(); + return true; + } + cpu->exception_index = -1; *last_tb = NULL; } /* The target hook may have updated the 'cpu->interrupt_request'; diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 3b918fe018..f90f4312ea 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1761,7 +1761,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, MemOpIdx oi, int size, int prot, uintptr_t retaddr) { - size_t mmu_idx = get_mmuidx(oi); + uintptr_t mmu_idx = get_mmuidx(oi); MemOp mop = get_memop(oi); int a_bits = get_alignment_bits(mop); uintptr_t index; @@ -1769,6 +1769,8 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, target_ulong tlb_addr; void *hostaddr; + tcg_debug_assert(mmu_idx < NB_MMU_MODES); + /* Adjust the given return address. */ retaddr -= GETPC_ADJ; @@ -1908,18 +1910,20 @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi, uintptr_t retaddr, MemOp op, bool code_read, FullLoadHelper *full_load) { - uintptr_t mmu_idx = get_mmuidx(oi); - uintptr_t index = tlb_index(env, mmu_idx, addr); - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); - target_ulong tlb_addr = code_read ? entry->addr_code : entry->addr_read; const size_t tlb_off = code_read ? offsetof(CPUTLBEntry, addr_code) : offsetof(CPUTLBEntry, addr_read); const MMUAccessType access_type = code_read ? MMU_INST_FETCH : MMU_DATA_LOAD; - unsigned a_bits = get_alignment_bits(get_memop(oi)); + const unsigned a_bits = get_alignment_bits(get_memop(oi)); + const size_t size = memop_size(op); + uintptr_t mmu_idx = get_mmuidx(oi); + uintptr_t index; + CPUTLBEntry *entry; + target_ulong tlb_addr; void *haddr; uint64_t res; - size_t size = memop_size(op); + + tcg_debug_assert(mmu_idx < NB_MMU_MODES); /* Handle CPU specific unaligned behaviour */ if (addr & ((1 << a_bits) - 1)) { @@ -1927,6 +1931,10 @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi, mmu_idx, retaddr); } + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); + tlb_addr = code_read ? entry->addr_code : entry->addr_read; + /* If the TLB entry is for a different page, reload and try again. */ if (!tlb_hit(tlb_addr, addr)) { if (!victim_tlb_hit(env, mmu_idx, index, tlb_off, @@ -2174,7 +2182,7 @@ uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr addr, uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) { - return cpu_load_helper(env, addr, oi, MO_BEUQ, helper_be_ldq_mmu); + return cpu_load_helper(env, addr, oi, ra, helper_be_ldq_mmu); } uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr addr, @@ -2310,14 +2318,16 @@ static inline void QEMU_ALWAYS_INLINE store_helper(CPUArchState *env, target_ulong addr, uint64_t val, MemOpIdx oi, uintptr_t retaddr, MemOp op) { - uintptr_t mmu_idx = get_mmuidx(oi); - uintptr_t index = tlb_index(env, mmu_idx, addr); - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); - target_ulong tlb_addr = tlb_addr_write(entry); const size_t tlb_off = offsetof(CPUTLBEntry, addr_write); - unsigned a_bits = get_alignment_bits(get_memop(oi)); + const unsigned a_bits = get_alignment_bits(get_memop(oi)); + const size_t size = memop_size(op); + uintptr_t mmu_idx = get_mmuidx(oi); + uintptr_t index; + CPUTLBEntry *entry; + target_ulong tlb_addr; void *haddr; - size_t size = memop_size(op); + + tcg_debug_assert(mmu_idx < NB_MMU_MODES); /* Handle CPU specific unaligned behaviour */ if (addr & ((1 << a_bits) - 1)) { @@ -2325,6 +2335,10 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, mmu_idx, retaddr); } + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); + tlb_addr = tlb_addr_write(entry); + /* If the TLB entry is for a different page, reload and try again. */ if (!tlb_hit(tlb_addr, addr)) { if (!victim_tlb_hit(env, mmu_idx, index, tlb_off, @@ -2552,7 +2566,6 @@ void cpu_stq_le_mmu(CPUArchState *env, target_ulong addr, uint64_t val, glue(glue(glue(cpu_atomic_ ## X, SUFFIX), END), _mmu) #define ATOMIC_MMU_CLEANUP -#define ATOMIC_MMU_IDX get_mmuidx(oi) #include "atomic_common.c.inc" diff --git a/accel/tcg/hmp.c b/accel/tcg/hmp.c index d2ea352655..bb67941420 100644 --- a/accel/tcg/hmp.c +++ b/accel/tcg/hmp.c @@ -4,7 +4,6 @@ #include "qapi/qapi-commands-machine.h" #include "exec/exec-all.h" #include "monitor/monitor.h" -#include "sysemu/tcg.h" static void hmp_tcg_register(void) { diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h index 881bc1ede0..3092bfa964 100644 --- a/accel/tcg/internal.h +++ b/accel/tcg/internal.h @@ -14,8 +14,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc, target_ulong cs_base, uint32_t flags, int cflags); - -void QEMU_NORETURN cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); +G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); void page_init(void); void tb_htable_init(void); diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c index ea42d1d51b..24520ea112 100644 --- a/accel/tcg/tcg-accel-ops-icount.c +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -24,9 +24,8 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/tcg.h" #include "sysemu/replay.h" +#include "sysemu/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "exec/exec-all.h" diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 29632bd4c0..d50239e0e2 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -24,9 +24,9 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "sysemu/tcg.h" #include "sysemu/replay.h" +#include "sysemu/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" @@ -142,7 +142,7 @@ void mttcg_start_vcpu_thread(CPUState *cpu) g_assert(tcg_enabled()); tcg_cpu_init_cflags(cpu, current_machine->smp.max_cpus > 1); - cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->thread = g_new0(QemuThread, 1); cpu->halt_cond = g_malloc0(sizeof(QemuCond)); qemu_cond_init(cpu->halt_cond); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index bf59f53dbc..1a72149f0e 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -24,9 +24,9 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "sysemu/tcg.h" #include "sysemu/replay.h" +#include "sysemu/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" @@ -279,8 +279,8 @@ void rr_start_vcpu_thread(CPUState *cpu) tcg_cpu_init_cflags(cpu, false); if (!single_tcg_cpu_thread) { - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + cpu->thread = g_new0(QemuThread, 1); + cpu->halt_cond = g_new0(QemuCond, 1); qemu_cond_init(cpu->halt_cond); /* share a single thread for all cpus with TCG */ diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 1a8e8390bd..684dc5a137 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -26,9 +26,9 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "sysemu/tcg.h" #include "sysemu/replay.h" +#include "sysemu/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "exec/exec-all.h" diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index d6336a9c96..47952eecd7 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "sysemu/tcg.h" #include "sysemu/cpu-timers.h" #include "tcg/tcg.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 9876242731..7b1dfe5f15 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #define NO_CPU_IO_DEFS #include "trace.h" @@ -2096,69 +2095,75 @@ TranslationBlock *tb_gen_code(CPUState *cpu, #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && qemu_log_in_addr_range(tb->pc)) { - FILE *logfile = qemu_log_lock(); - int code_size, data_size; - const tcg_target_ulong *rx_data_gen_ptr; - size_t chunk_start; - int insn = 0; + FILE *logfile = qemu_log_trylock(); + if (logfile) { + int code_size, data_size; + const tcg_target_ulong *rx_data_gen_ptr; + size_t chunk_start; + int insn = 0; - if (tcg_ctx->data_gen_ptr) { - rx_data_gen_ptr = tcg_splitwx_to_rx(tcg_ctx->data_gen_ptr); - code_size = (const void *)rx_data_gen_ptr - tb->tc.ptr; - data_size = gen_code_size - code_size; - } else { - rx_data_gen_ptr = 0; - code_size = gen_code_size; - data_size = 0; - } - - /* Dump header and the first instruction */ - qemu_log("OUT: [size=%d]\n", gen_code_size); - qemu_log(" -- guest addr 0x" TARGET_FMT_lx " + tb prologue\n", - tcg_ctx->gen_insn_data[insn][0]); - chunk_start = tcg_ctx->gen_insn_end_off[insn]; - log_disas(tb->tc.ptr, chunk_start); - - /* - * Dump each instruction chunk, wrapping up empty chunks into - * the next instruction. The whole array is offset so the - * first entry is the beginning of the 2nd instruction. - */ - while (insn < tb->icount) { - size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; - if (chunk_end > chunk_start) { - qemu_log(" -- guest addr 0x" TARGET_FMT_lx "\n", - tcg_ctx->gen_insn_data[insn][0]); - log_disas(tb->tc.ptr + chunk_start, chunk_end - chunk_start); - chunk_start = chunk_end; + if (tcg_ctx->data_gen_ptr) { + rx_data_gen_ptr = tcg_splitwx_to_rx(tcg_ctx->data_gen_ptr); + code_size = (const void *)rx_data_gen_ptr - tb->tc.ptr; + data_size = gen_code_size - code_size; + } else { + rx_data_gen_ptr = 0; + code_size = gen_code_size; + data_size = 0; } - insn++; - } - if (chunk_start < code_size) { - qemu_log(" -- tb slow paths + alignment\n"); - log_disas(tb->tc.ptr + chunk_start, code_size - chunk_start); - } + /* Dump header and the first instruction */ + fprintf(logfile, "OUT: [size=%d]\n", gen_code_size); + fprintf(logfile, + " -- guest addr 0x" TARGET_FMT_lx " + tb prologue\n", + tcg_ctx->gen_insn_data[insn][0]); + chunk_start = tcg_ctx->gen_insn_end_off[insn]; + disas(logfile, tb->tc.ptr, chunk_start); - /* Finally dump any data we may have after the block */ - if (data_size) { - int i; - qemu_log(" data: [size=%d]\n", data_size); - for (i = 0; i < data_size / sizeof(tcg_target_ulong); i++) { - if (sizeof(tcg_target_ulong) == 8) { - qemu_log("0x%08" PRIxPTR ": .quad 0x%016" TCG_PRIlx "\n", - (uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]); - } else if (sizeof(tcg_target_ulong) == 4) { - qemu_log("0x%08" PRIxPTR ": .long 0x%08" TCG_PRIlx "\n", - (uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]); - } else { - qemu_build_not_reached(); + /* + * Dump each instruction chunk, wrapping up empty chunks into + * the next instruction. The whole array is offset so the + * first entry is the beginning of the 2nd instruction. + */ + while (insn < tb->icount) { + size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; + if (chunk_end > chunk_start) { + fprintf(logfile, " -- guest addr 0x" TARGET_FMT_lx "\n", + tcg_ctx->gen_insn_data[insn][0]); + disas(logfile, tb->tc.ptr + chunk_start, + chunk_end - chunk_start); + chunk_start = chunk_end; + } + insn++; + } + + if (chunk_start < code_size) { + fprintf(logfile, " -- tb slow paths + alignment\n"); + disas(logfile, tb->tc.ptr + chunk_start, + code_size - chunk_start); + } + + /* Finally dump any data we may have after the block */ + if (data_size) { + int i; + fprintf(logfile, " data: [size=%d]\n", data_size); + for (i = 0; i < data_size / sizeof(tcg_target_ulong); i++) { + if (sizeof(tcg_target_ulong) == 8) { + fprintf(logfile, + "0x%08" PRIxPTR ": .quad 0x%016" TCG_PRIlx "\n", + (uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]); + } else if (sizeof(tcg_target_ulong) == 4) { + fprintf(logfile, + "0x%08" PRIxPTR ": .long 0x%08" TCG_PRIlx "\n", + (uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]); + } else { + qemu_build_not_reached(); + } } } + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); } - qemu_log("\n"); - qemu_log_flush(); - qemu_log_unlock(logfile); } #endif diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index ccaf171737..5b6ea35c59 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -185,11 +185,13 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(db->pc_first)) { - FILE *logfile = qemu_log_lock(); - qemu_log("----------------\n"); - ops->disas_log(db, cpu); - qemu_log("\n"); - qemu_log_unlock(logfile); + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "----------------\n"); + ops->disas_log(db, cpu, logfile); + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); + } } #endif } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 8edf0bbaa1..ac57324d4f 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -506,7 +506,6 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, #define ATOMIC_NAME(X) \ glue(glue(glue(cpu_atomic_ ## X, SUFFIX), END), _mmu) #define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0) -#define ATOMIC_MMU_IDX MMU_USER_IDX #define DATA_SIZE 1 #include "atomic_template.h" diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 2b9789e647..4a61378cd7 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -72,7 +72,7 @@ struct alsa_params_obt { snd_pcm_uframes_t samples; }; -static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) +static void G_GNUC_PRINTF (2, 3) alsa_logerr (int err, const char *fmt, ...) { va_list ap; @@ -83,7 +83,7 @@ static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); } -static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( +static void G_GNUC_PRINTF (3, 4) alsa_logerr2 ( int err, const char *typ, const char *fmt, @@ -916,6 +916,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { .init_out = alsa_init_out, .fini_out = alsa_fini_out, .write = alsa_write, + .buffer_get_free = audio_generic_buffer_get_free, .run_buffer_out = audio_generic_run_buffer_out, .enable_out = alsa_enable_out, diff --git a/audio/audio.c b/audio/audio.c index dc28685d22..9e91a5a4f2 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -32,7 +32,7 @@ #include "qapi/qapi-visit-audio.h" #include "qemu/cutils.h" #include "qemu/module.h" -#include "qemu-common.h" +#include "sysemu/sysemu.h" #include "sysemu/replay.h" #include "sysemu/runstate.h" #include "ui/qemu-spice.h" @@ -117,7 +117,6 @@ int audio_bug (const char *funcname, int cond) AUD_log (NULL, "I am sorry\n"); } AUD_log (NULL, "Context:\n"); - abort(); } return cond; @@ -138,7 +137,7 @@ static inline int audio_bits_to_index (int bits) default: audio_bug ("bits_to_index", 1); AUD_log (NULL, "invalid bits %d\n", bits); - return 0; + abort(); } } @@ -156,7 +155,7 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size) AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n", funcname); AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len); - return NULL; + abort(); } return g_malloc0 (len); @@ -543,70 +542,50 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw) size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); if (audio_bug(__func__, live > hw->conv_buf->size)) { dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); - return 0; + abort(); } return live; } -static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len) +static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples) { - size_t clipped = 0; - size_t pos = hw->mix_buf->pos; + size_t conv = 0; + STSampleBuffer *conv_buf = hw->conv_buf; - while (len) { - st_sample *src = hw->mix_buf->samples + pos; - uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame); - size_t samples_till_end_of_buf = hw->mix_buf->size - pos; - size_t samples_to_clip = MIN(len, samples_till_end_of_buf); + while (samples) { + uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame); + size_t proc = MIN(samples, conv_buf->size - conv_buf->pos); - hw->clip(dst, src, samples_to_clip); - - pos = (pos + samples_to_clip) % hw->mix_buf->size; - len -= samples_to_clip; - clipped += samples_to_clip; + hw->conv(conv_buf->samples + conv_buf->pos, src, proc); + conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size; + samples -= proc; + conv += proc; } + + return conv; } /* * Soft voice (capture) */ -static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw) -{ - HWVoiceIn *hw = sw->hw; - ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired; - ssize_t rpos; - - if (audio_bug(__func__, live < 0 || live > hw->conv_buf->size)) { - dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); - return 0; - } - - rpos = hw->conv_buf->pos - live; - if (rpos >= 0) { - return rpos; - } else { - return hw->conv_buf->size + rpos; - } -} - static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size) { HWVoiceIn *hw = sw->hw; size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; struct st_sample *src, *dst = sw->buf; - rpos = audio_pcm_sw_get_rpos_in(sw) % hw->conv_buf->size; - live = hw->total_samples_captured - sw->total_hw_samples_acquired; - if (audio_bug(__func__, live > hw->conv_buf->size)) { - dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); - return 0; - } - - samples = size / sw->info.bytes_per_frame; if (!live) { return 0; } + if (audio_bug(__func__, live > hw->conv_buf->size)) { + dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); + abort(); + } + + rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size); + + samples = size / sw->info.bytes_per_frame; swlim = (live * sw->ratio) >> 32; swlim = MIN (swlim, samples); @@ -632,7 +611,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size) total += isamp; } - if (hw->pcm_ops && !hw->pcm_ops->volume_in) { + if (!hw->pcm_ops->volume_in) { mixeng_volume (sw->buf, ret, &sw->vol); } @@ -676,19 +655,45 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live) if (audio_bug(__func__, live > hw->mix_buf->size)) { dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size); - return 0; + abort(); } return live; } return 0; } +static size_t audio_pcm_hw_get_free(HWVoiceOut *hw) +{ + return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) : + INT_MAX) / hw->info.bytes_per_frame; +} + +static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len) +{ + size_t clipped = 0; + size_t pos = hw->mix_buf->pos; + + while (len) { + st_sample *src = hw->mix_buf->samples + pos; + uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame); + size_t samples_till_end_of_buf = hw->mix_buf->size - pos; + size_t samples_to_clip = MIN(len, samples_till_end_of_buf); + + hw->clip(dst, src, samples_to_clip); + + pos = (pos + samples_to_clip) % hw->mix_buf->size; + len -= samples_to_clip; + clipped += samples_to_clip; + } +} + /* * Soft voice (playback) */ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) { - size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; + size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, blck; + size_t hw_free; size_t ret = 0, pos = 0, total = 0; if (!sw) { @@ -700,7 +705,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) live = sw->total_hw_samples_mixed; if (audio_bug(__func__, live > hwsamples)) { dolog("live=%zu hw->mix_buf->size=%zu\n", live, hwsamples); - return 0; + abort(); } if (live == hwsamples) { @@ -711,27 +716,28 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) } wpos = (sw->hw->mix_buf->pos + live) % hwsamples; - samples = size / sw->info.bytes_per_frame; dead = hwsamples - live; - swlim = ((int64_t) dead << 32) / sw->ratio; - swlim = MIN (swlim, samples); - if (swlim) { - sw->conv (sw->buf, buf, swlim); + hw_free = audio_pcm_hw_get_free(sw->hw); + hw_free = hw_free > live ? hw_free - live : 0; + samples = ((int64_t)MIN(dead, hw_free) << 32) / sw->ratio; + samples = MIN(samples, size / sw->info.bytes_per_frame); + if (samples) { + sw->conv(sw->buf, buf, samples); - if (sw->hw->pcm_ops && !sw->hw->pcm_ops->volume_out) { - mixeng_volume (sw->buf, swlim, &sw->vol); + if (!sw->hw->pcm_ops->volume_out) { + mixeng_volume(sw->buf, samples, &sw->vol); } } - while (swlim) { + while (samples) { dead = hwsamples - live; left = hwsamples - wpos; blck = MIN (dead, left); if (!blck) { break; } - isamp = swlim; + isamp = samples; osamp = blck; st_rate_flow_mix ( sw->rate, @@ -741,7 +747,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) &osamp ); ret += isamp; - swlim -= isamp; + samples -= isamp; pos += isamp; live += osamp; wpos = (wpos + osamp) % hwsamples; @@ -991,7 +997,7 @@ static size_t audio_get_avail (SWVoiceIn *sw) if (audio_bug(__func__, live > sw->hw->conv_buf->size)) { dolog("live=%zu sw->hw->conv_buf->size=%zu\n", live, sw->hw->conv_buf->size); - return 0; + abort(); } ldebug ( @@ -1003,6 +1009,11 @@ static size_t audio_get_avail (SWVoiceIn *sw) return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame; } +static size_t audio_sw_bytes_free(SWVoiceOut *sw, size_t free) +{ + return (((int64_t)free << 32) / sw->ratio) * sw->info.bytes_per_frame; +} + static size_t audio_get_free(SWVoiceOut *sw) { size_t live, dead; @@ -1016,19 +1027,17 @@ static size_t audio_get_free(SWVoiceOut *sw) if (audio_bug(__func__, live > sw->hw->mix_buf->size)) { dolog("live=%zu sw->hw->mix_buf->size=%zu\n", live, sw->hw->mix_buf->size); - return 0; + abort(); } dead = sw->hw->mix_buf->size - live; #ifdef DEBUG_OUT - dolog ("%s: get_free live %zu dead %zu ret %" PRId64 "\n", - SW_NAME (sw), - live, dead, (((int64_t) dead << 32) / sw->ratio) * - sw->info.bytes_per_frame); + dolog("%s: get_free live %zu dead %zu sw_bytes %zu\n", + SW_NAME(sw), live, dead, audio_sw_bytes_free(sw, dead)); #endif - return (((int64_t) dead << 32) / sw->ratio) * sw->info.bytes_per_frame; + return dead; } static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos, @@ -1132,9 +1141,27 @@ static void audio_run_out (AudioState *s) } while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { - size_t played, live, prev_rpos, free; + size_t played, live, prev_rpos; + size_t hw_free = audio_pcm_hw_get_free(hw); int nb_live; + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active) { + size_t sw_free = audio_get_free(sw); + size_t free; + + if (hw_free > sw->total_hw_samples_mixed) { + free = audio_sw_bytes_free(sw, + MIN(sw_free, hw_free - sw->total_hw_samples_mixed)); + } else { + free = 0; + } + if (free > 0) { + sw->callback.fn(sw->callback.opaque, free); + } + } + } + live = audio_pcm_hw_get_live_out (hw, &nb_live); if (!nb_live) { live = 0; @@ -1142,7 +1169,7 @@ static void audio_run_out (AudioState *s) if (audio_bug(__func__, live > hw->mix_buf->size)) { dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size); - continue; + abort(); } if (hw->pending_disable && !nb_live) { @@ -1163,14 +1190,6 @@ static void audio_run_out (AudioState *s) } if (!live) { - for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { - if (sw->active) { - free = audio_get_free (sw); - if (free > 0) { - sw->callback.fn (sw->callback.opaque, free); - } - } - } if (hw->pcm_ops->run_buffer_out) { hw->pcm_ops->run_buffer_out(hw); } @@ -1183,7 +1202,7 @@ static void audio_run_out (AudioState *s) if (audio_bug(__func__, hw->mix_buf->pos >= hw->mix_buf->size)) { dolog("hw->mix_buf->pos=%zu hw->mix_buf->size=%zu played=%zu\n", hw->mix_buf->pos, hw->mix_buf->size, played); - hw->mix_buf->pos = 0; + abort(); } #ifdef DEBUG_OUT @@ -1203,7 +1222,7 @@ static void audio_run_out (AudioState *s) if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) { dolog("played=%zu sw->total_hw_samples_mixed=%zu\n", played, sw->total_hw_samples_mixed); - played = sw->total_hw_samples_mixed; + abort(); } sw->total_hw_samples_mixed -= played; @@ -1211,13 +1230,6 @@ static void audio_run_out (AudioState *s) if (!sw->total_hw_samples_mixed) { sw->empty = 1; } - - if (sw->active) { - free = audio_get_free (sw); - if (free > 0) { - sw->callback.fn (sw->callback.opaque, free); - } - } } } } @@ -1225,7 +1237,6 @@ static void audio_run_out (AudioState *s) static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples) { size_t conv = 0; - STSampleBuffer *conv_buf = hw->conv_buf; if (hw->pcm_ops->run_buffer_in) { hw->pcm_ops->run_buffer_in(hw); @@ -1241,11 +1252,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples) break; } - proc = MIN(size / hw->info.bytes_per_frame, - conv_buf->size - conv_buf->pos); - - hw->conv(conv_buf->samples + conv_buf->pos, buf, proc); - conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size; + proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame); samples -= proc; conv += proc; @@ -1338,7 +1345,7 @@ static void audio_run_capture (AudioState *s) if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) { dolog("captured=%zu sw->total_hw_samples_mixed=%zu\n", captured, sw->total_hw_samples_mixed); - captured = sw->total_hw_samples_mixed; + abort(); } sw->total_hw_samples_mixed -= captured; @@ -1394,12 +1401,10 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw) void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) { - ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul; + size_t start; - if (start < 0) { - start += hw->size_emul; - } - assert(start >= 0 && start < hw->size_emul); + start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul); + assert(start < hw->size_emul); *size = MIN(*size, hw->pending_emul); *size = MIN(*size, hw->size_emul - start); @@ -1412,16 +1417,22 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size) hw->pending_emul -= size; } +size_t audio_generic_buffer_get_free(HWVoiceOut *hw) +{ + if (hw->buf_emul) { + return hw->size_emul - hw->pending_emul; + } else { + return hw->samples * hw->info.bytes_per_frame; + } +} + void audio_generic_run_buffer_out(HWVoiceOut *hw) { while (hw->pending_emul) { - size_t write_len, written; - ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; + size_t write_len, written, start; - if (start < 0) { - start += hw->size_emul; - } - assert(start >= 0 && start < hw->size_emul); + start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul); + assert(start < hw->size_emul); write_len = MIN(hw->pending_emul, hw->size_emul - start); @@ -1462,6 +1473,12 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size) { size_t total = 0; + if (hw->pcm_ops->buffer_get_free) { + size_t free = hw->pcm_ops->buffer_get_free(hw); + + size = MIN(size, free); + } + while (total < size) { size_t dst_size = size - total; size_t copy_size, proc; @@ -1716,7 +1733,7 @@ static AudioState *audio_init(Audiodev *dev, const char *name) audio_validate_opts(dev, &error_abort); } - s = g_malloc0(sizeof(AudioState)); + s = g_new0(AudioState, 1); s->dev = dev; QLIST_INIT (&s->hw_head_out); @@ -1821,6 +1838,7 @@ void AUD_remove_card (QEMUSoundCard *card) g_free (card->name); } +static struct audio_pcm_ops capture_pcm_ops; CaptureVoiceOut *AUD_add_capture( AudioState *s, @@ -1866,6 +1884,7 @@ CaptureVoiceOut *AUD_add_capture( hw = &cap->hw; hw->s = s; + hw->pcm_ops = &capture_pcm_ops; QLIST_INIT (&hw->sw_head); QLIST_INIT (&cap->cb_head); @@ -2089,7 +2108,7 @@ void audio_parse_option(const char *opt) audio_validate_opts(dev, &error_fatal); - e = g_malloc0(sizeof(AudiodevListEntry)); + e = g_new0(AudiodevListEntry, 1); e->dev = dev; QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next); } diff --git a/audio/audio.h b/audio/audio.h index c8bde536b5..3d5ecdecd5 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -32,7 +32,7 @@ typedef void (*audio_callback_fn) (void *opaque, int avail); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define AUDIO_HOST_ENDIANNESS 1 #else #define AUDIO_HOST_ENDIANNESS 0 @@ -91,8 +91,8 @@ typedef struct QEMUAudioTimeStamp { uint64_t old_ts; } QEMUAudioTimeStamp; -void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); -void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void AUD_vlog (const char *cap, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); +void AUD_log (const char *cap, const char *fmt, ...) G_GNUC_PRINTF(2, 3); void AUD_register_card (const char *name, QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card); diff --git a/audio/audio_int.h b/audio/audio_int.h index 428a091d05..2a6914d2aa 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -161,10 +161,14 @@ struct audio_pcm_ops { void (*fini_out)(HWVoiceOut *hw); size_t (*write) (HWVoiceOut *hw, void *buf, size_t size); void (*run_buffer_out)(HWVoiceOut *hw); + /* + * Get the free output buffer size. This is an upper limit. The size + * returned by function get_buffer_out may be smaller. + */ + size_t (*buffer_get_free)(HWVoiceOut *hw); /* * get a buffer that after later can be passed to put_buffer_out; optional * returns the buffer, and writes it's size to size (in bytes) - * this is unrelated to the above buffer_size_out function */ void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size); /* @@ -190,6 +194,7 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw); void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size); void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size); void audio_generic_run_buffer_out(HWVoiceOut *hw); +size_t audio_generic_buffer_get_free(HWVoiceOut *hw); void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size); size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size); size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size); @@ -266,6 +271,19 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len) return (dst >= src) ? (dst - src) : (len - src + dst); } +/** + * audio_ring_posb() - returns new position in ringbuffer in backward + * direction at given distance + * + * @pos: current position in ringbuffer + * @dist: distance in ringbuffer to walk in reverse direction + * @len: size of ringbuffer + */ +static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len) +{ + return pos >= dist ? pos - dist : len - dist + pos; +} + #define dolog(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__) #ifdef DEBUG diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c index 0fe827b057..595949f52c 100644 --- a/audio/audio_legacy.c +++ b/audio/audio_legacy.c @@ -328,8 +328,8 @@ static void handle_per_direction( static AudiodevListEntry *legacy_opt(const char *drvname) { - AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry)); - e->dev = g_malloc0(sizeof(Audiodev)); + AudiodevListEntry *e = g_new0(AudiodevListEntry, 1); + e->dev = g_new0(Audiodev, 1); e->dev->id = g_strdup(drvname); e->dev->driver = qapi_enum_parse( &AudiodevDriver_lookup, drvname, -1, &error_abort); @@ -508,7 +508,7 @@ static void lv_free(Visitor *v) static Visitor *legacy_visitor_new(void) { - LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor)); + LegacyPrintVisitor *lv = g_new0(LegacyPrintVisitor, 1); lv->visitor.start_struct = lv_start_struct; lv->visitor.end_struct = lv_end_struct; diff --git a/audio/audio_template.h b/audio/audio_template.h index d2d348638b..7192b19e73 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -59,12 +59,13 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, if (audio_bug(__func__, !voice_size && max_voices)) { dolog ("drv=`%s' voice_size=0 max_voices=%d\n", drv->name, max_voices); - glue (s->nb_hw_voices_, TYPE) = 0; + abort(); } if (audio_bug(__func__, voice_size && !max_voices)) { dolog ("drv=`%s' voice_size=%d max_voices=0\n", drv->name, voice_size); + abort(); } } @@ -81,6 +82,7 @@ static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) size_t samples = hw->samples; if (audio_bug(__func__, samples == 0)) { dolog("Attempted to allocate empty buffer\n"); + abort(); } HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples); @@ -252,12 +254,12 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, if (audio_bug(__func__, !drv)) { dolog ("No host audio driver\n"); - return NULL; + abort(); } if (audio_bug(__func__, !drv->pcm_ops)) { dolog ("Host audio driver without pcm_ops\n"); - return NULL; + abort(); } hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE)); @@ -275,12 +277,13 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, QLIST_INIT (&hw->cap_head); #endif if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) { - goto err0; + g_free(hw); + return NULL; } if (audio_bug(__func__, hw->samples <= 0)) { dolog("hw->samples=%zd\n", hw->samples); - goto err1; + abort(); } if (hw->info.is_float) { @@ -309,12 +312,6 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, audio_attach_capture (hw); #endif return hw; - - err1: - glue (hw->pcm_ops->fini_, TYPE) (hw); - err0: - g_free (hw); - return NULL; } AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) @@ -435,7 +432,7 @@ void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) if (sw) { if (audio_bug(__func__, !card)) { dolog ("card=%p\n", card); - return; + abort(); } glue (audio_close_, TYPE) (sw); @@ -457,7 +454,7 @@ SW *glue (AUD_open_, TYPE) ( if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { dolog ("card=%p name=%p callback_fn=%p as=%p\n", card, name, callback_fn, as); - goto fail; + abort(); } s = card->state; @@ -468,12 +465,12 @@ SW *glue (AUD_open_, TYPE) ( if (audio_bug(__func__, audio_validate_settings(as))) { audio_print_settings (as); - goto fail; + abort(); } if (audio_bug(__func__, !s->drv)) { dolog ("Can not open `%s' (no host audio driver)\n", name); - goto fail; + abort(); } if (sw && audio_pcm_info_eq (&sw->info, as)) { diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c index 5ea8157dfc..316f118f50 100644 --- a/audio/audio_win_int.c +++ b/audio/audio_win_int.c @@ -1,7 +1,6 @@ /* public domain */ #include "qemu/osdep.h" -#include "qemu-common.h" #define AUDIO_CAP "win-int" #include diff --git a/audio/coreaudio.c b/audio/coreaudio.m similarity index 95% rename from audio/coreaudio.c rename to audio/coreaudio.m index d8a21d3e50..4695291621 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.m @@ -44,10 +44,15 @@ typedef struct coreaudioVoiceOut { bool enabled; } coreaudioVoiceOut; +#if !defined(MAC_OS_VERSION_12_0) \ + || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) +#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster +#endif + static const AudioObjectPropertyAddress voice_addr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; static OSStatus coreaudio_get_voice(AudioDeviceID *id) @@ -69,7 +74,7 @@ static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, AudioObjectPropertyAddress addr = { kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectGetPropertyData(id, @@ -86,7 +91,7 @@ static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) AudioObjectPropertyAddress addr = { kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectGetPropertyData(id, @@ -103,7 +108,7 @@ static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) AudioObjectPropertyAddress addr = { kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectSetPropertyData(id, @@ -121,7 +126,7 @@ static OSStatus coreaudio_set_streamformat(AudioDeviceID id, AudioObjectPropertyAddress addr = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectSetPropertyData(id, @@ -138,7 +143,7 @@ static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) AudioObjectPropertyAddress addr = { kAudioDevicePropertyDeviceIsRunning, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectGetPropertyData(id, @@ -206,7 +211,7 @@ static void coreaudio_logstatus (OSStatus status) AUD_log (AUDIO_CAP, "Reason: %s\n", str); } -static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( +static void G_GNUC_PRINTF (2, 3) coreaudio_logerr ( OSStatus status, const char *fmt, ... @@ -221,7 +226,7 @@ static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( coreaudio_logstatus (status); } -static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( +static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 ( OSStatus status, const char *typ, const char *fmt, @@ -283,6 +288,7 @@ static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name) coreaudio_buf_unlock(core, "coreaudio_" #name); \ return ret; \ } +COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw)) COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), (hw, size)) COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, @@ -333,12 +339,10 @@ static OSStatus audioDeviceIOProc( len = frameCount * hw->info.bytes_per_frame; while (len) { - size_t write_len; - ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; - if (start < 0) { - start += hw->size_emul; - } - assert(start >= 0 && start < hw->size_emul); + size_t write_len, start; + + start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul); + assert(start < hw->size_emul); write_len = MIN(MIN(hw->pending_emul, len), hw->size_emul - start); @@ -541,7 +545,6 @@ static OSStatus handle_voice_change( const AudioObjectPropertyAddress *in_addresses, void *in_client_data) { - OSStatus status; coreaudioVoiceOut *core = in_client_data; qemu_mutex_lock_iothread(); @@ -550,13 +553,12 @@ static OSStatus handle_voice_change( fini_out_device(core); } - status = init_out_device(core); - if (!status) { + if (!init_out_device(core)) { update_device_playback_state(core); } qemu_mutex_unlock_iothread(); - return status; + return 0; } static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, @@ -604,6 +606,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, coreaudio_playback_logerr(status, "Could not remove voice property change listener\n"); } + + return -1; } return 0; @@ -654,6 +658,8 @@ static struct audio_pcm_ops coreaudio_pcm_ops = { .fini_out = coreaudio_fini_out, /* wrapper for audio_generic_write */ .write = coreaudio_write, + /* wrapper for audio_generic_buffer_get_free */ + .buffer_get_free = coreaudio_buffer_get_free, /* wrapper for audio_generic_get_buffer_out */ .get_buffer_out = coreaudio_get_buffer_out, /* wrapper for audio_generic_put_buffer_out */ diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c index f178b47dee..a3d656d3b0 100644 --- a/audio/dbusaudio.c +++ b/audio/dbusaudio.c @@ -122,7 +122,7 @@ static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) return size; } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define AUDIO_HOST_BE TRUE #else #define AUDIO_HOST_BE FALSE diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 3dd2c4d4a6..3fb67ec3ee 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -222,7 +222,7 @@ static void dsound_log_hresult (HRESULT hr) AUD_log (AUDIO_CAP, "Reason: %s\n", str); } -static void GCC_FMT_ATTR (2, 3) dsound_logerr ( +static void G_GNUC_PRINTF (2, 3) dsound_logerr ( HRESULT hr, const char *fmt, ... @@ -237,7 +237,7 @@ static void GCC_FMT_ATTR (2, 3) dsound_logerr ( dsound_log_hresult (hr); } -static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( +static void G_GNUC_PRINTF (3, 4) dsound_logerr2 ( HRESULT hr, const char *typ, const char *fmt, @@ -427,22 +427,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable) } } -static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) +static size_t dsound_buffer_get_free(HWVoiceOut *hw) { DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; HRESULT hr; - DWORD ppos, wpos, act_size; - size_t req_size; - int err; - void *ret; + DWORD ppos, wpos; hr = IDirectSoundBuffer_GetCurrentPosition( dsb, &ppos, ds->first_time ? &wpos : NULL); if (FAILED(hr)) { dsound_logerr(hr, "Could not get playback buffer position\n"); - *size = 0; - return NULL; + return 0; } if (ds->first_time) { @@ -450,13 +446,20 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) ds->first_time = false; } - req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul); - req_size = MIN(req_size, hw->size_emul - hw->pos_emul); + return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul); +} - if (req_size == 0) { - *size = 0; - return NULL; - } +static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) +{ + DSoundVoiceOut *ds = (DSoundVoiceOut *)hw; + LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; + DWORD act_size; + size_t req_size; + int err; + void *ret; + + req_size = MIN(*size, hw->size_emul - hw->pos_emul); + assert(req_size > 0); err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL, &act_size, NULL, false, ds->s); @@ -620,7 +623,7 @@ static void *dsound_audio_init(Audiodev *dev) { int err; HRESULT hr; - dsound *s = g_malloc0(sizeof(dsound)); + dsound *s = g_new0(dsound, 1); AudiodevDsoundOptions *dso; assert(dev->driver == AUDIODEV_DRIVER_DSOUND); @@ -699,6 +702,7 @@ static struct audio_pcm_ops dsound_pcm_ops = { .init_out = dsound_init_out, .fini_out = dsound_fini_out, .write = audio_generic_write, + .buffer_get_free = dsound_buffer_get_free, .get_buffer_out = dsound_get_buffer_out, .put_buffer_out = dsound_put_buffer_out, .enable_out = dsound_enable_out, diff --git a/audio/jackaudio.c b/audio/jackaudio.c index 317009e936..5bdf3d7a78 100644 --- a/audio/jackaudio.c +++ b/audio/jackaudio.c @@ -97,9 +97,9 @@ static void qjack_buffer_create(QJackBuffer *buffer, int channels, int frames) buffer->used = 0; buffer->rptr = 0; buffer->wptr = 0; - buffer->data = g_malloc(channels * sizeof(float *)); + buffer->data = g_new(float *, channels); for (int i = 0; i < channels; ++i) { - buffer->data[i] = g_malloc(frames * sizeof(float)); + buffer->data[i] = g_new(float, frames); } } @@ -453,7 +453,7 @@ static int qjack_client_init(QJackClient *c) jack_on_shutdown(c->client, qjack_shutdown, c); /* allocate and register the ports */ - c->port = g_malloc(sizeof(jack_port_t *) * c->nchannels); + c->port = g_new(jack_port_t *, c->nchannels); for (int i = 0; i < c->nchannels; ++i) { char port_name[16]; @@ -483,8 +483,8 @@ static int qjack_client_init(QJackClient *c) c->buffersize = 512; } - /* create a 2 period buffer */ - qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2); + /* create a 3 period buffer */ + qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 3); qjack_client_connect_ports(c); c->state = QJACK_STATE_RUNNING; @@ -652,6 +652,7 @@ static struct audio_pcm_ops jack_pcm_ops = { .init_out = qjack_init_out, .fini_out = qjack_fini_out, .write = qjack_write, + .buffer_get_free = audio_generic_buffer_get_free, .run_buffer_out = audio_generic_run_buffer_out, .enable_out = qjack_enable_out, diff --git a/audio/meson.build b/audio/meson.build index 0ac3791d0b..94dab16891 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -7,7 +7,7 @@ softmmu_ss.add(files( 'wavcapture.c', )) -softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.c')) +softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.m')) softmmu_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) audio_modules = {} @@ -28,7 +28,7 @@ endforeach if dbus_display module_ss = ss.source_set() - module_ss.add(when: gio, if_true: files('dbusaudio.c')) + module_ss.add(when: [gio, pixman, opengl, 'CONFIG_GIO'], if_true: files('dbusaudio.c')) audio_modules += {'dbus': module_ss} endif diff --git a/audio/mixeng.c b/audio/mixeng.c index f27deb165b..100a306d6f 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -342,13 +342,13 @@ f_sample *mixeng_clip_float[2] = { void audio_sample_to_uint64(const void *samples, int pos, uint64_t *left, uint64_t *right) { - const struct st_sample *sample = samples; - sample += pos; #ifdef FLOAT_MIXENG error_report( "Coreaudio and floating point samples are not supported by replay yet"); abort(); #else + const struct st_sample *sample = samples; + sample += pos; *left = sample->l; *right = sample->r; #endif @@ -357,13 +357,13 @@ void audio_sample_to_uint64(const void *samples, int pos, void audio_sample_from_uint64(void *samples, int pos, uint64_t left, uint64_t right) { - struct st_sample *sample = samples; - sample += pos; #ifdef FLOAT_MIXENG error_report( "Coreaudio and floating point samples are not supported by replay yet"); abort(); #else + struct st_sample *sample = samples; + sample += pos; sample->l = left; sample->r = right; #endif diff --git a/audio/noaudio.c b/audio/noaudio.c index aac87dbc93..84a6bfbb1c 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = { .init_out = no_init_out, .fini_out = no_fini_out, .write = no_write, + .buffer_get_free = audio_generic_buffer_get_free, .run_buffer_out = audio_generic_run_buffer_out, .enable_out = no_enable_out, diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 60eff66424..8e075edb70 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -63,7 +63,7 @@ struct oss_params { int fragsize; }; -static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...) +static void G_GNUC_PRINTF (2, 3) oss_logerr (int err, const char *fmt, ...) { va_list ap; @@ -74,7 +74,7 @@ static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...) AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); } -static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( +static void G_GNUC_PRINTF (3, 4) oss_logerr2 ( int err, const char *typ, const char *fmt, @@ -389,11 +389,23 @@ static void oss_run_buffer_out(HWVoiceOut *hw) } } +static size_t oss_buffer_get_free(HWVoiceOut *hw) +{ + OSSVoiceOut *oss = (OSSVoiceOut *)hw; + + if (oss->mmapped) { + return oss_get_available_bytes(oss); + } else { + return audio_generic_buffer_get_free(hw); + } +} + static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size) { - OSSVoiceOut *oss = (OSSVoiceOut *) hw; + OSSVoiceOut *oss = (OSSVoiceOut *)hw; + if (oss->mmapped) { - *size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul); + *size = hw->size_emul - hw->pos_emul; return hw->buf_emul + hw->pos_emul; } else { return audio_generic_get_buffer_out(hw, size); @@ -750,6 +762,7 @@ static struct audio_pcm_ops oss_pcm_ops = { .init_out = oss_init_out, .fini_out = oss_fini_out, .write = oss_write, + .buffer_get_free = oss_buffer_get_free, .run_buffer_out = oss_run_buffer_out, .get_buffer_out = oss_get_buffer_out, .put_buffer_out = oss_put_buffer_out, diff --git a/audio/paaudio.c b/audio/paaudio.c index 75401d5391..e91116f239 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -43,7 +43,7 @@ typedef struct { static void qpa_conn_fini(PAConnection *c); -static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) +static void G_GNUC_PRINTF (2, 3) qpa_logerr (int err, const char *fmt, ...) { va_list ap; @@ -201,13 +201,11 @@ unlock_and_fail: return 0; } -static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size) +static size_t qpa_buffer_get_free(HWVoiceOut *hw) { - PAVoiceOut *p = (PAVoiceOut *) hw; + PAVoiceOut *p = (PAVoiceOut *)hw; PAConnection *c = p->g->conn; - void *ret; size_t l; - int r; pa_threaded_mainloop_lock(c->mainloop); @@ -216,7 +214,6 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size) if (pa_stream_get_state(p->stream) != PA_STREAM_READY) { /* wait for stream to become ready */ l = 0; - ret = NULL; goto unlock; } @@ -224,16 +221,33 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size) CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail, "pa_stream_writable_size failed\n"); +unlock: + pa_threaded_mainloop_unlock(c->mainloop); + return l; + +unlock_and_fail: + pa_threaded_mainloop_unlock(c->mainloop); + return 0; +} + +static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size) +{ + PAVoiceOut *p = (PAVoiceOut *)hw; + PAConnection *c = p->g->conn; + void *ret; + int r; + + pa_threaded_mainloop_lock(c->mainloop); + + CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail, + "pa_threaded_mainloop_lock failed\n"); + *size = -1; r = pa_stream_begin_write(p->stream, &ret, size); CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_begin_write failed\n"); -unlock: pa_threaded_mainloop_unlock(c->mainloop); - if (*size > l) { - *size = l; - } return ret; unlock_and_fail: @@ -535,11 +549,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, } audio_pcm_init_info (&hw->info, &obt_as); - /* - * This is wrong. hw->samples counts in frames. hw->samples will be - * number of channels times larger than expected. - */ - hw->samples = audio_buffer_samples( + /* hw->samples counts in frames */ + hw->samples = audio_buffer_frames( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); return 0; @@ -587,11 +598,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } audio_pcm_init_info (&hw->info, &obt_as); - /* - * This is wrong. hw->samples counts in frames. hw->samples will be - * number of channels times larger than expected. - */ - hw->samples = audio_buffer_samples( + /* hw->samples counts in frames */ + hw->samples = audio_buffer_frames( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); return 0; @@ -744,7 +752,7 @@ static int qpa_validate_per_direction_opts(Audiodev *dev, { if (!pdo->has_latency) { pdo->has_latency = true; - pdo->latency = 15000; + pdo->latency = 46440; } return 1; } @@ -752,7 +760,7 @@ static int qpa_validate_per_direction_opts(Audiodev *dev, /* common */ static void *qpa_conn_init(const char *server) { - PAConnection *c = g_malloc0(sizeof(PAConnection)); + PAConnection *c = g_new0(PAConnection, 1); QTAILQ_INSERT_TAIL(&pa_conns, c, list); c->mainloop = pa_threaded_mainloop_new(); @@ -841,7 +849,7 @@ static void *qpa_audio_init(Audiodev *dev) return NULL; } - g = g_malloc0(sizeof(paaudio)); + g = g_new0(paaudio, 1); server = popts->has_server ? popts->server : NULL; g->dev = dev; @@ -901,6 +909,7 @@ static struct audio_pcm_ops qpa_pcm_ops = { .init_out = qpa_init_out, .fini_out = qpa_fini_out, .write = qpa_write, + .buffer_get_free = qpa_buffer_get_free, .get_buffer_out = qpa_get_buffer_out, .put_buffer_out = qpa_put_buffer_out, .volume_out = qpa_volume_out, diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index c68c62a3e4..68a237b76b 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -55,7 +55,7 @@ typedef struct SDLVoiceIn { SDL_AudioDeviceID devid; } SDLVoiceIn; -static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) +static void G_GNUC_PRINTF (1, 2) sdl_logerr (const char *fmt, ...) { va_list ap; @@ -224,12 +224,11 @@ static void sdl_callback_out(void *opaque, Uint8 *buf, int len) /* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */ while (hw->pending_emul && len) { - size_t write_len; - ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul; - if (start < 0) { - start += hw->size_emul; - } - assert(start >= 0 && start < hw->size_emul); + size_t write_len, start; + + start = audio_ring_posb(hw->pos_emul, hw->pending_emul, + hw->size_emul); + assert(start < hw->size_emul); write_len = MIN(MIN(hw->pending_emul, len), hw->size_emul - start); @@ -310,6 +309,7 @@ static void sdl_callback_in(void *opaque, Uint8 *buf, int len) SDL_UnlockAudioDevice(sdl->devid); \ } +SDL_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw), Out) SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), (hw, size), Out) SDL_WRAPPER_FUNC(put_buffer_out, size_t, @@ -347,11 +347,8 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, req.freq = as->freq; req.format = aud_to_sdlfmt (as->fmt); req.channels = as->nchannels; - /* - * This is wrong. SDL samples are QEMU frames. The buffer size will be - * the requested buffer size multiplied by the number of channels. - */ - req.samples = audio_buffer_samples( + /* SDL samples are QEMU frames */ + req.samples = audio_buffer_frames( qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); req.callback = sdl_callback_out; req.userdata = sdl; @@ -472,6 +469,8 @@ static struct audio_pcm_ops sdl_pcm_ops = { .fini_out = sdl_fini_out, /* wrapper for audio_generic_write */ .write = sdl_write, + /* wrapper for audio_generic_buffer_get_free */ + .buffer_get_free = sdl_buffer_get_free, /* wrapper for audio_generic_get_buffer_out */ .get_buffer_out = sdl_get_buffer_out, /* wrapper for audio_generic_put_buffer_out */ diff --git a/audio/wavaudio.c b/audio/wavaudio.c index 20e6853f85..ac666335c7 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -197,6 +197,7 @@ static struct audio_pcm_ops wav_pcm_ops = { .init_out = wav_init_out, .fini_out = wav_fini_out, .write = wav_write_out, + .buffer_get_free = audio_generic_buffer_get_free, .run_buffer_out = audio_generic_run_buffer_out, .enable_out = wav_enable_out, }; diff --git a/backends/cryptodev.c b/backends/cryptodev.c index bf52476166..2b105e433c 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -39,7 +39,7 @@ cryptodev_backend_new_client(const char *model, { CryptoDevBackendClient *cc; - cc = g_malloc0(sizeof(CryptoDevBackendClient)); + cc = g_new0(CryptoDevBackendClient, 1); cc->model = g_strdup(model); if (name) { cc->name = g_strdup(name); diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index b47f98b6a3..037292d267 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -12,7 +12,6 @@ #include #include "qemu/osdep.h" -#include "qemu-common.h" #include "qom/object_interfaces.h" #include "qapi/error.h" #include "sysemu/hostmem.h" diff --git a/backends/hostmem.c b/backends/hostmem.c index b2a5e905e8..a7bae3d713 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -319,7 +319,7 @@ size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) #else size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) { - return qemu_real_host_page_size; + return qemu_real_host_page_size(); } #endif diff --git a/backends/meson.build b/backends/meson.build index 6e68945528..535c3ca7dd 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -15,7 +15,7 @@ softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c')) softmmu_ss.add(when: ['CONFIG_VHOST_USER', 'CONFIG_VIRTIO'], if_true: files('vhost-user.c')) softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c')) softmmu_ss.add(when: ['CONFIG_VIRTIO_CRYPTO', 'CONFIG_VHOST_CRYPTO'], if_true: files('cryptodev-vhost-user.c')) -softmmu_ss.add(when: 'CONFIG_GIO', if_true: [files('dbus-vmstate.c'), gio]) +softmmu_ss.add(when: gio, if_true: files('dbus-vmstate.c')) softmmu_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) subdir('tpm') diff --git a/backends/tpm/tpm_passthrough.c b/backends/tpm/tpm_passthrough.c index d5558fae6c..5a2f74db1b 100644 --- a/backends/tpm/tpm_passthrough.c +++ b/backends/tpm/tpm_passthrough.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/sockets.h" diff --git a/block.c b/block.c index b54d59d1fa..8cd16e757e 100644 --- a/block.c +++ b/block.c @@ -67,12 +67,15 @@ #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ +/* Protected by BQL */ static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); +/* Protected by BQL */ static QTAILQ_HEAD(, BlockDriverState) all_bdrv_states = QTAILQ_HEAD_INITIALIZER(all_bdrv_states); +/* Protected by BQL */ static QLIST_HEAD(, BlockDriver) bdrv_drivers = QLIST_HEAD_INITIALIZER(bdrv_drivers); @@ -132,8 +135,9 @@ size_t bdrv_opt_mem_align(BlockDriverState *bs) { if (!bs || !bs->drv) { /* page size or 4k (hdd sector size) should be on the safe side */ - return MAX(4096, qemu_real_host_page_size); + return MAX(4096, qemu_real_host_page_size()); } + IO_CODE(); return bs->bl.opt_mem_alignment; } @@ -142,8 +146,9 @@ size_t bdrv_min_mem_align(BlockDriverState *bs) { if (!bs || !bs->drv) { /* page size or 4k (hdd sector size) should be on the safe side */ - return MAX(4096, qemu_real_host_page_size); + return MAX(4096, qemu_real_host_page_size()); } + IO_CODE(); return bs->bl.min_mem_alignment; } @@ -269,12 +274,15 @@ void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, * image is inactivated. */ bool bdrv_is_read_only(BlockDriverState *bs) { + IO_CODE(); return !(bs->open_flags & BDRV_O_RDWR); } int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, bool ignore_allow_rdw, Error **errp) { + IO_CODE(); + /* Do not set read_only if copy_on_read is enabled */ if (bs->copy_on_read && read_only) { error_setg(errp, "Can't set node '%s' to r/o with copy-on-read enabled", @@ -308,6 +316,7 @@ int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, Error **errp) { int ret = 0; + IO_CODE(); if (!(bs->open_flags & BDRV_O_RDWR)) { return 0; @@ -384,12 +393,14 @@ static char *bdrv_make_absolute_filename(BlockDriverState *relative_to, char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp) { + GLOBAL_STATE_CODE(); return bdrv_make_absolute_filename(bs, bs->backing_file, errp); } void bdrv_register(BlockDriver *bdrv) { assert(bdrv->format_name); + GLOBAL_STATE_CODE(); QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list); } @@ -398,6 +409,8 @@ BlockDriverState *bdrv_new(void) BlockDriverState *bs; int i; + GLOBAL_STATE_CODE(); + bs = g_new0(BlockDriverState, 1); QLIST_INIT(&bs->dirty_bitmaps); for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { @@ -425,6 +438,7 @@ BlockDriverState *bdrv_new(void) static BlockDriver *bdrv_do_find_format(const char *format_name) { BlockDriver *drv1; + GLOBAL_STATE_CODE(); QLIST_FOREACH(drv1, &bdrv_drivers, list) { if (!strcmp(drv1->format_name, format_name)) { @@ -440,6 +454,8 @@ BlockDriver *bdrv_find_format(const char *format_name) BlockDriver *drv1; int i; + GLOBAL_STATE_CODE(); + drv1 = bdrv_do_find_format(format_name); if (drv1) { return drv1; @@ -489,6 +505,7 @@ static int bdrv_format_is_whitelisted(const char *format_name, bool read_only) int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) { + GLOBAL_STATE_CODE(); return bdrv_format_is_whitelisted(drv->format_name, read_only); } @@ -512,6 +529,7 @@ static void coroutine_fn bdrv_create_co_entry(void *opaque) CreateCo *cco = opaque; assert(cco->drv); + GLOBAL_STATE_CODE(); ret = cco->drv->bdrv_co_create_opts(cco->drv, cco->filename, cco->opts, &local_err); @@ -524,6 +542,8 @@ int bdrv_create(BlockDriver *drv, const char* filename, { int ret; + GLOBAL_STATE_CODE(); + Coroutine *co; CreateCo cco = { .drv = drv, @@ -578,6 +598,8 @@ static int64_t create_file_fallback_truncate(BlockBackend *blk, int64_t size; int ret; + GLOBAL_STATE_CODE(); + ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, 0, &local_err); if (ret < 0 && ret != -ENOTSUP) { @@ -616,6 +638,8 @@ static int create_file_fallback_zero_first_sector(BlockBackend *blk, int64_t bytes_to_clear; int ret; + GLOBAL_STATE_CODE(); + bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE); if (bytes_to_clear) { ret = blk_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP); @@ -647,6 +671,8 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, Error *local_err = NULL; int ret; + GLOBAL_STATE_CODE(); + size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, @@ -699,6 +725,8 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) QDict *qdict; int ret; + GLOBAL_STATE_CODE(); + drv = bdrv_find_protocol(filename, true, errp); if (drv == NULL) { return -ENOENT; @@ -743,6 +771,7 @@ int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) Error *local_err = NULL; int ret; + IO_CODE(); assert(bs != NULL); if (!bs->drv) { @@ -768,6 +797,7 @@ void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs) { Error *local_err = NULL; int ret; + IO_CODE(); if (!bs) { return; @@ -796,6 +826,7 @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) { BlockDriver *drv = bs->drv; BlockDriverState *filtered = bdrv_filter_bs(bs); + GLOBAL_STATE_CODE(); if (drv && drv->bdrv_probe_blocksizes) { return drv->bdrv_probe_blocksizes(bs, bsz); @@ -816,6 +847,7 @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { BlockDriver *drv = bs->drv; BlockDriverState *filtered = bdrv_filter_bs(bs); + GLOBAL_STATE_CODE(); if (drv && drv->bdrv_probe_geometry) { return drv->bdrv_probe_geometry(bs, geo); @@ -870,6 +902,7 @@ static BlockDriver *find_hdev_driver(const char *filename) { int score_max = 0, score; BlockDriver *drv = NULL, *d; + GLOBAL_STATE_CODE(); QLIST_FOREACH(d, &bdrv_drivers, list) { if (d->bdrv_probe_device) { @@ -887,6 +920,7 @@ static BlockDriver *find_hdev_driver(const char *filename) static BlockDriver *bdrv_do_find_protocol(const char *protocol) { BlockDriver *drv1; + GLOBAL_STATE_CODE(); QLIST_FOREACH(drv1, &bdrv_drivers, list) { if (drv1->protocol_name && !strcmp(drv1->protocol_name, protocol)) { @@ -907,6 +941,7 @@ BlockDriver *bdrv_find_protocol(const char *filename, const char *p; int i; + GLOBAL_STATE_CODE(); /* TODO Drivers without bdrv_file_open must be specified explicitly */ /* @@ -972,6 +1007,7 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, { int score_max = 0, score; BlockDriver *drv = NULL, *d; + IO_CODE(); QLIST_FOREACH(d, &bdrv_drivers, list) { if (d->bdrv_probe) { @@ -993,6 +1029,8 @@ static int find_image_format(BlockBackend *file, const char *filename, uint8_t buf[BLOCK_PROBE_BUF_SIZE]; int ret = 0; + GLOBAL_STATE_CODE(); + /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ if (blk_is_sg(file) || !blk_is_inserted(file) || blk_getlength(file) == 0) { *pdrv = &bdrv_raw; @@ -1024,6 +1062,7 @@ static int find_image_format(BlockBackend *file, const char *filename, int refresh_total_sectors(BlockDriverState *bs, int64_t hint) { BlockDriver *drv = bs->drv; + IO_CODE(); if (!drv) { return -ENOMEDIUM; @@ -1058,6 +1097,7 @@ int refresh_total_sectors(BlockDriverState *bs, int64_t hint) static void bdrv_join_options(BlockDriverState *bs, QDict *options, QDict *old_options) { + GLOBAL_STATE_CODE(); if (bs->drv && bs->drv->bdrv_join_options) { bs->drv->bdrv_join_options(options, old_options); } else { @@ -1074,6 +1114,7 @@ static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts, BlockdevDetectZeroesOptions detect_zeroes = qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, value, BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err); + GLOBAL_STATE_CODE(); g_free(value); if (local_err) { error_propagate(errp, local_err); @@ -1189,6 +1230,7 @@ static void bdrv_child_cb_drained_end(BdrvChild *child, static int bdrv_child_cb_inactivate(BdrvChild *child) { BlockDriverState *bs = child->opaque; + GLOBAL_STATE_CODE(); assert(bs->open_flags & BDRV_O_INACTIVE); return 0; } @@ -1215,6 +1257,7 @@ static void bdrv_child_cb_set_aio_ctx(BdrvChild *child, AioContext *ctx, static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options) { + GLOBAL_STATE_CODE(); *child_flags = (parent_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY; /* For temporary files, unconditional cache=unsafe is fine */ @@ -1235,6 +1278,7 @@ static void bdrv_backing_attach(BdrvChild *c) BlockDriverState *parent = c->opaque; BlockDriverState *backing_hd = c->bs; + GLOBAL_STATE_CODE(); assert(!parent->backing_blocker); error_setg(&parent->backing_blocker, "node is used as backing hd of '%s'", @@ -1273,6 +1317,7 @@ static void bdrv_backing_detach(BdrvChild *c) { BlockDriverState *parent = c->opaque; + GLOBAL_STATE_CODE(); assert(parent->backing_blocker); bdrv_op_unblock_all(c->bs, parent->backing_blocker); error_free(parent->backing_blocker); @@ -1285,6 +1330,7 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, BlockDriverState *parent = c->opaque; bool read_only = bdrv_is_read_only(parent); int ret; + GLOBAL_STATE_CODE(); if (read_only) { ret = bdrv_reopen_set_read_only(parent, false, errp); @@ -1316,6 +1362,7 @@ static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format, int parent_flags, QDict *parent_options) { int flags = parent_flags; + GLOBAL_STATE_CODE(); /* * First, decide whether to set, clear, or leave BDRV_O_PROTOCOL. @@ -1391,6 +1438,7 @@ static void bdrv_child_cb_attach(BdrvChild *child) { BlockDriverState *bs = child->opaque; + assert_bdrv_graph_writable(bs); QLIST_INSERT_HEAD(&bs->children, child, next); if (child->role & BDRV_CHILD_COW) { @@ -1410,6 +1458,7 @@ static void bdrv_child_cb_detach(BdrvChild *child) bdrv_unapply_subtree_drain(child, bs); + assert_bdrv_graph_writable(bs); QLIST_REMOVE(child, next); } @@ -1425,6 +1474,7 @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base, AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c) { BlockDriverState *bs = c->opaque; + IO_CODE(); return bdrv_get_aio_context(bs); } @@ -1447,12 +1497,14 @@ const BdrvChildClass child_of_bds = { AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c) { + GLOBAL_STATE_CODE(); return c->klass->get_parent_aio_context(c); } static int bdrv_open_flags(BlockDriverState *bs, int flags) { int open_flags = flags; + GLOBAL_STATE_CODE(); /* * Clear flags that are internal to the block layer before opening the @@ -1465,6 +1517,8 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) static void update_flags_from_options(int *flags, QemuOpts *opts) { + GLOBAL_STATE_CODE(); + *flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY); if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { @@ -1486,6 +1540,7 @@ static void update_flags_from_options(int *flags, QemuOpts *opts) static void update_options_from_flags(QDict *options, int flags) { + GLOBAL_STATE_CODE(); if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) { qdict_put_bool(options, BDRV_OPT_CACHE_DIRECT, flags & BDRV_O_NOCACHE); } @@ -1507,6 +1562,7 @@ static void bdrv_assign_node_name(BlockDriverState *bs, Error **errp) { char *gen_node_name = NULL; + GLOBAL_STATE_CODE(); if (!node_name) { node_name = gen_node_name = id_generate(ID_BLOCK); @@ -1551,6 +1607,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, { Error *local_err = NULL; int i, ret; + GLOBAL_STATE_CODE(); bdrv_assign_node_name(bs, node_name, &local_err); if (local_err) { @@ -1631,6 +1688,8 @@ BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, BlockDriverState *bs; int ret; + GLOBAL_STATE_CODE(); + bs = bdrv_new(); bs->open_flags = flags; bs->options = options ?: qdict_new(); @@ -1656,6 +1715,7 @@ BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, int flags, Error **errp) { + GLOBAL_STATE_CODE(); return bdrv_new_open_driver_opts(drv, node_name, NULL, flags, errp); } @@ -1750,6 +1810,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, assert(bs->file == NULL); assert(options != NULL && bs->options != options); + GLOBAL_STATE_CODE(); opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); if (!qemu_opts_absorb_qdict(opts, options, errp)) { @@ -1875,6 +1936,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp) QObject *options_obj; QDict *options; int ret; + GLOBAL_STATE_CODE(); ret = strstart(filename, "json:", &filename); assert(ret); @@ -1902,6 +1964,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename, { QDict *json_options; Error *local_err = NULL; + GLOBAL_STATE_CODE(); /* Parse json: pseudo-protocol */ if (!*pfilename || !g_str_has_prefix(*pfilename, "json:")) { @@ -1936,6 +1999,8 @@ static int bdrv_fill_options(QDict **options, const char *filename, BlockDriver *drv = NULL; Error *local_err = NULL; + GLOBAL_STATE_CODE(); + /* * Caution: while qdict_get_try_str() is fine, getting non-string * types would require more care. When @options come from @@ -2057,11 +2122,13 @@ static bool bdrv_is_writable_after_reopen(BlockDriverState *bs, */ bool bdrv_is_writable(BlockDriverState *bs) { + IO_CODE(); return bdrv_is_writable_after_reopen(bs, NULL); } static char *bdrv_child_user_desc(BdrvChild *c) { + GLOBAL_STATE_CODE(); return c->klass->get_parent_desc(c); } @@ -2078,6 +2145,7 @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) assert(a->bs); assert(a->bs == b->bs); + GLOBAL_STATE_CODE(); if ((b->perm & a->shared_perm) == b->perm) { return true; @@ -2101,6 +2169,7 @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) { BdrvChild *a, *b; + GLOBAL_STATE_CODE(); /* * During the loop we'll look at each pair twice. That's correct because @@ -2129,6 +2198,7 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, uint64_t *nperm, uint64_t *nshared) { assert(bs->drv && bs->drv->bdrv_child_perm); + GLOBAL_STATE_CODE(); bs->drv->bdrv_child_perm(bs, c, role, reopen_queue, parent_perm, parent_shared, nperm, nshared); @@ -2155,6 +2225,8 @@ static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found, BdrvChild *child; g_autoptr(GHashTable) local_found = NULL; + GLOBAL_STATE_CODE(); + if (!found) { assert(!list); found = local_found = g_hash_table_new(NULL, NULL); @@ -2182,6 +2254,8 @@ static void bdrv_child_set_perm_abort(void *opaque) { BdrvChildSetPermState *s = opaque; + GLOBAL_STATE_CODE(); + s->child->perm = s->old_perm; s->child->shared_perm = s->old_shared_perm; } @@ -2195,6 +2269,7 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, Transaction *tran) { BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1); + GLOBAL_STATE_CODE(); *s = (BdrvChildSetPermState) { .child = c, @@ -2212,6 +2287,7 @@ static void bdrv_drv_set_perm_commit(void *opaque) { BlockDriverState *bs = opaque; uint64_t cumulative_perms, cumulative_shared_perms; + GLOBAL_STATE_CODE(); if (bs->drv->bdrv_set_perm) { bdrv_get_cumulative_perm(bs, &cumulative_perms, @@ -2223,6 +2299,7 @@ static void bdrv_drv_set_perm_commit(void *opaque) static void bdrv_drv_set_perm_abort(void *opaque) { BlockDriverState *bs = opaque; + GLOBAL_STATE_CODE(); if (bs->drv->bdrv_abort_perm_update) { bs->drv->bdrv_abort_perm_update(bs); @@ -2238,6 +2315,7 @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Transaction *tran, Error **errp) { + GLOBAL_STATE_CODE(); if (!bs->drv) { return 0; } @@ -2266,6 +2344,7 @@ typedef struct BdrvReplaceChildState { static void bdrv_replace_child_commit(void *opaque) { BdrvReplaceChildState *s = opaque; + GLOBAL_STATE_CODE(); if (s->free_empty_child && !s->child->bs) { bdrv_child_free(s->child); @@ -2278,6 +2357,7 @@ static void bdrv_replace_child_abort(void *opaque) BdrvReplaceChildState *s = opaque; BlockDriverState *new_bs = s->child->bs; + GLOBAL_STATE_CODE(); /* * old_bs reference is transparently moved from @s to s->child. * @@ -2374,6 +2454,7 @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, BdrvChild *c; int ret; uint64_t cumulative_perms, cumulative_shared_perms; + GLOBAL_STATE_CODE(); bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms); @@ -2442,6 +2523,7 @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, { int ret; BlockDriverState *bs; + GLOBAL_STATE_CODE(); for ( ; list; list = list->next) { bs = list->data; @@ -2466,6 +2548,8 @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, uint64_t cumulative_perms = 0; uint64_t cumulative_shared_perms = BLK_PERM_ALL; + GLOBAL_STATE_CODE(); + QLIST_FOREACH(c, &bs->parents, next_parent) { cumulative_perms |= c->perm; cumulative_shared_perms &= c->shared_perm; @@ -2509,6 +2593,7 @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) int ret; Transaction *tran = tran_new(); g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); + GLOBAL_STATE_CODE(); ret = bdrv_list_refresh_perms(list, NULL, tran, errp); tran_finalize(tran, ret); @@ -2523,6 +2608,8 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, Transaction *tran = tran_new(); int ret; + GLOBAL_STATE_CODE(); + bdrv_child_set_perm(c, perm, shared, tran); ret = bdrv_refresh_perms(c->bs, &local_err); @@ -2553,6 +2640,8 @@ int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp) uint64_t parent_perms, parent_shared; uint64_t perms, shared; + GLOBAL_STATE_CODE(); + bdrv_get_cumulative_perm(bs, &parent_perms, &parent_shared); bdrv_child_perm(bs, c->bs, c, c->role, NULL, parent_perms, parent_shared, &perms, &shared); @@ -2571,6 +2660,7 @@ static void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { + GLOBAL_STATE_CODE(); *nperm = perm & DEFAULT_PERM_PASSTHROUGH; *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; } @@ -2582,6 +2672,7 @@ static void bdrv_default_perms_for_cow(BlockDriverState *bs, BdrvChild *c, uint64_t *nperm, uint64_t *nshared) { assert(role & BDRV_CHILD_COW); + GLOBAL_STATE_CODE(); /* * We want consistent read from backing files if the parent needs it. @@ -2618,6 +2709,7 @@ static void bdrv_default_perms_for_storage(BlockDriverState *bs, BdrvChild *c, { int flags; + GLOBAL_STATE_CODE(); assert(role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA)); flags = bdrv_reopen_get_flags(reopen_queue, bs); @@ -2694,6 +2786,7 @@ void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { + GLOBAL_STATE_CODE(); if (role & BDRV_CHILD_FILTERED) { assert(!(role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | BDRV_CHILD_COW))); @@ -2752,6 +2845,7 @@ static void bdrv_replace_child_noperm(BdrvChild **childp, assert(!child->frozen); assert(old_bs != new_bs); + GLOBAL_STATE_CODE(); if (old_bs && new_bs) { assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); @@ -2776,6 +2870,7 @@ static void bdrv_replace_child_noperm(BdrvChild **childp, if (child->klass->detach) { child->klass->detach(child); } + assert_bdrv_graph_writable(old_bs); QLIST_REMOVE(child, next_parent); } @@ -2785,6 +2880,7 @@ static void bdrv_replace_child_noperm(BdrvChild **childp, } if (new_bs) { + assert_bdrv_graph_writable(new_bs); QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); /* @@ -2827,6 +2923,7 @@ static void bdrv_replace_child_noperm(BdrvChild **childp, static void bdrv_child_free(BdrvChild *child) { assert(!child->bs); + GLOBAL_STATE_CODE(); assert(!child->next.le_prev); /* not in children list */ g_free(child->name); @@ -2845,6 +2942,7 @@ static void bdrv_attach_child_common_abort(void *opaque) BdrvChild *child = *s->child; BlockDriverState *bs = child->bs; + GLOBAL_STATE_CODE(); /* * Pass free_empty_child=false, because we still need the child * for the AioContext operations on the parent below; those @@ -2907,6 +3005,7 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, assert(child); assert(*child == NULL); assert(child_class->get_parent_desc); + GLOBAL_STATE_CODE(); new_child = g_new(BdrvChild, 1); *new_child = (BdrvChild) { @@ -2987,6 +3086,7 @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, uint64_t perm, shared_perm; assert(parent_bs->drv); + GLOBAL_STATE_CODE(); if (bdrv_recurse_has_child(child_bs, parent_bs)) { error_setg(errp, "Making '%s' a %s child of '%s' would create a cycle", @@ -3012,6 +3112,7 @@ static void bdrv_detach_child(BdrvChild **childp) { BlockDriverState *old_bs = (*childp)->bs; + GLOBAL_STATE_CODE(); bdrv_replace_child_noperm(childp, NULL, true); if (old_bs) { @@ -3051,6 +3152,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, BdrvChild *child = NULL; Transaction *tran = tran_new(); + GLOBAL_STATE_CODE(); + ret = bdrv_attach_child_common(child_bs, child_name, child_class, child_role, perm, shared_perm, opaque, &child, tran, errp); @@ -3091,6 +3194,8 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, BdrvChild *child = NULL; Transaction *tran = tran_new(); + GLOBAL_STATE_CODE(); + ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class, child_role, &child, tran, errp); if (ret < 0) { @@ -3117,6 +3222,8 @@ void bdrv_root_unref_child(BdrvChild *child) { BlockDriverState *child_bs; + GLOBAL_STATE_CODE(); + child_bs = child->bs; bdrv_detach_child(&child); bdrv_unref(child_bs); @@ -3191,6 +3298,7 @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, /* Callers must ensure that child->frozen is false. */ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) { + GLOBAL_STATE_CODE(); if (child == NULL) { return; } @@ -3203,6 +3311,7 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) { BdrvChild *c; + GLOBAL_STATE_CODE(); QLIST_FOREACH(c, &bs->parents, next_parent) { if (c->klass->change_media) { c->klass->change_media(c, load); @@ -3253,6 +3362,8 @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, BdrvChild *child = is_backing ? parent_bs->backing : parent_bs->file; BdrvChildRole role; + GLOBAL_STATE_CODE(); + if (!parent_bs->drv) { /* * Node without drv is an object without a class :/. TODO: finally fix @@ -3332,6 +3443,7 @@ static int bdrv_set_backing_noperm(BlockDriverState *bs, BlockDriverState *backing_hd, Transaction *tran, Error **errp) { + GLOBAL_STATE_CODE(); return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); } @@ -3341,6 +3453,7 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, int ret; Transaction *tran = tran_new(); + GLOBAL_STATE_CODE(); bdrv_drained_begin(bs); ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp); @@ -3380,6 +3493,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, QDict *tmp_parent_options = NULL; Error *local_err = NULL; + GLOBAL_STATE_CODE(); + if (bs->backing != NULL) { goto free_exit; } @@ -3539,6 +3654,8 @@ BdrvChild *bdrv_open_child(const char *filename, { BlockDriverState *bs; + GLOBAL_STATE_CODE(); + bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, child_role, allow_none, errp); if (bs == NULL) { @@ -3561,6 +3678,8 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) const char *reference = NULL; Visitor *v = NULL; + GLOBAL_STATE_CODE(); + if (ref->type == QTYPE_QSTRING) { reference = ref->u.reference; } else { @@ -3603,6 +3722,8 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, BlockDriverState *bs_snapshot = NULL; int ret; + GLOBAL_STATE_CODE(); + /* if snapshot, we create a temporary backing file and open it instead of opening 'filename' directly */ @@ -3690,6 +3811,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, assert(!child_class || !flags); assert(!child_class == !parent); + GLOBAL_STATE_CODE(); if (reference) { bool options_non_empty = options ? qdict_size(options) : false; @@ -3958,6 +4080,8 @@ close_and_fail: BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) { + GLOBAL_STATE_CODE(); + return bdrv_open_inherit(filename, reference, options, flags, NULL, NULL, 0, errp); } @@ -4074,6 +4198,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, * important to avoid graph changes between the recursive queuing here and * bdrv_reopen_multiple(). */ assert(bs->quiesce_counter > 0); + GLOBAL_STATE_CODE(); if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); @@ -4212,12 +4337,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, bool keep_old_opts) { + GLOBAL_STATE_CODE(); + return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false, NULL, 0, keep_old_opts); } void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) { + GLOBAL_STATE_CODE(); if (bs_queue) { BlockReopenQueueEntry *bs_entry, *next; QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { @@ -4259,6 +4387,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) assert(qemu_get_current_aio_context() == qemu_get_aio_context()); assert(bs_queue != NULL); + GLOBAL_STATE_CODE(); QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ctx = bdrv_get_aio_context(bs_entry->state.bs); @@ -4365,6 +4494,8 @@ int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, BlockReopenQueue *queue; int ret; + GLOBAL_STATE_CODE(); + bdrv_subtree_drained_begin(bs); if (ctx != qemu_get_aio_context()) { aio_context_release(ctx); @@ -4386,6 +4517,8 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, { QDict *opts = qdict_new(); + GLOBAL_STATE_CODE(); + qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only); return bdrv_reopen(bs, opts, true, errp); @@ -4420,6 +4553,8 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, QObject *value; const char *str; + GLOBAL_STATE_CODE(); + value = qdict_get(reopen_state->options, child_name); if (value == NULL) { return 0; @@ -4518,6 +4653,7 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, assert(reopen_state != NULL); assert(reopen_state->bs->drv != NULL); + GLOBAL_STATE_CODE(); drv = reopen_state->bs->drv; /* This function and each driver's bdrv_reopen_prepare() remove @@ -4728,6 +4864,7 @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) bs = reopen_state->bs; drv = bs->drv; assert(drv != NULL); + GLOBAL_STATE_CODE(); /* If there are any driver level actions to take */ if (drv->bdrv_reopen_commit) { @@ -4769,6 +4906,7 @@ static void bdrv_reopen_abort(BDRVReopenState *reopen_state) assert(reopen_state != NULL); drv = reopen_state->bs->drv; assert(drv != NULL); + GLOBAL_STATE_CODE(); if (drv->bdrv_reopen_abort) { drv->bdrv_reopen_abort(reopen_state); @@ -4781,6 +4919,7 @@ static void bdrv_close(BlockDriverState *bs) BdrvAioNotifier *ban, *ban_next; BdrvChild *child, *next; + GLOBAL_STATE_CODE(); assert(!bs->refcnt); bdrv_drained_begin(bs); /* complete I/O */ @@ -4840,6 +4979,7 @@ static void bdrv_close(BlockDriverState *bs) void bdrv_close_all(void) { assert(job_next(NULL) == NULL); + GLOBAL_STATE_CODE(); /* Drop references from requests still in flight, such as canceled block * jobs whose AIO context has not been polled yet */ @@ -4958,7 +5098,7 @@ static void bdrv_remove_filter_or_cow_child_abort(void *opaque) static void bdrv_remove_filter_or_cow_child_commit(void *opaque) { BdrvRemoveFilterOrCowChild *s = opaque; - + GLOBAL_STATE_CODE(); bdrv_child_free(s->child); } @@ -5041,6 +5181,7 @@ static int bdrv_replace_node_noperm(BlockDriverState *from, BdrvChild *c, *next; assert(to != NULL); + GLOBAL_STATE_CODE(); QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { assert(c->bs == from); @@ -5091,6 +5232,7 @@ static int bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to_cow_parent = NULL; int ret; + GLOBAL_STATE_CODE(); assert(to != NULL); if (detach_subchain) { @@ -5154,11 +5296,15 @@ out: int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp) { + GLOBAL_STATE_CODE(); + return bdrv_replace_node_common(from, to, true, false, errp); } int bdrv_drop_filter(BlockDriverState *bs, Error **errp) { + GLOBAL_STATE_CODE(); + return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true, errp); } @@ -5181,6 +5327,8 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, int ret; Transaction *tran = tran_new(); + GLOBAL_STATE_CODE(); + assert(!bs_new->backing); ret = bdrv_attach_child_noperm(bs_new, bs_top, "backing", @@ -5214,6 +5362,8 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, g_autoptr(GSList) refresh_list = NULL; BlockDriverState *old_bs = child->bs; + GLOBAL_STATE_CODE(); + bdrv_ref(old_bs); bdrv_drained_begin(old_bs); bdrv_drained_begin(new_bs); @@ -5241,6 +5391,7 @@ static void bdrv_delete(BlockDriverState *bs) { assert(bdrv_op_blocker_is_empty(bs)); assert(!bs->refcnt); + GLOBAL_STATE_CODE(); /* remove from list, if necessary */ if (bs->node_name[0] != '\0') { @@ -5285,6 +5436,8 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, node_name = qdict_get_try_str(options, "node-name"); + GLOBAL_STATE_CODE(); + new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, errp); options = NULL; /* bdrv_new_open_driver() eats options */ @@ -5320,6 +5473,7 @@ fail: int coroutine_fn bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { + IO_CODE(); if (bs->drv == NULL) { return -ENOMEDIUM; } @@ -5345,6 +5499,8 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, BlockDriver *drv = bs->drv; int ret; + GLOBAL_STATE_CODE(); + if (!drv) { return -ENOMEDIUM; } @@ -5386,6 +5542,9 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, BlockDriverState *bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs) { + + GLOBAL_STATE_CODE(); + bs = bdrv_skip_filters(bs); active = bdrv_skip_filters(active); @@ -5403,6 +5562,8 @@ BlockDriverState *bdrv_find_overlay(BlockDriverState *active, /* Given a BDS, searches for the base layer. */ BlockDriverState *bdrv_find_base(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); + return bdrv_find_overlay(bs, NULL); } @@ -5417,6 +5578,8 @@ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, BlockDriverState *i; BdrvChild *child; + GLOBAL_STATE_CODE(); + for (i = bs; i != base; i = child_bs(child)) { child = bdrv_filter_or_cow_child(i); @@ -5443,6 +5606,8 @@ int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, BlockDriverState *i; BdrvChild *child; + GLOBAL_STATE_CODE(); + if (bdrv_is_backing_chain_frozen(bs, base, errp)) { return -EPERM; } @@ -5477,6 +5642,8 @@ void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base) BlockDriverState *i; BdrvChild *child; + GLOBAL_STATE_CODE(); + for (i = bs; i != base; i = child_bs(child)) { child = bdrv_filter_or_cow_child(i); if (child) { @@ -5526,6 +5693,8 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, g_autoptr(GSList) updated_children = NULL; GSList *p; + GLOBAL_STATE_CODE(); + bdrv_ref(top); bdrv_subtree_drained_begin(top); @@ -5637,6 +5806,8 @@ static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs) int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) { BlockDriver *drv = bs->drv; + IO_CODE(); + if (!drv) { return -ENOMEDIUM; } @@ -5686,6 +5857,7 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, BlockDriverState *in_bs, Error **errp) { + IO_CODE(); if (!drv->bdrv_measure) { error_setg(errp, "Block driver '%s' does not support size measurement", drv->format_name); @@ -5701,6 +5873,7 @@ BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, int64_t bdrv_nb_sectors(BlockDriverState *bs) { BlockDriver *drv = bs->drv; + IO_CODE(); if (!drv) return -ENOMEDIUM; @@ -5721,6 +5894,7 @@ int64_t bdrv_nb_sectors(BlockDriverState *bs) int64_t bdrv_getlength(BlockDriverState *bs) { int64_t ret = bdrv_nb_sectors(bs); + IO_CODE(); if (ret < 0) { return ret; @@ -5735,12 +5909,14 @@ int64_t bdrv_getlength(BlockDriverState *bs) void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) { int64_t nb_sectors = bdrv_nb_sectors(bs); + IO_CODE(); *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; } bool bdrv_is_sg(BlockDriverState *bs) { + IO_CODE(); return bs->sg; } @@ -5750,6 +5926,7 @@ bool bdrv_is_sg(BlockDriverState *bs) bool bdrv_supports_compressed_writes(BlockDriverState *bs) { BlockDriverState *filtered; + IO_CODE(); if (!bs->drv || !block_driver_can_compress(bs->drv)) { return false; @@ -5769,6 +5946,7 @@ bool bdrv_supports_compressed_writes(BlockDriverState *bs) const char *bdrv_get_format_name(BlockDriverState *bs) { + IO_CODE(); return bs->drv ? bs->drv->format_name : NULL; } @@ -5785,6 +5963,8 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name), int i; const char **formats = NULL; + GLOBAL_STATE_CODE(); + QLIST_FOREACH(drv, &bdrv_drivers, list) { if (drv->format_name) { bool found = false; @@ -5843,6 +6023,7 @@ BlockDriverState *bdrv_find_node(const char *node_name) BlockDriverState *bs; assert(node_name); + GLOBAL_STATE_CODE(); QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { if (!strcmp(node_name, bs->node_name)) { @@ -5859,6 +6040,8 @@ BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, BlockDeviceInfoList *list; BlockDriverState *bs; + GLOBAL_STATE_CODE(); + list = NULL; QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, flat, errp); @@ -5934,6 +6117,7 @@ static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, { BlockPermission qapi_perm; XDbgBlockGraphEdge *edge; + GLOBAL_STATE_CODE(); edge = g_new0(XDbgBlockGraphEdge, 1); @@ -5964,6 +6148,8 @@ XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) BdrvChild *child; XDbgBlockGraphConstructor *gr = xdbg_graph_new(); + GLOBAL_STATE_CODE(); + for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { char *allocated_name = NULL; const char *name = blk_name(blk); @@ -6007,6 +6193,8 @@ BlockDriverState *bdrv_lookup_bs(const char *device, BlockBackend *blk; BlockDriverState *bs; + GLOBAL_STATE_CODE(); + if (device) { blk = blk_by_name(device); @@ -6038,6 +6226,9 @@ BlockDriverState *bdrv_lookup_bs(const char *device, * return false. If either argument is NULL, return false. */ bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) { + + GLOBAL_STATE_CODE(); + while (top && top != base) { top = bdrv_filter_or_cow_bs(top); } @@ -6047,6 +6238,7 @@ bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) BlockDriverState *bdrv_next_node(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); if (!bs) { return QTAILQ_FIRST(&graph_bdrv_states); } @@ -6055,6 +6247,7 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs) BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); if (!bs) { return QTAILQ_FIRST(&all_bdrv_states); } @@ -6063,6 +6256,7 @@ BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) const char *bdrv_get_node_name(const BlockDriverState *bs) { + IO_CODE(); return bs->node_name; } @@ -6070,6 +6264,7 @@ const char *bdrv_get_parent_name(const BlockDriverState *bs) { BdrvChild *c; const char *name; + IO_CODE(); /* If multiple parents have a name, just pick the first one. */ QLIST_FOREACH(c, &bs->parents, next_parent) { @@ -6087,6 +6282,7 @@ const char *bdrv_get_parent_name(const BlockDriverState *bs) /* TODO check what callers really want: bs->node_name or blk_name() */ const char *bdrv_get_device_name(const BlockDriverState *bs) { + IO_CODE(); return bdrv_get_parent_name(bs) ?: ""; } @@ -6096,22 +6292,26 @@ const char *bdrv_get_device_name(const BlockDriverState *bs) * absent, then this returns an empty (non-null) string. */ const char *bdrv_get_device_or_node_name(const BlockDriverState *bs) { + IO_CODE(); return bdrv_get_parent_name(bs) ?: bs->node_name; } int bdrv_get_flags(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); return bs->open_flags; } int bdrv_has_zero_init_1(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); return 1; } int bdrv_has_zero_init(BlockDriverState *bs) { BlockDriverState *filtered; + GLOBAL_STATE_CODE(); if (!bs->drv) { return 0; @@ -6137,6 +6337,7 @@ int bdrv_has_zero_init(BlockDriverState *bs) bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) { + IO_CODE(); if (!(bs->open_flags & BDRV_O_UNMAP)) { return false; } @@ -6147,6 +6348,7 @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size) { + IO_CODE(); pstrcpy(filename, filename_size, bs->backing_file); } @@ -6154,6 +6356,7 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { int ret; BlockDriver *drv = bs->drv; + IO_CODE(); /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ if (!drv) { return -ENOMEDIUM; @@ -6182,6 +6385,7 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; + IO_CODE(); if (drv && drv->bdrv_get_specific_info) { return drv->bdrv_get_specific_info(bs, errp); } @@ -6191,6 +6395,7 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) { BlockDriver *drv = bs->drv; + IO_CODE(); if (!drv || !drv->bdrv_get_specific_stats) { return NULL; } @@ -6199,6 +6404,7 @@ BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) { + IO_CODE(); if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { return; } @@ -6208,6 +6414,7 @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { bs = bdrv_primary_bs(bs); } @@ -6223,6 +6430,7 @@ static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs) int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, const char *tag) { + GLOBAL_STATE_CODE(); bs = bdrv_find_debug_node(bs); if (bs) { return bs->drv->bdrv_debug_breakpoint(bs, event, tag); @@ -6233,6 +6441,7 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) { + GLOBAL_STATE_CODE(); bs = bdrv_find_debug_node(bs); if (bs) { return bs->drv->bdrv_debug_remove_breakpoint(bs, tag); @@ -6243,6 +6452,7 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) int bdrv_debug_resume(BlockDriverState *bs, const char *tag) { + GLOBAL_STATE_CODE(); while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { bs = bdrv_primary_bs(bs); } @@ -6256,6 +6466,7 @@ int bdrv_debug_resume(BlockDriverState *bs, const char *tag) bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) { + GLOBAL_STATE_CODE(); while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) { bs = bdrv_primary_bs(bs); } @@ -6283,6 +6494,8 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, BlockDriverState *retval = NULL; BlockDriverState *bs_below; + GLOBAL_STATE_CODE(); + if (!bs || !bs->drv || !backing_file) { return NULL; } @@ -6393,19 +6606,21 @@ void bdrv_init_with_whitelist(void) bdrv_init(); } -int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) +int bdrv_activate(BlockDriverState *bs, Error **errp) { BdrvChild *child, *parent; Error *local_err = NULL; int ret; BdrvDirtyBitmap *bm; + GLOBAL_STATE_CODE(); + if (!bs->drv) { return -ENOMEDIUM; } QLIST_FOREACH(child, &bs->children, next) { - bdrv_co_invalidate_cache(child->bs, &local_err); + bdrv_activate(child->bs, &local_err); if (local_err) { error_propagate(errp, local_err); return -EINVAL; @@ -6418,7 +6633,7 @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) * Note that the required permissions of inactive images are always a * subset of the permissions required after activating the image. This * allows us to just get the permissions upfront without restricting - * drv->bdrv_invalidate_cache(). + * bdrv_co_invalidate_cache(). * * It also means that in error cases, we don't have to try and revert to * the old permissions (which is an operation that could fail, too). We can @@ -6433,13 +6648,10 @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) return ret; } - if (bs->drv->bdrv_co_invalidate_cache) { - bs->drv->bdrv_co_invalidate_cache(bs, &local_err); - if (local_err) { - bs->open_flags |= BDRV_O_INACTIVE; - error_propagate(errp, local_err); - return -EINVAL; - } + ret = bdrv_invalidate_cache(bs, errp); + if (ret < 0) { + bs->open_flags |= BDRV_O_INACTIVE; + return ret; } FOR_EACH_DIRTY_BITMAP(bs, bm) { @@ -6468,17 +6680,37 @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) return 0; } -void bdrv_invalidate_cache_all(Error **errp) +int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) +{ + Error *local_err = NULL; + IO_CODE(); + + assert(!(bs->open_flags & BDRV_O_INACTIVE)); + + if (bs->drv->bdrv_co_invalidate_cache) { + bs->drv->bdrv_co_invalidate_cache(bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + } + + return 0; +} + +void bdrv_activate_all(Error **errp) { BlockDriverState *bs; BdrvNextIterator it; + GLOBAL_STATE_CODE(); + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); int ret; aio_context_acquire(aio_context); - ret = bdrv_invalidate_cache(bs, errp); + ret = bdrv_activate(bs, errp); aio_context_release(aio_context); if (ret < 0) { bdrv_next_cleanup(&it); @@ -6490,6 +6722,7 @@ void bdrv_invalidate_cache_all(Error **errp) static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) { BdrvChild *parent; + GLOBAL_STATE_CODE(); QLIST_FOREACH(parent, &bs->parents, next_parent) { if (parent->klass->parent_is_bds) { @@ -6509,6 +6742,8 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) int ret; uint64_t cumulative_perms, cumulative_shared_perms; + GLOBAL_STATE_CODE(); + if (!bs->drv) { return -ENOMEDIUM; } @@ -6572,6 +6807,8 @@ int bdrv_inactivate_all(void) int ret = 0; GSList *aio_ctxs = NULL, *ctx; + GLOBAL_STATE_CODE(); + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); @@ -6615,6 +6852,7 @@ bool bdrv_is_inserted(BlockDriverState *bs) { BlockDriver *drv = bs->drv; BdrvChild *child; + IO_CODE(); if (!drv) { return false; @@ -6636,6 +6874,7 @@ bool bdrv_is_inserted(BlockDriverState *bs) void bdrv_eject(BlockDriverState *bs, bool eject_flag) { BlockDriver *drv = bs->drv; + IO_CODE(); if (drv && drv->bdrv_eject) { drv->bdrv_eject(bs, eject_flag); @@ -6649,7 +6888,7 @@ void bdrv_eject(BlockDriverState *bs, bool eject_flag) void bdrv_lock_medium(BlockDriverState *bs, bool locked) { BlockDriver *drv = bs->drv; - + IO_CODE(); trace_bdrv_lock_medium(bs, locked); if (drv && drv->bdrv_lock_medium) { @@ -6660,6 +6899,7 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked) /* Get a reference to bs */ void bdrv_ref(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); bs->refcnt++; } @@ -6668,6 +6908,7 @@ void bdrv_ref(BlockDriverState *bs) * deleted. */ void bdrv_unref(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); if (!bs) { return; } @@ -6685,6 +6926,7 @@ struct BdrvOpBlocker { bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) { BdrvOpBlocker *blocker; + GLOBAL_STATE_CODE(); assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); if (!QLIST_EMPTY(&bs->op_blockers[op])) { blocker = QLIST_FIRST(&bs->op_blockers[op]); @@ -6699,6 +6941,7 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason) { BdrvOpBlocker *blocker; + GLOBAL_STATE_CODE(); assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); blocker = g_new0(BdrvOpBlocker, 1); @@ -6709,6 +6952,7 @@ void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason) void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason) { BdrvOpBlocker *blocker, *next; + GLOBAL_STATE_CODE(); assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, next) { if (blocker->reason == reason) { @@ -6721,6 +6965,7 @@ void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason) void bdrv_op_block_all(BlockDriverState *bs, Error *reason) { int i; + GLOBAL_STATE_CODE(); for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { bdrv_op_block(bs, i, reason); } @@ -6729,6 +6974,7 @@ void bdrv_op_block_all(BlockDriverState *bs, Error *reason) void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason) { int i; + GLOBAL_STATE_CODE(); for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { bdrv_op_unblock(bs, i, reason); } @@ -6737,7 +6983,7 @@ void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason) bool bdrv_op_blocker_is_empty(BlockDriverState *bs) { int i; - + GLOBAL_STATE_CODE(); for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { if (!QLIST_EMPTY(&bs->op_blockers[i])) { return false; @@ -6759,6 +7005,8 @@ void bdrv_img_create(const char *filename, const char *fmt, Error *local_err = NULL; int ret = 0; + GLOBAL_STATE_CODE(); + /* Find driver and parse its options */ drv = bdrv_find_format(fmt); if (!drv) { @@ -6936,6 +7184,7 @@ out: AioContext *bdrv_get_aio_context(BlockDriverState *bs) { + IO_CODE(); return bs ? bs->aio_context : qemu_get_aio_context(); } @@ -6944,6 +7193,7 @@ AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs) Coroutine *self = qemu_coroutine_self(); AioContext *old_ctx = qemu_coroutine_get_aio_context(self); AioContext *new_ctx; + IO_CODE(); /* * Increase bs->in_flight to ensure that this operation is completed before @@ -6958,6 +7208,7 @@ AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs) void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) { + IO_CODE(); aio_co_reschedule_self(old_ctx); bdrv_dec_in_flight(bs); } @@ -6991,11 +7242,13 @@ void coroutine_fn bdrv_co_unlock(BlockDriverState *bs) void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co) { + IO_CODE(); aio_co_enter(bdrv_get_aio_context(bs), co); } static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) { + GLOBAL_STATE_CODE(); QLIST_REMOVE(ban, list); g_free(ban); } @@ -7005,6 +7258,7 @@ static void bdrv_detach_aio_context(BlockDriverState *bs) BdrvAioNotifier *baf, *baf_tmp; assert(!bs->walking_aio_notifiers); + GLOBAL_STATE_CODE(); bs->walking_aio_notifiers = true; QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) { if (baf->deleted) { @@ -7032,6 +7286,7 @@ static void bdrv_attach_aio_context(BlockDriverState *bs, AioContext *new_context) { BdrvAioNotifier *ban, *ban_tmp; + GLOBAL_STATE_CODE(); if (bs->quiesce_counter) { aio_disable_external(new_context); @@ -7078,6 +7333,7 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs, BdrvChild *child, *parent; g_assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + GLOBAL_STATE_CODE(); if (old_context == new_context) { return; @@ -7150,6 +7406,7 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs, static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx, GSList **ignore, Error **errp) { + GLOBAL_STATE_CODE(); if (g_slist_find(*ignore, c)) { return true; } @@ -7175,6 +7432,7 @@ static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx, bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, GSList **ignore, Error **errp) { + GLOBAL_STATE_CODE(); if (g_slist_find(*ignore, c)) { return true; } @@ -7193,6 +7451,8 @@ bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, return true; } + GLOBAL_STATE_CODE(); + QLIST_FOREACH(c, &bs->parents, next_parent) { if (!bdrv_parent_can_set_aio_context(c, ctx, ignore, errp)) { return false; @@ -7213,6 +7473,8 @@ int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, GSList *ignore; bool ret; + GLOBAL_STATE_CODE(); + ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL; ret = bdrv_can_set_aio_context(bs, ctx, &ignore, errp); g_slist_free(ignore); @@ -7231,6 +7493,7 @@ int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, Error **errp) { + GLOBAL_STATE_CODE(); return bdrv_child_try_set_aio_context(bs, ctx, NULL, errp); } @@ -7244,6 +7507,7 @@ void bdrv_add_aio_context_notifier(BlockDriverState *bs, .detach_aio_context = detach_aio_context, .opaque = opaque }; + GLOBAL_STATE_CODE(); QLIST_INSERT_HEAD(&bs->aio_notifiers, ban, list); } @@ -7255,6 +7519,7 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, void *opaque) { BdrvAioNotifier *ban, *ban_next; + GLOBAL_STATE_CODE(); QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { if (ban->attached_aio_context == attached_aio_context && @@ -7279,6 +7544,7 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, bool force, Error **errp) { + GLOBAL_STATE_CODE(); if (!bs->drv) { error_setg(errp, "Node is ejected"); return -ENOMEDIUM; @@ -7309,6 +7575,8 @@ bool bdrv_recurse_can_replace(BlockDriverState *bs, { BlockDriverState *filtered; + GLOBAL_STATE_CODE(); + if (!bs || !bs->drv) { return false; } @@ -7349,6 +7617,8 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, BlockDriverState *to_replace_bs = bdrv_find_node(node_name); AioContext *aio_context; + GLOBAL_STATE_CODE(); + if (!to_replace_bs) { error_setg(errp, "Failed to find node with node-name='%s'", node_name); return NULL; @@ -7478,6 +7748,7 @@ static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs) * would result in exactly bs->backing. */ static bool bdrv_backing_overridden(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); if (bs->backing) { return strcmp(bs->auto_backing_file, bs->backing->bs->filename); @@ -7510,6 +7781,8 @@ void bdrv_refresh_filename(BlockDriverState *bs) bool generate_json_filename; /* Whether our default implementation should fill exact_filename (false) or not (true) */ + GLOBAL_STATE_CODE(); + if (!drv) { return; } @@ -7632,6 +7905,8 @@ char *bdrv_dirname(BlockDriverState *bs, Error **errp) BlockDriver *drv = bs->drv; BlockDriverState *child_bs; + GLOBAL_STATE_CODE(); + if (!drv) { error_setg(errp, "Node '%s' is ejected", bs->node_name); return NULL; @@ -7663,7 +7938,7 @@ char *bdrv_dirname(BlockDriverState *bs, Error **errp) void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, Error **errp) { - + GLOBAL_STATE_CODE(); if (!parent_bs->drv || !parent_bs->drv->bdrv_add_child) { error_setg(errp, "The node %s does not support adding a child", bdrv_get_device_or_node_name(parent_bs)); @@ -7683,6 +7958,7 @@ void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) { BdrvChild *tmp; + GLOBAL_STATE_CODE(); if (!parent_bs->drv || !parent_bs->drv->bdrv_del_child) { error_setg(errp, "The node %s does not support removing a child", bdrv_get_device_or_node_name(parent_bs)); @@ -7710,6 +7986,7 @@ int bdrv_make_empty(BdrvChild *c, Error **errp) BlockDriver *drv = c->bs->drv; int ret; + GLOBAL_STATE_CODE(); assert(c->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)); if (!drv->bdrv_make_empty) { @@ -7734,6 +8011,8 @@ int bdrv_make_empty(BdrvChild *c, Error **errp) */ BdrvChild *bdrv_cow_child(BlockDriverState *bs) { + IO_CODE(); + if (!bs || !bs->drv) { return NULL; } @@ -7757,6 +8036,7 @@ BdrvChild *bdrv_cow_child(BlockDriverState *bs) BdrvChild *bdrv_filter_child(BlockDriverState *bs) { BdrvChild *c; + IO_CODE(); if (!bs || !bs->drv) { return NULL; @@ -7788,6 +8068,7 @@ BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs) { BdrvChild *cow_child = bdrv_cow_child(bs); BdrvChild *filter_child = bdrv_filter_child(bs); + IO_CODE(); /* Filter nodes cannot have COW backing files */ assert(!(cow_child && filter_child)); @@ -7808,6 +8089,7 @@ BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs) BdrvChild *bdrv_primary_child(BlockDriverState *bs) { BdrvChild *c, *found = NULL; + IO_CODE(); QLIST_FOREACH(c, &bs->children, next) { if (c->role & BDRV_CHILD_PRIMARY) { @@ -7860,6 +8142,7 @@ static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs, */ BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); return bdrv_do_skip_filters(bs, true); } @@ -7869,6 +8152,7 @@ BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs) */ BlockDriverState *bdrv_skip_filters(BlockDriverState *bs) { + IO_CODE(); return bdrv_do_skip_filters(bs, false); } @@ -7878,6 +8162,7 @@ BlockDriverState *bdrv_skip_filters(BlockDriverState *bs) */ BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs) { + IO_CODE(); return bdrv_skip_filters(bdrv_cow_bs(bdrv_skip_filters(bs))); } @@ -7913,8 +8198,8 @@ static bool bdrv_bsc_range_overlaps_locked(BlockDriverState *bs, */ bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum) { + IO_CODE(); RCU_READ_LOCK_GUARD(); - return bdrv_bsc_range_overlaps_locked(bs, offset, 1, pnum); } @@ -7924,6 +8209,7 @@ bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum) void bdrv_bsc_invalidate_range(BlockDriverState *bs, int64_t offset, int64_t bytes) { + IO_CODE(); RCU_READ_LOCK_GUARD(); if (bdrv_bsc_range_overlaps_locked(bs, offset, bytes, NULL)) { @@ -7938,6 +8224,7 @@ void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes) { BdrvBlockStatusCache *new_bsc = g_new(BdrvBlockStatusCache, 1); BdrvBlockStatusCache *old_bsc; + IO_CODE(); *new_bsc = (BdrvBlockStatusCache) { .valid = true, diff --git a/block/amend.c b/block/amend.c index 392df9ef83..f696a006e3 100644 --- a/block/amend.c +++ b/block/amend.c @@ -53,10 +53,31 @@ static int coroutine_fn blockdev_amend_run(Job *job, Error **errp) return ret; } +static int blockdev_amend_pre_run(BlockdevAmendJob *s, Error **errp) +{ + if (s->bs->drv->bdrv_amend_pre_run) { + return s->bs->drv->bdrv_amend_pre_run(s->bs, errp); + } + + return 0; +} + +static void blockdev_amend_free(Job *job) +{ + BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common); + + if (s->bs->drv->bdrv_amend_clean) { + s->bs->drv->bdrv_amend_clean(s->bs); + } + + bdrv_unref(s->bs); +} + static const JobDriver blockdev_amend_job_driver = { .instance_size = sizeof(BlockdevAmendJob), .job_type = JOB_TYPE_AMEND, .run = blockdev_amend_run, + .free = blockdev_amend_free, }; void qmp_x_blockdev_amend(const char *job_id, @@ -110,8 +131,15 @@ void qmp_x_blockdev_amend(const char *job_id, return; } + bdrv_ref(bs); s->bs = bs, s->opts = QAPI_CLONE(BlockdevAmendOptions, options), s->force = has_force ? force : false; + + if (blockdev_amend_pre_run(s, errp)) { + job_early_fail(&s->common); + return; + } + job_start(&s->common); } diff --git a/block/backup.c b/block/backup.c index 21d5983779..5cfd0b999c 100644 --- a/block/backup.c +++ b/block/backup.c @@ -372,6 +372,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, assert(bs); assert(target); + GLOBAL_STATE_CODE(); /* QMP interface protects us from these cases */ assert(sync_mode != MIRROR_SYNC_MODE_INCREMENTAL); diff --git a/block/blkverify.c b/block/blkverify.c index d1facf5ba9..e4a37af3b2 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -16,6 +16,7 @@ #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/memalign.h" typedef struct { BdrvChild *test_file; @@ -43,7 +44,7 @@ typedef struct BlkverifyRequest { QEMUIOVector *raw_qiov; /* cloned I/O vector for raw file */ } BlkverifyRequest; -static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyRequest *r, +static void G_GNUC_PRINTF(2, 3) blkverify_err(BlkverifyRequest *r, const char *fmt, ...) { va_list ap; diff --git a/block/block-backend.c b/block/block-backend.c index 4ff6b4d785..e0e1aff4b1 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -79,6 +79,7 @@ struct BlockBackend { bool allow_aio_context_change; bool allow_write_beyond_eof; + /* Protected by BQL */ NotifierList remove_bs_notifiers, insert_bs_notifiers; QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; @@ -111,12 +112,14 @@ static const AIOCBInfo block_backend_aiocb_info = { static void drive_info_del(DriveInfo *dinfo); static BlockBackend *bdrv_first_blk(BlockDriverState *bs); -/* All BlockBackends */ +/* All BlockBackends. Protected by BQL. */ static QTAILQ_HEAD(, BlockBackend) block_backends = QTAILQ_HEAD_INITIALIZER(block_backends); -/* All BlockBackends referenced by the monitor and which are iterated through by - * blk_next() */ +/* + * All BlockBackends referenced by the monitor and which are iterated through by + * blk_next(). Protected by BQL. + */ static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = QTAILQ_HEAD_INITIALIZER(monitor_block_backends); @@ -236,6 +239,7 @@ static void blk_root_activate(BdrvChild *child, Error **errp) void blk_set_force_allow_inactivate(BlockBackend *blk) { + GLOBAL_STATE_CODE(); blk->force_allow_inactivate = true; } @@ -354,6 +358,8 @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm) { BlockBackend *blk; + GLOBAL_STATE_CODE(); + blk = g_new0(BlockBackend, 1); blk->refcnt = 1; blk->ctx = ctx; @@ -391,6 +397,8 @@ BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, { BlockBackend *blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm); + GLOBAL_STATE_CODE(); + if (blk_insert_bs(blk, bs, errp) < 0) { blk_unref(blk); return NULL; @@ -419,6 +427,8 @@ BlockBackend *blk_new_open(const char *filename, const char *reference, uint64_t perm = 0; uint64_t shared = BLK_PERM_ALL; + GLOBAL_STATE_CODE(); + /* * blk_new_open() is mainly used in .bdrv_create implementations and the * tools where sharing isn't a major concern because the BDS stays private @@ -496,6 +506,7 @@ static void drive_info_del(DriveInfo *dinfo) int blk_get_refcnt(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk ? blk->refcnt : 0; } @@ -506,6 +517,7 @@ int blk_get_refcnt(BlockBackend *blk) void blk_ref(BlockBackend *blk) { assert(blk->refcnt > 0); + GLOBAL_STATE_CODE(); blk->refcnt++; } @@ -516,6 +528,7 @@ void blk_ref(BlockBackend *blk) */ void blk_unref(BlockBackend *blk) { + GLOBAL_STATE_CODE(); if (blk) { assert(blk->refcnt > 0); if (blk->refcnt > 1) { @@ -536,6 +549,7 @@ void blk_unref(BlockBackend *blk) */ BlockBackend *blk_all_next(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk ? QTAILQ_NEXT(blk, link) : QTAILQ_FIRST(&block_backends); } @@ -544,6 +558,8 @@ void blk_remove_all_bs(void) { BlockBackend *blk = NULL; + GLOBAL_STATE_CODE(); + while ((blk = blk_all_next(blk)) != NULL) { AioContext *ctx = blk_get_aio_context(blk); @@ -567,6 +583,7 @@ void blk_remove_all_bs(void) */ BlockBackend *blk_next(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk ? QTAILQ_NEXT(blk, monitor_link) : QTAILQ_FIRST(&monitor_block_backends); } @@ -633,6 +650,7 @@ static void bdrv_next_reset(BdrvNextIterator *it) BlockDriverState *bdrv_first(BdrvNextIterator *it) { + GLOBAL_STATE_CODE(); bdrv_next_reset(it); return bdrv_next(it); } @@ -670,6 +688,7 @@ bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp) { assert(!blk->name); assert(name && name[0]); + GLOBAL_STATE_CODE(); if (!id_wellformed(name)) { error_setg(errp, "Invalid device name"); @@ -697,6 +716,8 @@ bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp) */ void monitor_remove_blk(BlockBackend *blk) { + GLOBAL_STATE_CODE(); + if (!blk->name) { return; } @@ -712,6 +733,7 @@ void monitor_remove_blk(BlockBackend *blk) */ const char *blk_name(const BlockBackend *blk) { + IO_CODE(); return blk->name ?: ""; } @@ -723,6 +745,7 @@ BlockBackend *blk_by_name(const char *name) { BlockBackend *blk = NULL; + GLOBAL_STATE_CODE(); assert(name); while ((blk = blk_next(blk)) != NULL) { if (!strcmp(name, blk->name)) { @@ -737,12 +760,16 @@ BlockBackend *blk_by_name(const char *name) */ BlockDriverState *blk_bs(BlockBackend *blk) { + IO_CODE(); return blk->root ? blk->root->bs : NULL; } static BlockBackend *bdrv_first_blk(BlockDriverState *bs) { BdrvChild *child; + + GLOBAL_STATE_CODE(); + QLIST_FOREACH(child, &bs->parents, next_parent) { if (child->klass == &child_root) { return child->opaque; @@ -757,6 +784,7 @@ static BlockBackend *bdrv_first_blk(BlockDriverState *bs) */ bool bdrv_has_blk(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); return bdrv_first_blk(bs) != NULL; } @@ -767,6 +795,7 @@ bool bdrv_is_root_node(BlockDriverState *bs) { BdrvChild *c; + GLOBAL_STATE_CODE(); QLIST_FOREACH(c, &bs->parents, next_parent) { if (c->klass != &child_root) { return false; @@ -781,6 +810,7 @@ bool bdrv_is_root_node(BlockDriverState *bs) */ DriveInfo *blk_legacy_dinfo(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk->legacy_dinfo; } @@ -792,6 +822,7 @@ DriveInfo *blk_legacy_dinfo(BlockBackend *blk) DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo) { assert(!blk->legacy_dinfo); + GLOBAL_STATE_CODE(); return blk->legacy_dinfo = dinfo; } @@ -802,6 +833,7 @@ DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo) BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) { BlockBackend *blk = NULL; + GLOBAL_STATE_CODE(); while ((blk = blk_next(blk)) != NULL) { if (blk->legacy_dinfo == dinfo) { @@ -816,6 +848,7 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) */ BlockBackendPublic *blk_get_public(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return &blk->public; } @@ -824,6 +857,7 @@ BlockBackendPublic *blk_get_public(BlockBackend *blk) */ BlockBackend *blk_by_public(BlockBackendPublic *public) { + GLOBAL_STATE_CODE(); return container_of(public, BlockBackend, public); } @@ -835,6 +869,8 @@ void blk_remove_bs(BlockBackend *blk) ThrottleGroupMember *tgm = &blk->public.throttle_group_member; BdrvChild *root; + GLOBAL_STATE_CODE(); + notifier_list_notify(&blk->remove_bs_notifiers, blk); if (tgm->throttle_state) { BlockDriverState *bs = blk_bs(blk); @@ -869,6 +905,7 @@ void blk_remove_bs(BlockBackend *blk) int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) { ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + GLOBAL_STATE_CODE(); bdrv_ref(bs); blk->root = bdrv_root_attach_child(bs, "root", &child_root, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, @@ -892,6 +929,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) */ int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp) { + GLOBAL_STATE_CODE(); return bdrv_replace_child_bs(blk->root, new_bs, errp); } @@ -902,6 +940,7 @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, Error **errp) { int ret; + GLOBAL_STATE_CODE(); if (blk->root && !blk->disable_perm) { ret = bdrv_child_try_set_perm(blk->root, perm, shared_perm, errp); @@ -918,6 +957,7 @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) { + GLOBAL_STATE_CODE(); *perm = blk->perm; *shared_perm = blk->shared_perm; } @@ -928,6 +968,7 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) */ int blk_attach_dev(BlockBackend *blk, DeviceState *dev) { + GLOBAL_STATE_CODE(); if (blk->dev) { return -EBUSY; } @@ -953,6 +994,7 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev) void blk_detach_dev(BlockBackend *blk, DeviceState *dev) { assert(blk->dev == dev); + GLOBAL_STATE_CODE(); blk->dev = NULL; blk->dev_ops = NULL; blk->dev_opaque = NULL; @@ -966,6 +1008,7 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) */ DeviceState *blk_get_attached_dev(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk->dev; } @@ -974,6 +1017,7 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) char *blk_get_attached_dev_id(BlockBackend *blk) { DeviceState *dev = blk->dev; + IO_CODE(); if (!dev) { return g_strdup(""); @@ -994,6 +1038,8 @@ BlockBackend *blk_by_dev(void *dev) { BlockBackend *blk = NULL; + GLOBAL_STATE_CODE(); + assert(dev != NULL); while ((blk = blk_all_next(blk)) != NULL) { if (blk->dev == dev) { @@ -1011,6 +1057,7 @@ BlockBackend *blk_by_dev(void *dev) void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque) { + GLOBAL_STATE_CODE(); blk->dev_ops = ops; blk->dev_opaque = opaque; @@ -1032,6 +1079,7 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, */ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) { + GLOBAL_STATE_CODE(); if (blk->dev_ops && blk->dev_ops->change_media_cb) { bool tray_was_open, tray_is_open; Error *local_err = NULL; @@ -1064,6 +1112,7 @@ static void blk_root_change_media(BdrvChild *child, bool load) */ bool blk_dev_has_removable_media(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb); } @@ -1072,6 +1121,7 @@ bool blk_dev_has_removable_media(BlockBackend *blk) */ bool blk_dev_has_tray(BlockBackend *blk) { + IO_CODE(); return blk->dev_ops && blk->dev_ops->is_tray_open; } @@ -1081,6 +1131,7 @@ bool blk_dev_has_tray(BlockBackend *blk) */ void blk_dev_eject_request(BlockBackend *blk, bool force) { + GLOBAL_STATE_CODE(); if (blk->dev_ops && blk->dev_ops->eject_request_cb) { blk->dev_ops->eject_request_cb(blk->dev_opaque, force); } @@ -1091,6 +1142,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force) */ bool blk_dev_is_tray_open(BlockBackend *blk) { + IO_CODE(); if (blk_dev_has_tray(blk)) { return blk->dev_ops->is_tray_open(blk->dev_opaque); } @@ -1103,6 +1155,7 @@ bool blk_dev_is_tray_open(BlockBackend *blk) */ bool blk_dev_is_medium_locked(BlockBackend *blk) { + GLOBAL_STATE_CODE(); if (blk->dev_ops && blk->dev_ops->is_medium_locked) { return blk->dev_ops->is_medium_locked(blk->dev_opaque); } @@ -1123,6 +1176,7 @@ static void blk_root_resize(BdrvChild *child) void blk_iostatus_enable(BlockBackend *blk) { + GLOBAL_STATE_CODE(); blk->iostatus_enabled = true; blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } @@ -1131,6 +1185,7 @@ void blk_iostatus_enable(BlockBackend *blk) * enables it _and_ the VM is configured to stop on errors */ bool blk_iostatus_is_enabled(const BlockBackend *blk) { + IO_CODE(); return (blk->iostatus_enabled && (blk->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || blk->on_write_error == BLOCKDEV_ON_ERROR_STOP || @@ -1139,16 +1194,19 @@ bool blk_iostatus_is_enabled(const BlockBackend *blk) BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk->iostatus; } void blk_iostatus_disable(BlockBackend *blk) { + GLOBAL_STATE_CODE(); blk->iostatus_enabled = false; } void blk_iostatus_reset(BlockBackend *blk) { + GLOBAL_STATE_CODE(); if (blk_iostatus_is_enabled(blk)) { blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } @@ -1156,6 +1214,7 @@ void blk_iostatus_reset(BlockBackend *blk) void blk_iostatus_set_err(BlockBackend *blk, int error) { + IO_CODE(); assert(blk_iostatus_is_enabled(blk)); if (blk->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { blk->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : @@ -1165,16 +1224,19 @@ void blk_iostatus_set_err(BlockBackend *blk, int error) void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow) { + IO_CODE(); blk->allow_write_beyond_eof = allow; } void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow) { + IO_CODE(); blk->allow_aio_context_change = allow; } void blk_set_disable_request_queuing(BlockBackend *blk, bool disable) { + IO_CODE(); blk->disable_request_queuing = disable; } @@ -1228,6 +1290,7 @@ blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, { int ret; BlockDriverState *bs; + IO_CODE(); blk_wait_while_drained(blk); @@ -1258,6 +1321,7 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, BdrvRequestFlags flags) { int ret; + IO_OR_GS_CODE(); blk_inc_in_flight(blk); ret = blk_co_do_preadv(blk, offset, bytes, qiov, flags); @@ -1274,6 +1338,7 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, { int ret; BlockDriverState *bs; + IO_CODE(); blk_wait_while_drained(blk); @@ -1309,6 +1374,7 @@ int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, BdrvRequestFlags flags) { int ret; + IO_OR_GS_CODE(); blk_inc_in_flight(blk); ret = blk_co_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); @@ -1321,6 +1387,7 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { + IO_OR_GS_CODE(); return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags); } @@ -1349,22 +1416,26 @@ typedef struct BlkRwCo { int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequestFlags flags) { + IO_OR_GS_CODE(); return blk_pwritev_part(blk, offset, bytes, NULL, 0, flags | BDRV_REQ_ZERO_WRITE); } int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) { + GLOBAL_STATE_CODE(); return bdrv_make_zero(blk->root, flags); } void blk_inc_in_flight(BlockBackend *blk) { + IO_CODE(); qatomic_inc(&blk->in_flight); } void blk_dec_in_flight(BlockBackend *blk) { + IO_CODE(); qatomic_dec(&blk->in_flight); aio_wait_kick(); } @@ -1383,6 +1454,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, void *opaque, int ret) { struct BlockBackendAIOCB *acb; + IO_CODE(); blk_inc_in_flight(blk); acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); @@ -1490,6 +1562,7 @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { + IO_CODE(); return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_write_entry, flags | BDRV_REQ_ZERO_WRITE, cb, opaque); } @@ -1498,6 +1571,7 @@ int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes) { int ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); blk_inc_in_flight(blk); ret = blk_do_preadv(blk, offset, bytes, &qiov, 0); @@ -1511,6 +1585,7 @@ int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, { int ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); ret = blk_pwritev_part(blk, offset, bytes, &qiov, 0, flags); @@ -1519,6 +1594,7 @@ int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, int64_t blk_getlength(BlockBackend *blk) { + IO_CODE(); if (!blk_is_available(blk)) { return -ENOMEDIUM; } @@ -1528,6 +1604,7 @@ int64_t blk_getlength(BlockBackend *blk) void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) { + IO_CODE(); if (!blk_bs(blk)) { *nb_sectors_ptr = 0; } else { @@ -1537,6 +1614,7 @@ void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) int64_t blk_nb_sectors(BlockBackend *blk) { + IO_CODE(); if (!blk_is_available(blk)) { return -ENOMEDIUM; } @@ -1548,6 +1626,7 @@ BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { + IO_CODE(); assert((uint64_t)qiov->size <= INT64_MAX); return blk_aio_prwv(blk, offset, qiov->size, qiov, blk_aio_read_entry, flags, cb, opaque); @@ -1557,6 +1636,7 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { + IO_CODE(); assert((uint64_t)qiov->size <= INT64_MAX); return blk_aio_prwv(blk, offset, qiov->size, qiov, blk_aio_write_entry, flags, cb, opaque); @@ -1564,11 +1644,13 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, void blk_aio_cancel(BlockAIOCB *acb) { + GLOBAL_STATE_CODE(); bdrv_aio_cancel(acb); } void blk_aio_cancel_async(BlockAIOCB *acb) { + IO_CODE(); bdrv_aio_cancel_async(acb); } @@ -1576,6 +1658,8 @@ void blk_aio_cancel_async(BlockAIOCB *acb) int coroutine_fn blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { + IO_CODE(); + blk_wait_while_drained(blk); if (!blk_is_available(blk)) { @@ -1588,6 +1672,7 @@ blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { int ret; + IO_OR_GS_CODE(); blk_inc_in_flight(blk); ret = blk_do_ioctl(blk, req, buf); @@ -1609,6 +1694,7 @@ static void blk_aio_ioctl_entry(void *opaque) BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque) { + IO_CODE(); return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque); } @@ -1617,6 +1703,7 @@ int coroutine_fn blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; + IO_CODE(); blk_wait_while_drained(blk); @@ -1641,6 +1728,7 @@ BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, BlockCompletionFunc *cb, void *opaque) { + IO_CODE(); return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0, cb, opaque); } @@ -1649,6 +1737,7 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; + IO_OR_GS_CODE(); blk_inc_in_flight(blk); ret = blk_co_do_pdiscard(blk, offset, bytes); @@ -1660,6 +1749,7 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; + IO_OR_GS_CODE(); blk_inc_in_flight(blk); ret = blk_do_pdiscard(blk, offset, bytes); @@ -1672,6 +1762,7 @@ int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) int coroutine_fn blk_co_do_flush(BlockBackend *blk) { blk_wait_while_drained(blk); + IO_CODE(); if (!blk_is_available(blk)) { return -ENOMEDIUM; @@ -1692,12 +1783,14 @@ static void blk_aio_flush_entry(void *opaque) BlockAIOCB *blk_aio_flush(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque) { + IO_CODE(); return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque); } int coroutine_fn blk_co_flush(BlockBackend *blk) { int ret; + IO_OR_GS_CODE(); blk_inc_in_flight(blk); ret = blk_co_do_flush(blk); @@ -1720,6 +1813,7 @@ int blk_flush(BlockBackend *blk) void blk_drain(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (bs) { bdrv_ref(bs); @@ -1740,6 +1834,8 @@ void blk_drain_all(void) { BlockBackend *blk = NULL; + GLOBAL_STATE_CODE(); + bdrv_drain_all_begin(); while ((blk = blk_all_next(blk)) != NULL) { @@ -1759,12 +1855,14 @@ void blk_drain_all(void) void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, BlockdevOnError on_write_error) { + GLOBAL_STATE_CODE(); blk->on_read_error = on_read_error; blk->on_write_error = on_write_error; } BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) { + IO_CODE(); return is_read ? blk->on_read_error : blk->on_write_error; } @@ -1772,6 +1870,7 @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, int error) { BlockdevOnError on_err = blk_get_on_error(blk, is_read); + IO_CODE(); switch (on_err) { case BLOCKDEV_ON_ERROR_ENOSPC: @@ -1811,6 +1910,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, bool is_read, int error) { assert(error >= 0); + IO_CODE(); if (action == BLOCK_ERROR_ACTION_STOP) { /* First set the iostatus, so that "info block" returns an iostatus @@ -1842,6 +1942,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, bool blk_supports_write_perm(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (bs) { return !bdrv_is_read_only(bs); @@ -1856,12 +1957,14 @@ bool blk_supports_write_perm(BlockBackend *blk) */ bool blk_is_writable(BlockBackend *blk) { + IO_CODE(); return blk->perm & BLK_PERM_WRITE; } bool blk_is_sg(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (!bs) { return false; @@ -1872,41 +1975,47 @@ bool blk_is_sg(BlockBackend *blk) bool blk_enable_write_cache(BlockBackend *blk) { + IO_CODE(); return blk->enable_write_cache; } void blk_set_enable_write_cache(BlockBackend *blk, bool wce) { + GLOBAL_STATE_CODE(); blk->enable_write_cache = wce; } -void blk_invalidate_cache(BlockBackend *blk, Error **errp) +void blk_activate(BlockBackend *blk, Error **errp) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (!bs) { error_setg(errp, "Device '%s' has no medium", blk->name); return; } - bdrv_invalidate_cache(bs, errp); + bdrv_activate(bs, errp); } bool blk_is_inserted(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + IO_CODE(); return bs && bdrv_is_inserted(bs); } bool blk_is_available(BlockBackend *blk) { + IO_CODE(); return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk); } void blk_lock_medium(BlockBackend *blk, bool locked) { BlockDriverState *bs = blk_bs(blk); + IO_CODE(); if (bs) { bdrv_lock_medium(bs, locked); @@ -1917,6 +2026,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag) { BlockDriverState *bs = blk_bs(blk); char *id; + IO_CODE(); if (bs) { bdrv_eject(bs, eject_flag); @@ -1933,6 +2043,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag) int blk_get_flags(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (bs) { return bdrv_get_flags(bs); @@ -1945,6 +2056,7 @@ int blk_get_flags(BlockBackend *blk) uint32_t blk_get_request_alignment(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + IO_CODE(); return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE; } @@ -1953,6 +2065,7 @@ uint64_t blk_get_max_hw_transfer(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); uint64_t max = INT_MAX; + IO_CODE(); if (bs) { max = MIN_NON_ZERO(max, bs->bl.max_hw_transfer); @@ -1966,6 +2079,7 @@ uint32_t blk_get_max_transfer(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); uint32_t max = INT_MAX; + IO_CODE(); if (bs) { max = MIN_NON_ZERO(max, bs->bl.max_transfer); @@ -1975,33 +2089,39 @@ uint32_t blk_get_max_transfer(BlockBackend *blk) int blk_get_max_hw_iov(BlockBackend *blk) { + IO_CODE(); return MIN_NON_ZERO(blk->root->bs->bl.max_hw_iov, blk->root->bs->bl.max_iov); } int blk_get_max_iov(BlockBackend *blk) { + IO_CODE(); return blk->root->bs->bl.max_iov; } void blk_set_guest_block_size(BlockBackend *blk, int align) { + IO_CODE(); blk->guest_block_size = align; } void *blk_try_blockalign(BlockBackend *blk, size_t size) { + IO_CODE(); return qemu_try_blockalign(blk ? blk_bs(blk) : NULL, size); } void *blk_blockalign(BlockBackend *blk, size_t size) { + IO_CODE(); return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); } bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (!bs) { return false; @@ -2013,6 +2133,7 @@ bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (bs) { bdrv_op_unblock(bs, op, reason); @@ -2022,6 +2143,7 @@ void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) void blk_op_block_all(BlockBackend *blk, Error *reason) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (bs) { bdrv_op_block_all(bs, reason); @@ -2031,6 +2153,7 @@ void blk_op_block_all(BlockBackend *blk, Error *reason) void blk_op_unblock_all(BlockBackend *blk, Error *reason) { BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); if (bs) { bdrv_op_unblock_all(bs, reason); @@ -2040,6 +2163,7 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) AioContext *blk_get_aio_context(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + IO_CODE(); if (bs) { AioContext *ctx = bdrv_get_aio_context(blk_bs(blk)); @@ -2090,6 +2214,7 @@ static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context, int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, Error **errp) { + GLOBAL_STATE_CODE(); return blk_do_set_aio_context(blk, new_context, true, errp); } @@ -2126,6 +2251,7 @@ void blk_add_aio_context_notifier(BlockBackend *blk, { BlockBackendAioNotifier *notifier; BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); notifier = g_new(BlockBackendAioNotifier, 1); notifier->attached_aio_context = attached_aio_context; @@ -2148,6 +2274,8 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, BlockBackendAioNotifier *notifier; BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); + if (bs) { bdrv_remove_aio_context_notifier(bs, attached_aio_context, detach_aio_context, opaque); @@ -2168,17 +2296,20 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) { + GLOBAL_STATE_CODE(); notifier_list_add(&blk->remove_bs_notifiers, notify); } void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) { + GLOBAL_STATE_CODE(); notifier_list_add(&blk->insert_bs_notifiers, notify); } void blk_io_plug(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + IO_CODE(); if (bs) { bdrv_io_plug(bs); @@ -2188,6 +2319,7 @@ void blk_io_plug(BlockBackend *blk) void blk_io_unplug(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + IO_CODE(); if (bs) { bdrv_io_unplug(bs); @@ -2196,18 +2328,21 @@ void blk_io_unplug(BlockBackend *blk) BlockAcctStats *blk_get_stats(BlockBackend *blk) { + IO_CODE(); return &blk->stats; } void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, BlockCompletionFunc *cb, void *opaque) { + IO_CODE(); return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque); } int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequestFlags flags) { + IO_OR_GS_CODE(); return blk_co_pwritev(blk, offset, bytes, NULL, flags | BDRV_REQ_ZERO_WRITE); } @@ -2216,6 +2351,7 @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, int64_t bytes) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); return blk_pwritev_part(blk, offset, bytes, &qiov, 0, BDRV_REQ_WRITE_COMPRESSED); } @@ -2223,6 +2359,7 @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) { + IO_OR_GS_CODE(); if (!blk_is_available(blk)) { error_setg(errp, "No medium inserted"); return -ENOMEDIUM; @@ -2235,6 +2372,7 @@ int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int64_t pos, int size) { int ret; + GLOBAL_STATE_CODE(); if (!blk_is_available(blk)) { return -ENOMEDIUM; @@ -2254,6 +2392,7 @@ int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) { + GLOBAL_STATE_CODE(); if (!blk_is_available(blk)) { return -ENOMEDIUM; } @@ -2263,6 +2402,7 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) { + GLOBAL_STATE_CODE(); if (!blk_is_available(blk)) { return -ENOMEDIUM; } @@ -2272,6 +2412,7 @@ int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) { + GLOBAL_STATE_CODE(); if (!blk_is_available(blk)) { return -ENOMEDIUM; } @@ -2285,6 +2426,7 @@ int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) */ void blk_update_root_state(BlockBackend *blk) { + GLOBAL_STATE_CODE(); assert(blk->root); blk->root_state.open_flags = blk->root->bs->open_flags; @@ -2297,6 +2439,7 @@ void blk_update_root_state(BlockBackend *blk) */ bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk->root_state.detect_zeroes; } @@ -2306,17 +2449,20 @@ bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk) */ int blk_get_open_flags_from_root_state(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk->root_state.open_flags; } BlockBackendRootState *blk_get_root_state(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return &blk->root_state; } int blk_commit_all(void) { BlockBackend *blk = NULL; + GLOBAL_STATE_CODE(); while ((blk = blk_all_next(blk)) != NULL) { AioContext *aio_context = blk_get_aio_context(blk); @@ -2341,6 +2487,7 @@ int blk_commit_all(void) /* throttling disk I/O limits */ void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) { + GLOBAL_STATE_CODE(); throttle_group_config(&blk->public.throttle_group_member, cfg); } @@ -2349,6 +2496,7 @@ void blk_io_limits_disable(BlockBackend *blk) BlockDriverState *bs = blk_bs(blk); ThrottleGroupMember *tgm = &blk->public.throttle_group_member; assert(tgm->throttle_state); + GLOBAL_STATE_CODE(); if (bs) { bdrv_ref(bs); bdrv_drained_begin(bs); @@ -2364,12 +2512,14 @@ void blk_io_limits_disable(BlockBackend *blk) void blk_io_limits_enable(BlockBackend *blk, const char *group) { assert(!blk->public.throttle_group_member.throttle_state); + GLOBAL_STATE_CODE(); throttle_group_register_tgm(&blk->public.throttle_group_member, group, blk_get_aio_context(blk)); } void blk_io_limits_update_group(BlockBackend *blk, const char *group) { + GLOBAL_STATE_CODE(); /* this BB is not part of any group */ if (!blk->public.throttle_group_member.throttle_state) { return; @@ -2437,11 +2587,13 @@ static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter) void blk_register_buf(BlockBackend *blk, void *host, size_t size) { + GLOBAL_STATE_CODE(); bdrv_register_buf(blk_bs(blk), host, size); } void blk_unregister_buf(BlockBackend *blk, void *host) { + GLOBAL_STATE_CODE(); bdrv_unregister_buf(blk_bs(blk), host); } @@ -2451,6 +2603,8 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, BdrvRequestFlags write_flags) { int r; + IO_CODE(); + r = blk_check_byte_request(blk_in, off_in, bytes); if (r) { return r; @@ -2466,11 +2620,13 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, const BdrvChild *blk_root(BlockBackend *blk) { + GLOBAL_STATE_CODE(); return blk->root; } int blk_make_empty(BlockBackend *blk, Error **errp) { + GLOBAL_STATE_CODE(); if (!blk_is_available(blk)) { error_setg(errp, "No medium inserted"); return -ENOMEDIUM; diff --git a/block/block-copy.c b/block/block-copy.c index ce116318b5..ec46775ea5 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -17,11 +17,13 @@ #include "trace.h" #include "qapi/error.h" #include "block/block-copy.h" +#include "block/reqlist.h" #include "sysemu/block-backend.h" #include "qemu/units.h" #include "qemu/coroutine.h" #include "block/aio_task.h" #include "qemu/error-report.h" +#include "qemu/memalign.h" #define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB) #define BLOCK_COPY_MAX_BUFFER (1 * MiB) @@ -83,7 +85,6 @@ typedef struct BlockCopyTask { */ BlockCopyState *s; BlockCopyCallState *call_state; - int64_t offset; /* * @method can also be set again in the while loop of * block_copy_dirty_clusters(), but it is never accessed concurrently @@ -94,21 +95,17 @@ typedef struct BlockCopyTask { BlockCopyMethod method; /* - * Fields whose state changes throughout the execution - * Protected by lock in BlockCopyState. + * Generally, req is protected by lock in BlockCopyState, Still req.offset + * is only set on task creation, so may be read concurrently after creation. + * req.bytes is changed at most once, and need only protecting the case of + * parallel read while updating @bytes value in block_copy_task_shrink(). */ - CoQueue wait_queue; /* coroutines blocked on this task */ - /* - * Only protect the case of parallel read while updating @bytes - * value in block_copy_task_shrink(). - */ - int64_t bytes; - QLIST_ENTRY(BlockCopyTask) list; + BlockReq req; } BlockCopyTask; static int64_t task_end(BlockCopyTask *task) { - return task->offset + task->bytes; + return task->req.offset + task->req.bytes; } typedef struct BlockCopyState { @@ -136,7 +133,7 @@ typedef struct BlockCopyState { CoMutex lock; int64_t in_flight_bytes; BlockCopyMethod method; - QLIST_HEAD(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */ + BlockReqList reqs; QLIST_HEAD(, BlockCopyCallState) calls; /* * skip_unallocated: @@ -160,42 +157,6 @@ typedef struct BlockCopyState { RateLimit rate_limit; } BlockCopyState; -/* Called with lock held */ -static BlockCopyTask *find_conflicting_task(BlockCopyState *s, - int64_t offset, int64_t bytes) -{ - BlockCopyTask *t; - - QLIST_FOREACH(t, &s->tasks, list) { - if (offset + bytes > t->offset && offset < t->offset + t->bytes) { - return t; - } - } - - return NULL; -} - -/* - * If there are no intersecting tasks return false. Otherwise, wait for the - * first found intersecting tasks to finish and return true. - * - * Called with lock held. May temporary release the lock. - * Return value of 0 proves that lock was NOT released. - */ -static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset, - int64_t bytes) -{ - BlockCopyTask *task = find_conflicting_task(s, offset, bytes); - - if (!task) { - return false; - } - - qemu_co_queue_wait(&task->wait_queue, &s->lock); - - return true; -} - /* Called with lock held */ static int64_t block_copy_chunk_size(BlockCopyState *s) { @@ -239,7 +200,7 @@ block_copy_task_create(BlockCopyState *s, BlockCopyCallState *call_state, bytes = QEMU_ALIGN_UP(bytes, s->cluster_size); /* region is dirty, so no existent tasks possible in it */ - assert(!find_conflicting_task(s, offset, bytes)); + assert(!reqlist_find_conflict(&s->reqs, offset, bytes)); bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); s->in_flight_bytes += bytes; @@ -249,12 +210,9 @@ block_copy_task_create(BlockCopyState *s, BlockCopyCallState *call_state, .task.func = block_copy_task_entry, .s = s, .call_state = call_state, - .offset = offset, - .bytes = bytes, .method = s->method, }; - qemu_co_queue_init(&task->wait_queue); - QLIST_INSERT_HEAD(&s->tasks, task, list); + reqlist_init_req(&s->reqs, &task->req, offset, bytes); return task; } @@ -270,34 +228,34 @@ static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task, int64_t new_bytes) { QEMU_LOCK_GUARD(&task->s->lock); - if (new_bytes == task->bytes) { + if (new_bytes == task->req.bytes) { return; } - assert(new_bytes > 0 && new_bytes < task->bytes); + assert(new_bytes > 0 && new_bytes < task->req.bytes); - task->s->in_flight_bytes -= task->bytes - new_bytes; + task->s->in_flight_bytes -= task->req.bytes - new_bytes; bdrv_set_dirty_bitmap(task->s->copy_bitmap, - task->offset + new_bytes, task->bytes - new_bytes); + task->req.offset + new_bytes, + task->req.bytes - new_bytes); - task->bytes = new_bytes; - qemu_co_queue_restart_all(&task->wait_queue); + reqlist_shrink_req(&task->req, new_bytes); } static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret) { QEMU_LOCK_GUARD(&task->s->lock); - task->s->in_flight_bytes -= task->bytes; + task->s->in_flight_bytes -= task->req.bytes; if (ret < 0) { - bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes); + bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->req.offset, + task->req.bytes); } - QLIST_REMOVE(task, list); if (task->s->progress) { progress_set_remaining(task->s->progress, bdrv_get_dirty_count(task->s->copy_bitmap) + task->s->in_flight_bytes); } - qemu_co_queue_restart_all(&task->wait_queue); + reqlist_remove_req(&task->req); } void block_copy_state_free(BlockCopyState *s) @@ -384,8 +342,10 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, } BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + const BdrvDirtyBitmap *bitmap, Error **errp) { + ERRP_GUARD(); BlockCopyState *s; int64_t cluster_size; BdrvDirtyBitmap *copy_bitmap; @@ -402,6 +362,17 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, return NULL; } bdrv_disable_dirty_bitmap(copy_bitmap); + if (bitmap) { + if (!bdrv_merge_dirty_bitmap(copy_bitmap, bitmap, NULL, errp)) { + error_prepend(errp, "Failed to merge bitmap '%s' to internal " + "copy-bitmap: ", bdrv_dirty_bitmap_name(bitmap)); + bdrv_release_dirty_bitmap(copy_bitmap); + return NULL; + } + } else { + bdrv_set_dirty_bitmap(copy_bitmap, 0, + bdrv_dirty_bitmap_size(copy_bitmap)); + } /* * If source is in backing chain of target assume that target is going to be @@ -437,7 +408,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, ratelimit_init(&s->rate_limit); qemu_co_mutex_init(&s->lock); - QLIST_INIT(&s->tasks); + QLIST_INIT(&s->reqs); QLIST_INIT(&s->calls); return s; @@ -470,7 +441,7 @@ static coroutine_fn int block_copy_task_run(AioTaskPool *pool, aio_task_pool_wait_slot(pool); if (aio_task_pool_status(pool) < 0) { - co_put_to_shres(task->s->mem, task->bytes); + co_put_to_shres(task->s->mem, task->req.bytes); block_copy_task_end(task, -ECANCELED); g_free(task); return -ECANCELED; @@ -583,7 +554,8 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) BlockCopyMethod method = t->method; int ret; - ret = block_copy_do_copy(s, t->offset, t->bytes, &method, &error_is_read); + ret = block_copy_do_copy(s, t->req.offset, t->req.bytes, &method, + &error_is_read); WITH_QEMU_LOCK_GUARD(&s->lock) { if (s->method == t->method) { @@ -596,10 +568,10 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) t->call_state->error_is_read = error_is_read; } } else if (s->progress) { - progress_work_done(s->progress, t->bytes); + progress_work_done(s->progress, t->req.bytes); } } - co_put_to_shres(s->mem, t->bytes); + co_put_to_shres(s->mem, t->req.bytes); block_copy_task_end(t, ret); return ret; @@ -679,6 +651,18 @@ static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset, } } +void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes) +{ + QEMU_LOCK_GUARD(&s->lock); + + bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); + if (s->progress) { + progress_set_remaining(s->progress, + bdrv_get_dirty_count(s->copy_bitmap) + + s->in_flight_bytes); + } +} + /* * Reset bits in copy_bitmap starting at offset if they represent unallocated * data in the image. May reset subsequent contiguous bits. @@ -699,14 +683,7 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, bytes = clusters * s->cluster_size; if (!ret) { - qemu_co_mutex_lock(&s->lock); - bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); - if (s->progress) { - progress_set_remaining(s->progress, - bdrv_get_dirty_count(s->copy_bitmap) + - s->in_flight_bytes); - } - qemu_co_mutex_unlock(&s->lock); + block_copy_reset(s, offset, bytes); } *count = bytes; @@ -753,22 +730,22 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state) trace_block_copy_skip_range(s, offset, bytes); break; } - if (task->offset > offset) { - trace_block_copy_skip_range(s, offset, task->offset - offset); + if (task->req.offset > offset) { + trace_block_copy_skip_range(s, offset, task->req.offset - offset); } found_dirty = true; - ret = block_copy_block_status(s, task->offset, task->bytes, + ret = block_copy_block_status(s, task->req.offset, task->req.bytes, &status_bytes); assert(ret >= 0); /* never fail */ - if (status_bytes < task->bytes) { + if (status_bytes < task->req.bytes) { block_copy_task_shrink(task, status_bytes); } if (qatomic_read(&s->skip_unallocated) && !(ret & BDRV_BLOCK_ALLOCATED)) { block_copy_task_end(task, 0); - trace_block_copy_skip_range(s, task->offset, task->bytes); + trace_block_copy_skip_range(s, task->req.offset, task->req.bytes); offset = task_end(task); bytes = end - offset; g_free(task); @@ -789,11 +766,11 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state) } } - ratelimit_calculate_delay(&s->rate_limit, task->bytes); + ratelimit_calculate_delay(&s->rate_limit, task->req.bytes); - trace_block_copy_process(s, task->offset); + trace_block_copy_process(s, task->req.offset); - co_get_from_shres(s->mem, task->bytes); + co_get_from_shres(s->mem, task->req.bytes); offset = task_end(task); bytes = end - offset; @@ -861,8 +838,8 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state) * Check that there is no task we still need to * wait to complete */ - ret = block_copy_wait_one(s, call_state->offset, - call_state->bytes); + ret = reqlist_wait_one(&s->reqs, call_state->offset, + call_state->bytes, &s->lock); if (ret == 0) { /* * No pending tasks, but check again the bitmap in this @@ -870,7 +847,7 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state) * between this and the critical section in * block_copy_dirty_clusters(). * - * block_copy_wait_one return value 0 also means that it + * reqlist_wait_one return value 0 also means that it * didn't release the lock. So, we are still in the same * critical section, not interrupted by any concurrent * access to state. diff --git a/block/commit.c b/block/commit.c index b1fc7b908b..851d1c557a 100644 --- a/block/commit.c +++ b/block/commit.c @@ -20,6 +20,7 @@ #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" +#include "qemu/memalign.h" #include "sysemu/block-backend.h" enum { @@ -253,6 +254,8 @@ void commit_start(const char *job_id, BlockDriverState *bs, uint64_t base_perms, iter_shared_perms; int ret; + GLOBAL_STATE_CODE(); + assert(top != bs); if (bdrv_skip_filters(top) == bdrv_skip_filters(base)) { error_setg(errp, "Invalid files for merge: top and base are the same"); @@ -432,6 +435,8 @@ int bdrv_commit(BlockDriverState *bs) QEMU_AUTO_VFREE uint8_t *buf = NULL; Error *local_err = NULL; + GLOBAL_STATE_CODE(); + if (!drv) return -ENOMEDIUM; diff --git a/block/copy-before-write.c b/block/copy-before-write.c index c30a5ff8de..a8a06fdc09 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -33,10 +33,37 @@ #include "block/block-copy.h" #include "block/copy-before-write.h" +#include "block/reqlist.h" + +#include "qapi/qapi-visit-block-core.h" typedef struct BDRVCopyBeforeWriteState { BlockCopyState *bcs; BdrvChild *target; + + /* + * @lock: protects access to @access_bitmap, @done_bitmap and + * @frozen_read_reqs + */ + CoMutex lock; + + /* + * @access_bitmap: represents areas allowed for reading by fleecing user. + * Reading from non-dirty areas leads to -EACCES. + */ + BdrvDirtyBitmap *access_bitmap; + + /* + * @done_bitmap: represents areas that was successfully copied to @target by + * copy-before-write operations. + */ + BdrvDirtyBitmap *done_bitmap; + + /* + * @frozen_read_reqs: current read requests for fleecing user in bs->file + * node. These areas must not be rewritten by guest. + */ + BlockReqList frozen_read_reqs; } BDRVCopyBeforeWriteState; static coroutine_fn int cbw_co_preadv( @@ -46,10 +73,20 @@ static coroutine_fn int cbw_co_preadv( return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } +/* + * Do copy-before-write operation. + * + * On failure guest request must be failed too. + * + * On success, we also wait for all in-flight fleecing read requests in source + * node, and it's guaranteed that after cbw_do_copy_before_write() successful + * return there are no such requests and they will never appear. + */ static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, uint64_t offset, uint64_t bytes, BdrvRequestFlags flags) { BDRVCopyBeforeWriteState *s = bs->opaque; + int ret; uint64_t off, end; int64_t cluster_size = block_copy_cluster_size(s->bcs); @@ -60,7 +97,17 @@ static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, off = QEMU_ALIGN_DOWN(offset, cluster_size); end = QEMU_ALIGN_UP(offset + bytes, cluster_size); - return block_copy(s->bcs, off, end - off, true); + ret = block_copy(s->bcs, off, end - off, true); + if (ret < 0) { + return ret; + } + + WITH_QEMU_LOCK_GUARD(&s->lock) { + bdrv_set_dirty_bitmap(s->done_bitmap, off, end - off); + reqlist_wait_all(&s->frozen_read_reqs, off, end - off, &s->lock); + } + + return 0; } static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, @@ -108,6 +155,142 @@ static int coroutine_fn cbw_co_flush(BlockDriverState *bs) return bdrv_co_flush(bs->file->bs); } +/* + * If @offset not accessible - return NULL. + * + * Otherwise, set @pnum to some bytes that accessible from @file (@file is set + * to bs->file or to s->target). Return newly allocated BlockReq object that + * should be than passed to cbw_snapshot_read_unlock(). + * + * It's guaranteed that guest writes will not interact in the region until + * cbw_snapshot_read_unlock() called. + */ +static BlockReq *cbw_snapshot_read_lock(BlockDriverState *bs, + int64_t offset, int64_t bytes, + int64_t *pnum, BdrvChild **file) +{ + BDRVCopyBeforeWriteState *s = bs->opaque; + BlockReq *req = g_new(BlockReq, 1); + bool done; + + QEMU_LOCK_GUARD(&s->lock); + + if (bdrv_dirty_bitmap_next_zero(s->access_bitmap, offset, bytes) != -1) { + g_free(req); + return NULL; + } + + done = bdrv_dirty_bitmap_status(s->done_bitmap, offset, bytes, pnum); + if (done) { + /* + * Special invalid BlockReq, that is handled in + * cbw_snapshot_read_unlock(). We don't need to lock something to read + * from s->target. + */ + *req = (BlockReq) {.offset = -1, .bytes = -1}; + *file = s->target; + } else { + reqlist_init_req(&s->frozen_read_reqs, req, offset, bytes); + *file = bs->file; + } + + return req; +} + +static void cbw_snapshot_read_unlock(BlockDriverState *bs, BlockReq *req) +{ + BDRVCopyBeforeWriteState *s = bs->opaque; + + if (req->offset == -1 && req->bytes == -1) { + g_free(req); + return; + } + + QEMU_LOCK_GUARD(&s->lock); + + reqlist_remove_req(req); + g_free(req); +} + +static coroutine_fn int +cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) +{ + BlockReq *req; + BdrvChild *file; + int ret; + + /* TODO: upgrade to async loop using AioTask */ + while (bytes) { + int64_t cur_bytes; + + req = cbw_snapshot_read_lock(bs, offset, bytes, &cur_bytes, &file); + if (!req) { + return -EACCES; + } + + ret = bdrv_co_preadv_part(file, offset, cur_bytes, + qiov, qiov_offset, 0); + cbw_snapshot_read_unlock(bs, req); + if (ret < 0) { + return ret; + } + + bytes -= cur_bytes; + offset += cur_bytes; + qiov_offset += cur_bytes; + } + + return 0; +} + +static int coroutine_fn +cbw_co_snapshot_block_status(BlockDriverState *bs, + bool want_zero, int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file) +{ + BDRVCopyBeforeWriteState *s = bs->opaque; + BlockReq *req; + int ret; + int64_t cur_bytes; + BdrvChild *child; + + req = cbw_snapshot_read_lock(bs, offset, bytes, &cur_bytes, &child); + if (!req) { + return -EACCES; + } + + ret = bdrv_block_status(child->bs, offset, cur_bytes, pnum, map, file); + if (child == s->target) { + /* + * We refer to s->target only for areas that we've written to it. + * And we can not report unallocated blocks in s->target: this will + * break generic block-status-above logic, that will go to + * copy-before-write filtered child in this case. + */ + assert(ret & BDRV_BLOCK_ALLOCATED); + } + + cbw_snapshot_read_unlock(bs, req); + + return ret; +} + +static int coroutine_fn cbw_co_pdiscard_snapshot(BlockDriverState *bs, + int64_t offset, int64_t bytes) +{ + BDRVCopyBeforeWriteState *s = bs->opaque; + + WITH_QEMU_LOCK_GUARD(&s->lock) { + bdrv_reset_dirty_bitmap(s->access_bitmap, offset, bytes); + } + + block_copy_reset(s->bcs, offset, bytes); + + return bdrv_co_pdiscard(s->target, offset, bytes); +} + static void cbw_refresh_filename(BlockDriverState *bs) { pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), @@ -145,11 +328,54 @@ static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, } } +static bool cbw_parse_bitmap_option(QDict *options, BdrvDirtyBitmap **bitmap, + Error **errp) +{ + QDict *bitmap_qdict = NULL; + BlockDirtyBitmap *bmp_param = NULL; + Visitor *v = NULL; + bool ret = false; + + *bitmap = NULL; + + qdict_extract_subqdict(options, &bitmap_qdict, "bitmap."); + if (!qdict_size(bitmap_qdict)) { + ret = true; + goto out; + } + + v = qobject_input_visitor_new_flat_confused(bitmap_qdict, errp); + if (!v) { + goto out; + } + + visit_type_BlockDirtyBitmap(v, NULL, &bmp_param, errp); + if (!bmp_param) { + goto out; + } + + *bitmap = block_dirty_bitmap_lookup(bmp_param->node, bmp_param->name, NULL, + errp); + if (!*bitmap) { + goto out; + } + + ret = true; + +out: + qapi_free_BlockDirtyBitmap(bmp_param); + visit_free(v); + qobject_unref(bitmap_qdict); + + return ret; +} + static int cbw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVCopyBeforeWriteState *s = bs->opaque; - BdrvDirtyBitmap *copy_bitmap; + BdrvDirtyBitmap *bitmap = NULL; + int64_t cluster_size; bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, @@ -164,6 +390,10 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } + if (!cbw_parse_bitmap_option(options, &bitmap, errp)) { + return -EINVAL; + } + bs->total_sectors = bs->file->bs->total_sectors; bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); @@ -171,14 +401,32 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags); - s->bcs = block_copy_state_new(bs->file, s->target, errp); + s->bcs = block_copy_state_new(bs->file, s->target, bitmap, errp); if (!s->bcs) { error_prepend(errp, "Cannot create block-copy-state: "); return -EINVAL; } - copy_bitmap = block_copy_dirty_bitmap(s->bcs); - bdrv_set_dirty_bitmap(copy_bitmap, 0, bdrv_dirty_bitmap_size(copy_bitmap)); + cluster_size = block_copy_cluster_size(s->bcs); + + s->done_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); + if (!s->done_bitmap) { + return -EINVAL; + } + bdrv_disable_dirty_bitmap(s->done_bitmap); + + /* s->access_bitmap starts equal to bcs bitmap */ + s->access_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); + if (!s->access_bitmap) { + return -EINVAL; + } + bdrv_disable_dirty_bitmap(s->access_bitmap); + bdrv_dirty_bitmap_merge_internal(s->access_bitmap, + block_copy_dirty_bitmap(s->bcs), NULL, + true); + + qemu_co_mutex_init(&s->lock); + QLIST_INIT(&s->frozen_read_reqs); return 0; } @@ -187,6 +435,9 @@ static void cbw_close(BlockDriverState *bs) { BDRVCopyBeforeWriteState *s = bs->opaque; + bdrv_release_dirty_bitmap(s->access_bitmap); + bdrv_release_dirty_bitmap(s->done_bitmap); + block_copy_state_free(s->bcs); s->bcs = NULL; } @@ -204,6 +455,10 @@ BlockDriver bdrv_cbw_filter = { .bdrv_co_pdiscard = cbw_co_pdiscard, .bdrv_co_flush = cbw_co_flush, + .bdrv_co_preadv_snapshot = cbw_co_preadv_snapshot, + .bdrv_co_pdiscard_snapshot = cbw_co_pdiscard_snapshot, + .bdrv_co_snapshot_block_status = cbw_co_snapshot_block_status, + .bdrv_refresh_filename = cbw_refresh_filename, .bdrv_child_perm = cbw_child_perm, @@ -223,6 +478,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, QDict *opts; assert(source->total_sectors == target->total_sectors); + GLOBAL_STATE_CODE(); opts = qdict_new(); qdict_put_str(opts, "driver", "copy-before-write"); @@ -245,6 +501,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, void bdrv_cbw_drop(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); bdrv_drop_filter(bs, &error_abort); bdrv_unref(bs); } diff --git a/block/copy-before-write.h b/block/copy-before-write.h index 51847e711a..6e72bb25e9 100644 --- a/block/copy-before-write.h +++ b/block/copy-before-write.h @@ -29,6 +29,13 @@ #include "block/block_int.h" #include "block/block-copy.h" +/* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + BlockDriverState *bdrv_cbw_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, diff --git a/block/coroutines.h b/block/coroutines.h index c8c14a29c8..8ea70d45f9 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -30,17 +30,17 @@ /* For blk_bs() in generated block/block-gen.c */ #include "sysemu/block-backend.h" +/* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + int coroutine_fn bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp); -int generated_co_wrapper -bdrv_preadv(BdrvChild *child, int64_t offset, unsigned int bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); -int generated_co_wrapper -bdrv_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - int coroutine_fn bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, @@ -52,6 +52,52 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, int64_t *map, BlockDriverState **file, int *depth); + +int coroutine_fn bdrv_co_readv_vmstate(BlockDriverState *bs, + QEMUIOVector *qiov, int64_t pos); +int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs, + QEMUIOVector *qiov, int64_t pos); + +int coroutine_fn +nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking, + Error **errp); + + +int coroutine_fn +blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags); + + +int coroutine_fn +blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); + +int coroutine_fn +blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); + +int coroutine_fn +blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); + +int coroutine_fn blk_co_do_flush(BlockBackend *blk); + + +/* + * "I/O or GS" API functions. These functions can run without + * the BQL, but only in one specific iothread/main loop. + * + * See include/block/block-io.h for more information about + * the "I/O or GS" API. + */ + +int generated_co_wrapper +bdrv_preadv(BdrvChild *child, int64_t offset, unsigned int bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags); + +int generated_co_wrapper +bdrv_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags); + int generated_co_wrapper bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, @@ -63,46 +109,24 @@ bdrv_common_block_status_above(BlockDriverState *bs, int64_t *map, BlockDriverState **file, int *depth); - -int coroutine_fn bdrv_co_readv_vmstate(BlockDriverState *bs, - QEMUIOVector *qiov, int64_t pos); -int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs, - QEMUIOVector *qiov, int64_t pos); - int generated_co_wrapper -nbd_do_establish_connection(BlockDriverState *bs, Error **errp); -int coroutine_fn -nbd_co_do_establish_connection(BlockDriverState *bs, Error **errp); - +nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp); int generated_co_wrapper blk_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -int coroutine_fn -blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - int generated_co_wrapper blk_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); -int coroutine_fn -blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags); int generated_co_wrapper blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); -int coroutine_fn -blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); int generated_co_wrapper blk_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); -int coroutine_fn -blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); int generated_co_wrapper blk_do_flush(BlockBackend *blk); -int coroutine_fn blk_co_do_flush(BlockBackend *blk); #endif /* BLOCK_COROUTINES_INT_H */ diff --git a/block/create.c b/block/create.c index 89812669df..4df43f11f4 100644 --- a/block/create.c +++ b/block/create.c @@ -42,6 +42,8 @@ static int coroutine_fn blockdev_create_run(Job *job, Error **errp) BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); int ret; + GLOBAL_STATE_CODE(); + job_progress_set_remaining(&s->common, 1); ret = s->drv->bdrv_co_create(s->opts, errp); job_progress_update(&s->common, 1); diff --git a/block/crypto.c b/block/crypto.c index c8ba4681e2..1ba82984ef 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -31,6 +31,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #include "crypto.h" typedef struct BlockCrypto BlockCrypto; @@ -777,6 +778,37 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) return spec_info; } +static int +block_crypto_amend_prepare(BlockDriverState *bs, Error **errp) +{ + BlockCrypto *crypto = bs->opaque; + int ret; + + /* apply for exclusive read/write permissions to the underlying file */ + crypto->updating_keys = true; + ret = bdrv_child_refresh_perms(bs, bs->file, errp); + if (ret < 0) { + /* Well, in this case we will not be updating any keys */ + crypto->updating_keys = false; + } + return ret; +} + +static void +block_crypto_amend_cleanup(BlockDriverState *bs) +{ + BlockCrypto *crypto = bs->opaque; + Error *errp = NULL; + + /* release exclusive read/write permissions to the underlying file */ + crypto->updating_keys = false; + bdrv_child_refresh_perms(bs, bs->file, &errp); + + if (errp) { + error_report_err(errp); + } +} + static int block_crypto_amend_options_generic_luks(BlockDriverState *bs, QCryptoBlockAmendOptions *amend_options, @@ -784,30 +816,17 @@ block_crypto_amend_options_generic_luks(BlockDriverState *bs, Error **errp) { BlockCrypto *crypto = bs->opaque; - int ret; assert(crypto); assert(crypto->block); - /* apply for exclusive read/write permissions to the underlying file*/ - crypto->updating_keys = true; - ret = bdrv_child_refresh_perms(bs, bs->file, errp); - if (ret) { - goto cleanup; - } - - ret = qcrypto_block_amend_options(crypto->block, - block_crypto_read_func, - block_crypto_write_func, - bs, - amend_options, - force, - errp); -cleanup: - /* release exclusive read/write permissions to the underlying file*/ - crypto->updating_keys = false; - bdrv_child_refresh_perms(bs, bs->file, errp); - return ret; + return qcrypto_block_amend_options(crypto->block, + block_crypto_read_func, + block_crypto_write_func, + bs, + amend_options, + force, + errp); } static int @@ -833,8 +852,16 @@ block_crypto_amend_options_luks(BlockDriverState *bs, if (!amend_options) { goto cleanup; } + + ret = block_crypto_amend_prepare(bs, errp); + if (ret) { + goto perm_cleanup; + } ret = block_crypto_amend_options_generic_luks(bs, amend_options, force, errp); + +perm_cleanup: + block_crypto_amend_cleanup(bs); cleanup: qapi_free_QCryptoBlockAmendOptions(amend_options); return ret; @@ -931,6 +958,8 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_get_specific_info = block_crypto_get_specific_info_luks, .bdrv_amend_options = block_crypto_amend_options_luks, .bdrv_co_amend = block_crypto_co_amend_luks, + .bdrv_amend_pre_run = block_crypto_amend_prepare, + .bdrv_amend_clean = block_crypto_amend_cleanup, .is_format = true, diff --git a/block/curl.c b/block/curl.c index 6a6cd72975..1e0f609579 100644 --- a/block/curl.c +++ b/block/curl.c @@ -458,38 +458,51 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) if (!state->curl) { return -EIO; } - curl_easy_setopt(state->curl, CURLOPT_URL, s->url); - curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, - (long) s->sslverify); - curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, - s->sslverify ? 2L : 0L); - if (s->cookie) { - curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); + if (curl_easy_setopt(state->curl, CURLOPT_URL, s->url) || + curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, + (long) s->sslverify) || + curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, + s->sslverify ? 2L : 0L)) { + goto err; + } + if (s->cookie) { + if (curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie)) { + goto err; + } + } + if (curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout) || + curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, + (void *)curl_read_cb) || + curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) || + curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) || + curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) || + curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) || + curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) || + curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) || + curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) { + goto err; } - curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout); - curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, - (void *)curl_read_cb); - curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state); - curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state); - curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1); - curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg); - curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1); - if (s->username) { - curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username); + if (curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username)) { + goto err; + } } if (s->password) { - curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password); + if (curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password)) { + goto err; + } } if (s->proxyusername) { - curl_easy_setopt(state->curl, - CURLOPT_PROXYUSERNAME, s->proxyusername); + if (curl_easy_setopt(state->curl, + CURLOPT_PROXYUSERNAME, s->proxyusername)) { + goto err; + } } if (s->proxypassword) { - curl_easy_setopt(state->curl, - CURLOPT_PROXYPASSWORD, s->proxypassword); + if (curl_easy_setopt(state->curl, + CURLOPT_PROXYPASSWORD, s->proxypassword)) { + goto err; + } } /* Restrict supported protocols to avoid security issues in the more @@ -499,18 +512,27 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) * Restricting protocols is only supported from 7.19.4 upwards. */ #if LIBCURL_VERSION_NUM >= 0x071304 - curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS); - curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS); + if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) || + curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) { + goto err; + } #endif #ifdef DEBUG_VERBOSE - curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1); + if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) { + goto err; + } #endif } state->s = s; return 0; + +err: + curl_easy_cleanup(state->curl); + state->curl = NULL; + return -EIO; } /* Called with s->mutex held. */ @@ -759,14 +781,19 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, // Get file size if (curl_init_state(s, state) < 0) { + pstrcpy(state->errmsg, CURL_ERROR_SIZE, + "curl library initialization failed."); goto out; } s->accept_range = false; - curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1); - curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, - curl_header_cb); - curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s); + if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) || + curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) || + curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) { + pstrcpy(state->errmsg, CURL_ERROR_SIZE, + "curl library initialization failed."); + goto out; + } if (curl_easy_perform(state->curl)) goto out; if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) { @@ -879,9 +906,8 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); trace_curl_setup_preadv(acb->bytes, start, state->range); - curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); - - if (curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) { + if (curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range) || + curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) { state->acb[0] = NULL; acb->ret = -EIO; diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 0ef46163e3..da1b91166f 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -496,6 +496,7 @@ static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque) bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, uint32_t granularity, Error **errp) { + IO_CODE(); if (qemu_in_coroutine()) { return bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp); } else { @@ -656,6 +657,7 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) { + IO_CODE(); assert(!bdrv_dirty_bitmap_readonly(bitmap)); bdrv_dirty_bitmaps_lock(bitmap->bs); if (!out) { @@ -673,6 +675,7 @@ void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) { HBitmap *tmp = bitmap->bitmap; assert(!bdrv_dirty_bitmap_readonly(bitmap)); + GLOBAL_STATE_CODE(); bitmap->bitmap = backup; hbitmap_free(tmp); } @@ -737,6 +740,7 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap) void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes) { BdrvDirtyBitmap *bitmap; + IO_CODE(); if (QLIST_EMPTY(&bs->dirty_bitmaps)) { return; @@ -875,16 +879,25 @@ bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, dirty_start, dirty_count); } +bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes, int64_t *count) +{ + return hbitmap_status(bitmap->bitmap, offset, bytes, count); +} + /** * bdrv_merge_dirty_bitmap: merge src into dest. * Ensures permissions on bitmaps are reasonable; use for public API. * * @backup: If provided, make a copy of dest here prior to merge. + * + * Returns true on success, false on failure. In case of failure bitmaps are + * untouched. */ -void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, +bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, HBitmap **backup, Error **errp) { - bool ret; + bool ret = false; bdrv_dirty_bitmaps_lock(dest->bs); if (src->bs != dest->bs) { @@ -912,6 +925,8 @@ out: if (src->bs != dest->bs) { bdrv_dirty_bitmaps_unlock(src->bs); } + + return ret; } /** @@ -928,6 +943,7 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, bool lock) { bool ret; + IO_CODE(); assert(!bdrv_dirty_bitmap_readonly(dest)); assert(!bdrv_dirty_bitmap_inconsistent(dest)); diff --git a/block/dmg.c b/block/dmg.c index 447901fbb8..c626587f9c 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -27,6 +27,7 @@ #include "qemu/bswap.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "qemu/memalign.h" #include "dmg.h" int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, diff --git a/block/export/export.c b/block/export/export.c index 6d3b9964c8..7253af3bc3 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -139,7 +139,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) * access since the export could be available before migration handover. * ctx was acquired in the caller. */ - bdrv_invalidate_cache(bs, NULL); + bdrv_activate(bs, NULL); perm = BLK_PERM_CONSISTENT_READ; if (export->writable) { diff --git a/block/export/fuse.c b/block/export/fuse.c index fdda8e3c81..e80b24a867 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -19,6 +19,7 @@ #define FUSE_USE_VERSION 31 #include "qemu/osdep.h" +#include "qemu/memalign.h" #include "block/aio.h" #include "block/block.h" #include "block/export.h" @@ -86,8 +87,8 @@ static int fuse_export_create(BlockExport *blk_exp, assert(blk_exp_args->type == BLOCK_EXPORT_TYPE_FUSE); - /* For growable exports, take the RESIZE permission */ - if (args->growable) { + /* For growable and writable exports, take the RESIZE permission */ + if (args->growable || blk_exp_args->writable) { uint64_t blk_perm, blk_shared_perm; blk_get_perm(exp->common.blk, &blk_perm, &blk_shared_perm); @@ -392,14 +393,23 @@ static int fuse_do_truncate(const FuseExport *exp, int64_t size, { uint64_t blk_perm, blk_shared_perm; BdrvRequestFlags truncate_flags = 0; - int ret; + bool add_resize_perm; + int ret, ret_check; + + /* Growable and writable exports have a permanent RESIZE permission */ + add_resize_perm = !exp->growable && !exp->writable; if (req_zero_write) { truncate_flags |= BDRV_REQ_ZERO_WRITE; } - /* Growable exports have a permanent RESIZE permission */ - if (!exp->growable) { + if (add_resize_perm) { + + if (!qemu_in_main_thread()) { + /* Changing permissions like below only works in the main thread */ + return -EPERM; + } + blk_get_perm(exp->common.blk, &blk_perm, &blk_shared_perm); ret = blk_set_perm(exp->common.blk, blk_perm | BLK_PERM_RESIZE, @@ -412,9 +422,11 @@ static int fuse_do_truncate(const FuseExport *exp, int64_t size, ret = blk_truncate(exp->common.blk, size, true, prealloc, truncate_flags, NULL); - if (!exp->growable) { + if (add_resize_perm) { /* Must succeed, because we are only giving up the RESIZE permission */ - blk_set_perm(exp->common.blk, blk_perm, blk_shared_perm, &error_abort); + ret_check = blk_set_perm(exp->common.blk, blk_perm, + blk_shared_perm, &error_abort); + assert(ret_check == 0); } return ret; diff --git a/block/file-posix.c b/block/file-posix.c index 1f1756e192..48cd096624 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -31,6 +30,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/units.h" +#include "qemu/memalign.h" #include "trace.h" #include "block/thread-pool.h" #include "qemu/iov.h" @@ -385,7 +385,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) { BDRVRawState *s = bs->opaque; char *buf; - size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size); + size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size()); size_t alignments[] = {1, 512, 1024, 2048, 4096}; /* For SCSI generic devices the alignment is not really used. @@ -1022,6 +1022,21 @@ static int raw_handle_perm_lock(BlockDriverState *bs, return ret; } +/* Sets a specific flag */ +static int fcntl_setfl(int fd, int flag) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + return -errno; + } + if (fcntl(fd, F_SETFL, flags | flag) == -1) { + return -errno; + } + return 0; +} + static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, int *open_flags, uint64_t perm, bool force_dup, Error **errp) @@ -1260,7 +1275,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) raw_probe_alignment(bs, s->fd, errp); bs->bl.min_mem_alignment = s->buf_align; - bs->bl.opt_mem_alignment = MAX(s->buf_align, qemu_real_host_page_size); + bs->bl.opt_mem_alignment = MAX(s->buf_align, qemu_real_host_page_size()); /* * Maximum transfers are best effort, so it is okay to ignore any @@ -1885,7 +1900,7 @@ static int allocate_first_block(int fd, size_t max_size) size_t write_size = (max_size < MAX_BLOCKSIZE) ? BDRV_SECTOR_SIZE : MAX_BLOCKSIZE; - size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size); + size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size()); void *buf; ssize_t n; int ret; @@ -3319,17 +3334,23 @@ BlockDriver bdrv_file = { #if defined(__APPLE__) && defined(__MACH__) static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize, int flags); + +#if !defined(MAC_OS_VERSION_12_0) \ + || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) +#define IOMainPort IOMasterPort +#endif + static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) { kern_return_t kernResult = KERN_FAILURE; - mach_port_t masterPort; + mach_port_t mainPort; CFMutableDictionaryRef classesToMatch; const char *matching_array[] = {kIODVDMediaClass, kIOCDMediaClass}; char *mediaType = NULL; - kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); + kernResult = IOMainPort(MACH_PORT_NULL, &mainPort); if ( KERN_SUCCESS != kernResult ) { - printf( "IOMasterPort returned %d\n", kernResult ); + printf("IOMainPort returned %d\n", kernResult); } int index; @@ -3342,7 +3363,7 @@ static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) } CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue); - kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, + kernResult = IOServiceGetMatchingServices(mainPort, classesToMatch, mediaIterator); if (kernResult != KERN_SUCCESS) { error_report("Note: IOServiceGetMatchingServices returned %d", diff --git a/block/io.c b/block/io.c index 4e4cb556c5..9769ec53b0 100644 --- a/block/io.c +++ b/block/io.c @@ -32,6 +32,7 @@ #include "block/coroutines.h" #include "block/write-threshold.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -70,6 +71,7 @@ static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c, void bdrv_parent_drained_end_single(BdrvChild *c) { int drained_end_counter = 0; + IO_OR_GS_CODE(); bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter); BDRV_POLL_WHILE(c->bs, qatomic_read(&drained_end_counter) > 0); } @@ -114,6 +116,7 @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) { + IO_OR_GS_CODE(); c->parent_quiesce_counter++; if (c->klass->drained_begin) { c->klass->drained_begin(c); @@ -164,6 +167,8 @@ void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) BdrvChild *c; bool have_limits; + GLOBAL_STATE_CODE(); + if (tran) { BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1); *s = (BdrvRefreshLimitsState) { @@ -189,10 +194,6 @@ void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) QLIST_FOREACH(c, &bs->children, next) { if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW)) { - bdrv_refresh_limits(c->bs, tran, errp); - if (*errp) { - return; - } bdrv_merge_limits(&bs->bl, &c->bs->bl); have_limits = true; } @@ -200,7 +201,7 @@ void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) if (!have_limits) { bs->bl.min_mem_alignment = 512; - bs->bl.opt_mem_alignment = qemu_real_host_page_size; + bs->bl.opt_mem_alignment = qemu_real_host_page_size(); /* Safe default since most protocols use readv()/writev()/etc */ bs->bl.max_iov = IOV_MAX; @@ -226,12 +227,14 @@ void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) */ void bdrv_enable_copy_on_read(BlockDriverState *bs) { + IO_CODE(); qatomic_inc(&bs->copy_on_read); } void bdrv_disable_copy_on_read(BlockDriverState *bs) { int old = qatomic_fetch_dec(&bs->copy_on_read); + IO_CODE(); assert(old >= 1); } @@ -303,6 +306,7 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, BdrvChild *ignore_parent, bool ignore_bds_parents) { BdrvChild *child, *next; + IO_OR_GS_CODE(); if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) { return true; @@ -426,6 +430,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent, bool ignore_bds_parents) { + IO_OR_GS_CODE(); assert(!qemu_in_coroutine()); /* Stop things in parent-to-child order */ @@ -477,11 +482,13 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, void bdrv_drained_begin(BlockDriverState *bs) { + IO_OR_GS_CODE(); bdrv_do_drained_begin(bs, false, NULL, false, true); } void bdrv_subtree_drained_begin(BlockDriverState *bs) { + IO_OR_GS_CODE(); bdrv_do_drained_begin(bs, true, NULL, false, true); } @@ -538,18 +545,21 @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, void bdrv_drained_end(BlockDriverState *bs) { int drained_end_counter = 0; + IO_OR_GS_CODE(); bdrv_do_drained_end(bs, false, NULL, false, &drained_end_counter); BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); } void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter) { + IO_CODE(); bdrv_do_drained_end(bs, false, NULL, false, drained_end_counter); } void bdrv_subtree_drained_end(BlockDriverState *bs) { int drained_end_counter = 0; + IO_OR_GS_CODE(); bdrv_do_drained_end(bs, true, NULL, false, &drained_end_counter); BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); } @@ -557,6 +567,7 @@ void bdrv_subtree_drained_end(BlockDriverState *bs) void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) { int i; + IO_OR_GS_CODE(); for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { bdrv_do_drained_begin(child->bs, true, child, false, true); @@ -567,6 +578,7 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) { int drained_end_counter = 0; int i; + IO_OR_GS_CODE(); for (i = 0; i < old_parent->recursive_quiesce_counter; i++) { bdrv_do_drained_end(child->bs, true, child, false, @@ -585,6 +597,7 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) */ void coroutine_fn bdrv_co_drain(BlockDriverState *bs) { + IO_OR_GS_CODE(); assert(qemu_in_coroutine()); bdrv_drained_begin(bs); bdrv_drained_end(bs); @@ -592,6 +605,7 @@ void coroutine_fn bdrv_co_drain(BlockDriverState *bs) void bdrv_drain(BlockDriverState *bs) { + IO_OR_GS_CODE(); bdrv_drained_begin(bs); bdrv_drained_end(bs); } @@ -612,6 +626,7 @@ static bool bdrv_drain_all_poll(void) { BlockDriverState *bs = NULL; bool result = false; + GLOBAL_STATE_CODE(); /* bdrv_drain_poll() can't make changes to the graph and we are holding the * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ @@ -640,6 +655,7 @@ static bool bdrv_drain_all_poll(void) void bdrv_drain_all_begin(void) { BlockDriverState *bs = NULL; + GLOBAL_STATE_CODE(); if (qemu_in_coroutine()) { bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true, NULL); @@ -682,6 +698,7 @@ void bdrv_drain_all_begin(void) void bdrv_drain_all_end_quiesce(BlockDriverState *bs) { int drained_end_counter = 0; + GLOBAL_STATE_CODE(); g_assert(bs->quiesce_counter > 0); g_assert(!bs->refcnt); @@ -696,6 +713,7 @@ void bdrv_drain_all_end(void) { BlockDriverState *bs = NULL; int drained_end_counter = 0; + GLOBAL_STATE_CODE(); /* * bdrv queue is managed by record/replay, @@ -723,6 +741,7 @@ void bdrv_drain_all_end(void) void bdrv_drain_all(void) { + GLOBAL_STATE_CODE(); bdrv_drain_all_begin(); bdrv_drain_all_end(); } @@ -867,6 +886,7 @@ BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs) { BdrvTrackedRequest *req; Coroutine *self = qemu_coroutine_self(); + IO_CODE(); QLIST_FOREACH(req, &bs->tracked_requests, list) { if (req->co == self) { @@ -886,7 +906,7 @@ void bdrv_round_to_clusters(BlockDriverState *bs, int64_t *cluster_bytes) { BlockDriverInfo bdi; - + IO_CODE(); if (bdrv_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) { *cluster_offset = offset; *cluster_bytes = bytes; @@ -912,16 +932,19 @@ static int bdrv_get_cluster_size(BlockDriverState *bs) void bdrv_inc_in_flight(BlockDriverState *bs) { + IO_CODE(); qatomic_inc(&bs->in_flight); } void bdrv_wakeup(BlockDriverState *bs) { + IO_CODE(); aio_wait_kick(); } void bdrv_dec_in_flight(BlockDriverState *bs) { + IO_CODE(); qatomic_dec(&bs->in_flight); bdrv_wakeup(bs); } @@ -946,6 +969,7 @@ bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, uint64_t align) { bool waited; + IO_CODE(); qemu_co_mutex_lock(&req->bs->reqs_lock); @@ -1040,6 +1064,7 @@ static int bdrv_check_request32(int64_t offset, int64_t bytes, int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, BdrvRequestFlags flags) { + IO_CODE(); return bdrv_pwritev(child, offset, bytes, NULL, BDRV_REQ_ZERO_WRITE | flags); } @@ -1058,6 +1083,7 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) int ret; int64_t target_size, bytes, offset = 0; BlockDriverState *bs = child->bs; + IO_CODE(); target_size = bdrv_getlength(bs); if (target_size < 0) { @@ -1090,6 +1116,7 @@ int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes) { int ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_CODE(); if (bytes < 0) { return -EINVAL; @@ -1111,6 +1138,7 @@ int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, { int ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_CODE(); if (bytes < 0) { return -EINVAL; @@ -1131,6 +1159,7 @@ int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, const void *buf, int64_t count) { int ret; + IO_CODE(); ret = bdrv_pwrite(child, offset, buf, count); if (ret < 0) { @@ -1797,6 +1826,7 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { + IO_CODE(); return bdrv_co_preadv_part(child, offset, bytes, qiov, 0, flags); } @@ -1809,6 +1839,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, BdrvTrackedRequest req; BdrvRequestPadding pad; int ret; + IO_CODE(); trace_bdrv_co_preadv_part(bs, offset, bytes, flags); @@ -2173,6 +2204,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, padding = bdrv_init_padding(bs, offset, bytes, &pad); if (padding) { + assert(!(flags & BDRV_REQ_NO_WAIT)); bdrv_make_request_serialising(req, align); bdrv_padding_rmw_read(child, req, &pad, true); @@ -2230,6 +2262,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { + IO_CODE(); return bdrv_co_pwritev_part(child, offset, bytes, qiov, 0, flags); } @@ -2243,6 +2276,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, BdrvRequestPadding pad; int ret; bool padded = false; + IO_CODE(); trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); @@ -2307,6 +2341,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, * serialize the request to prevent interactions of the * widened region with other transactions. */ + assert(!(flags & BDRV_REQ_NO_WAIT)); bdrv_make_request_serialising(&req, align); bdrv_padding_rmw_read(child, &req, &pad, false); } @@ -2326,6 +2361,7 @@ out: int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, BdrvRequestFlags flags) { + IO_CODE(); trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); if (!(child->bs->open_flags & BDRV_O_UNMAP)) { @@ -2345,6 +2381,8 @@ int bdrv_flush_all(void) BlockDriverState *bs = NULL; int result = 0; + GLOBAL_STATE_CODE(); + /* * bdrv queue is managed by record/replay, * creating new flush request for stopping @@ -2639,6 +2677,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *p; int64_t eof = 0; int dummy; + IO_CODE(); assert(!include_base || base); /* Can't include NULL base */ @@ -2728,6 +2767,7 @@ int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { + IO_CODE(); return bdrv_common_block_status_above(bs, base, false, true, offset, bytes, pnum, map, file, NULL); } @@ -2735,6 +2775,7 @@ int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { + IO_CODE(); return bdrv_block_status_above(bs, bdrv_filter_or_cow_bs(bs), offset, bytes, pnum, map, file); } @@ -2751,6 +2792,7 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, { int ret; int64_t pnum = bytes; + IO_CODE(); if (!bytes) { return 1; @@ -2771,6 +2813,7 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, { int ret; int64_t dummy; + IO_CODE(); ret = bdrv_common_block_status_above(bs, bs, true, false, offset, bytes, pnum ? pnum : &dummy, NULL, @@ -2807,6 +2850,7 @@ int bdrv_is_allocated_above(BlockDriverState *top, int ret = bdrv_common_block_status_above(top, base, include_base, false, offset, bytes, pnum, NULL, NULL, &depth); + IO_CODE(); if (ret < 0) { return ret; } @@ -2823,6 +2867,7 @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) BlockDriver *drv = bs->drv; BlockDriverState *child_bs = bdrv_primary_bs(bs); int ret; + IO_CODE(); ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); if (ret < 0) { @@ -2854,6 +2899,7 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) BlockDriver *drv = bs->drv; BlockDriverState *child_bs = bdrv_primary_bs(bs); int ret; + IO_CODE(); ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); if (ret < 0) { @@ -2884,6 +2930,7 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); int ret = bdrv_writev_vmstate(bs, &qiov, pos); + IO_CODE(); return ret < 0 ? ret : size; } @@ -2893,6 +2940,7 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); int ret = bdrv_readv_vmstate(bs, &qiov, pos); + IO_CODE(); return ret < 0 ? ret : size; } @@ -2902,6 +2950,7 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, void bdrv_aio_cancel(BlockAIOCB *acb) { + IO_CODE(); qemu_aio_ref(acb); bdrv_aio_cancel_async(acb); while (acb->refcnt > 1) { @@ -2926,6 +2975,7 @@ void bdrv_aio_cancel(BlockAIOCB *acb) * In either case the completion callback must be called. */ void bdrv_aio_cancel_async(BlockAIOCB *acb) { + IO_CODE(); if (acb->aiocb_info->cancel_async) { acb->aiocb_info->cancel_async(acb); } @@ -2940,6 +2990,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) BdrvChild *child; int current_gen; int ret = 0; + IO_CODE(); bdrv_inc_in_flight(bs); @@ -3065,6 +3116,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t max_pdiscard; int head, tail, align; BlockDriverState *bs = child->bs; + IO_CODE(); if (!bs || !bs->drv || !bdrv_is_inserted(bs)) { return -ENOMEDIUM; @@ -3183,6 +3235,7 @@ int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) .coroutine = qemu_coroutine_self(), }; BlockAIOCB *acb; + IO_CODE(); bdrv_inc_in_flight(bs); if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) { @@ -3207,17 +3260,20 @@ out: void *qemu_blockalign(BlockDriverState *bs, size_t size) { + IO_CODE(); return qemu_memalign(bdrv_opt_mem_align(bs), size); } void *qemu_blockalign0(BlockDriverState *bs, size_t size) { + IO_CODE(); return memset(qemu_blockalign(bs, size), 0, size); } void *qemu_try_blockalign(BlockDriverState *bs, size_t size) { size_t align = bdrv_opt_mem_align(bs); + IO_CODE(); /* Ensure that NULL is never returned on success */ assert(align > 0); @@ -3231,6 +3287,7 @@ void *qemu_try_blockalign(BlockDriverState *bs, size_t size) void *qemu_try_blockalign0(BlockDriverState *bs, size_t size) { void *mem = qemu_try_blockalign(bs, size); + IO_CODE(); if (mem) { memset(mem, 0, size); @@ -3246,6 +3303,7 @@ bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) { int i; size_t alignment = bdrv_min_mem_align(bs); + IO_CODE(); for (i = 0; i < qiov->niov; i++) { if ((uintptr_t) qiov->iov[i].iov_base % alignment) { @@ -3262,6 +3320,7 @@ bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) void bdrv_io_plug(BlockDriverState *bs) { BdrvChild *child; + IO_CODE(); QLIST_FOREACH(child, &bs->children, next) { bdrv_io_plug(child->bs); @@ -3278,6 +3337,7 @@ void bdrv_io_plug(BlockDriverState *bs) void bdrv_io_unplug(BlockDriverState *bs) { BdrvChild *child; + IO_CODE(); assert(bs->io_plugged); if (qatomic_fetch_dec(&bs->io_plugged) == 1) { @@ -3296,6 +3356,7 @@ void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size) { BdrvChild *child; + GLOBAL_STATE_CODE(); if (bs->drv && bs->drv->bdrv_register_buf) { bs->drv->bdrv_register_buf(bs, host, size); } @@ -3308,6 +3369,7 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) { BdrvChild *child; + GLOBAL_STATE_CODE(); if (bs->drv && bs->drv->bdrv_unregister_buf) { bs->drv->bdrv_unregister_buf(bs, host); } @@ -3328,6 +3390,8 @@ static int coroutine_fn bdrv_co_copy_range_internal( /* TODO We can support BDRV_REQ_NO_FALLBACK here */ assert(!(read_flags & BDRV_REQ_NO_FALLBACK)); assert(!(write_flags & BDRV_REQ_NO_FALLBACK)); + assert(!(read_flags & BDRV_REQ_NO_WAIT)); + assert(!(write_flags & BDRV_REQ_NO_WAIT)); if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) { return -ENOMEDIUM; @@ -3402,6 +3466,7 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { + IO_CODE(); trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags); return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, @@ -3418,6 +3483,7 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { + IO_CODE(); trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags); return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, @@ -3429,6 +3495,7 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { + IO_CODE(); return bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags); @@ -3461,7 +3528,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, BdrvTrackedRequest req; int64_t old_size, new_bytes; int ret; - + IO_CODE(); /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ if (!drv) { @@ -3579,6 +3646,7 @@ out: void bdrv_cancel_in_flight(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); if (!bs || !bs->drv) { return; } @@ -3587,3 +3655,75 @@ void bdrv_cancel_in_flight(BlockDriverState *bs) bs->drv->bdrv_cancel_in_flight(bs); } } + +int coroutine_fn +bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) +{ + BlockDriverState *bs = child->bs; + BlockDriver *drv = bs->drv; + int ret; + IO_CODE(); + + if (!drv) { + return -ENOMEDIUM; + } + + if (!drv->bdrv_co_preadv_snapshot) { + return -ENOTSUP; + } + + bdrv_inc_in_flight(bs); + ret = drv->bdrv_co_preadv_snapshot(bs, offset, bytes, qiov, qiov_offset); + bdrv_dec_in_flight(bs); + + return ret; +} + +int coroutine_fn +bdrv_co_snapshot_block_status(BlockDriverState *bs, + bool want_zero, int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file) +{ + BlockDriver *drv = bs->drv; + int ret; + IO_CODE(); + + if (!drv) { + return -ENOMEDIUM; + } + + if (!drv->bdrv_co_snapshot_block_status) { + return -ENOTSUP; + } + + bdrv_inc_in_flight(bs); + ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes, + pnum, map, file); + bdrv_dec_in_flight(bs); + + return ret; +} + +int coroutine_fn +bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) +{ + BlockDriver *drv = bs->drv; + int ret; + IO_CODE(); + + if (!drv) { + return -ENOMEDIUM; + } + + if (!drv->bdrv_co_pdiscard_snapshot) { + return -ENOTSUP; + } + + bdrv_inc_in_flight(bs); + ret = drv->bdrv_co_pdiscard_snapshot(bs, offset, bytes); + bdrv_dec_in_flight(bs); + + return ret; +} diff --git a/block/io_uring.c b/block/io_uring.c index 782afdb433..0b401512b9 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -10,7 +10,6 @@ */ #include "qemu/osdep.h" #include -#include "qemu-common.h" #include "block/aio.h" #include "qemu/queue.h" #include "block/block.h" diff --git a/block/iscsi.c b/block/iscsi.c index 51f2a5eeaa..d707d0b354 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -28,7 +28,7 @@ #include #include #include -#include "qemu-common.h" +#include "sysemu/sysemu.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/bitops.h" diff --git a/block/meson.build b/block/meson.build index 8a1ce58c9c..0b2a60c99b 100644 --- a/block/meson.build +++ b/block/meson.build @@ -32,7 +32,9 @@ block_ss.add(files( 'qcow2.c', 'quorum.c', 'raw-format.c', + 'reqlist.c', 'snapshot.c', + 'snapshot-access.c', 'throttle-groups.c', 'throttle.c', 'vhdx-endian.c', @@ -131,8 +133,11 @@ block_ss.add(module_block_h) wrapper_py = find_program('../scripts/block-coroutine-wrapper.py') block_gen_c = custom_target('block-gen.c', output: 'block-gen.c', - input: files('../include/block/block.h', - 'coroutines.h'), + input: files( + '../include/block/block-io.h', + '../include/block/block-global-state.h', + 'coroutines.h' + ), command: [wrapper_py, '@OUTPUT@', '@INPUT@']) block_ss.add(block_gen_c) diff --git a/block/mirror.c b/block/mirror.c index 69b2c1c697..d8ecb9efa2 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -23,6 +23,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" #include "qemu/bitmap.h" +#include "qemu/memalign.h" #define MAX_IN_FLIGHT 16 #define MAX_IO_BYTES (1 << 20) /* 1 Mb */ @@ -1864,6 +1865,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs, bool is_none_mode; BlockDriverState *base; + GLOBAL_STATE_CODE(); + if ((mode == MIRROR_SYNC_MODE_INCREMENTAL) || (mode == MIRROR_SYNC_MODE_BITMAP)) { error_setg(errp, "Sync mode '%s' not supported", @@ -1889,6 +1892,8 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, bool base_read_only; BlockJob *job; + GLOBAL_STATE_CODE(); + base_read_only = bdrv_is_read_only(base); if (base_read_only) { diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index 9f11deec64..2b677c4a2f 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -56,6 +56,8 @@ BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, BlockDriverState *bs; BdrvDirtyBitmap *bitmap; + GLOBAL_STATE_CODE(); + if (!node) { error_setg(errp, "Node cannot be NULL"); return NULL; @@ -155,6 +157,8 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, BdrvDirtyBitmap *bitmap; AioContext *aio_context; + GLOBAL_STATE_CODE(); + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); if (!bitmap || !bs) { return NULL; @@ -253,13 +257,14 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name, } BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, - BlockDirtyBitmapMergeSourceList *bms, + BlockDirtyBitmapOrStrList *bms, HBitmap **backup, Error **errp) { BlockDriverState *bs; BdrvDirtyBitmap *dst, *src, *anon; - BlockDirtyBitmapMergeSourceList *lst; - Error *local_err = NULL; + BlockDirtyBitmapOrStrList *lst; + + GLOBAL_STATE_CODE(); dst = block_dirty_bitmap_lookup(node, target, &bs, errp); if (!dst) { @@ -297,9 +302,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, abort(); } - bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!bdrv_merge_dirty_bitmap(anon, src, NULL, errp)) { dst = NULL; goto out; } @@ -314,7 +317,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, } void qmp_block_dirty_bitmap_merge(const char *node, const char *target, - BlockDirtyBitmapMergeSourceList *bitmaps, + BlockDirtyBitmapOrStrList *bitmaps, Error **errp) { block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); diff --git a/block/nbd.c b/block/nbd.c index 5853d85d60..6085ab1d2c 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -35,7 +35,6 @@ #include "qemu/option.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" -#include "qemu/atomic.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/qmp/qstring.h" @@ -58,7 +57,6 @@ typedef struct { Coroutine *coroutine; uint64_t offset; /* original offset of the request */ bool receiving; /* sleeping in the yield in nbd_receive_replies */ - bool reply_possible; /* reply header not yet received */ } NBDClientRequest; typedef enum NBDClientState { @@ -72,27 +70,39 @@ typedef struct BDRVNBDState { QIOChannel *ioc; /* The current I/O channel */ NBDExportInfo info; - CoMutex send_mutex; - CoQueue free_sema; - - CoMutex receive_mutex; - int in_flight; + /* + * Protects state, free_sema, in_flight, requests[].coroutine, + * reconnect_delay_timer. + */ + QemuMutex requests_lock; NBDClientState state; - + CoQueue free_sema; + int in_flight; + NBDClientRequest requests[MAX_NBD_REQUESTS]; QEMUTimer *reconnect_delay_timer; + + /* Protects sending data on the socket. */ + CoMutex send_mutex; + + /* + * Protects receiving reply headers from the socket, as well as the + * fields reply and requests[].receiving + */ + CoMutex receive_mutex; + NBDReply reply; + QEMUTimer *open_timer; - NBDClientRequest requests[MAX_NBD_REQUESTS]; - NBDReply reply; BlockDriverState *bs; /* Connection parameters */ uint32_t reconnect_delay; uint32_t open_timeout; SocketAddress *saddr; - char *export, *tlscredsid; + char *export; + char *tlscredsid; QCryptoTLSCreds *tlscreds; - const char *hostname; + char *tlshostname; char *x_dirty_bitmap; bool alloc_depth; @@ -121,16 +131,14 @@ static void nbd_clear_bdrvstate(BlockDriverState *bs) s->export = NULL; g_free(s->tlscredsid); s->tlscredsid = NULL; + g_free(s->tlshostname); + s->tlshostname = NULL; g_free(s->x_dirty_bitmap); s->x_dirty_bitmap = NULL; } -static bool nbd_client_connected(BDRVNBDState *s) -{ - return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED; -} - -static bool nbd_recv_coroutine_wake_one(NBDClientRequest *req) +/* Called with s->receive_mutex taken. */ +static bool coroutine_fn nbd_recv_coroutine_wake_one(NBDClientRequest *req) { if (req->receiving) { req->receiving = false; @@ -141,33 +149,39 @@ static bool nbd_recv_coroutine_wake_one(NBDClientRequest *req) return false; } -static void nbd_recv_coroutines_wake(BDRVNBDState *s, bool all) +static void coroutine_fn nbd_recv_coroutines_wake(BDRVNBDState *s) { int i; + QEMU_LOCK_GUARD(&s->receive_mutex); for (i = 0; i < MAX_NBD_REQUESTS; i++) { - if (nbd_recv_coroutine_wake_one(&s->requests[i]) && !all) { + if (nbd_recv_coroutine_wake_one(&s->requests[i])) { return; } } } -static void nbd_channel_error(BDRVNBDState *s, int ret) +/* Called with s->requests_lock held. */ +static void coroutine_fn nbd_channel_error_locked(BDRVNBDState *s, int ret) { - if (nbd_client_connected(s)) { + if (s->state == NBD_CLIENT_CONNECTED) { qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); } if (ret == -EIO) { - if (nbd_client_connected(s)) { + if (s->state == NBD_CLIENT_CONNECTED) { s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT : NBD_CLIENT_CONNECTING_NOWAIT; } } else { s->state = NBD_CLIENT_QUIT; } +} - nbd_recv_coroutines_wake(s, true); +static void coroutine_fn nbd_channel_error(BDRVNBDState *s, int ret) +{ + QEMU_LOCK_GUARD(&s->requests_lock); + nbd_channel_error_locked(s, ret); } static void reconnect_delay_timer_del(BDRVNBDState *s) @@ -182,23 +196,18 @@ static void reconnect_delay_timer_cb(void *opaque) { BDRVNBDState *s = opaque; - if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) { - s->state = NBD_CLIENT_CONNECTING_NOWAIT; - nbd_co_establish_connection_cancel(s->conn); - while (qemu_co_enter_next(&s->free_sema, NULL)) { - /* Resume all queued requests */ - } - } - reconnect_delay_timer_del(s); + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + if (s->state != NBD_CLIENT_CONNECTING_WAIT) { + return; + } + s->state = NBD_CLIENT_CONNECTING_NOWAIT; + } + nbd_co_establish_connection_cancel(s->conn); } static void reconnect_delay_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) { - if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTING_WAIT) { - return; - } - assert(!s->reconnect_delay_timer); s->reconnect_delay_timer = aio_timer_new(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME, @@ -221,7 +230,9 @@ static void nbd_teardown_connection(BlockDriverState *bs) s->ioc = NULL; } - s->state = NBD_CLIENT_QUIT; + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + s->state = NBD_CLIENT_QUIT; + } } static void open_timer_del(BDRVNBDState *s) @@ -250,16 +261,13 @@ static void open_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) timer_mod(s->open_timer, expire_time_ns); } -static bool nbd_client_connecting(BDRVNBDState *s) +static bool nbd_client_will_reconnect(BDRVNBDState *s) { - NBDClientState state = qatomic_load_acquire(&s->state); - return state == NBD_CLIENT_CONNECTING_WAIT || - state == NBD_CLIENT_CONNECTING_NOWAIT; -} - -static bool nbd_client_connecting_wait(BDRVNBDState *s) -{ - return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT; + /* + * Called only after a socket error, so this is not performance sensitive. + */ + QEMU_LOCK_GUARD(&s->requests_lock); + return s->state == NBD_CLIENT_CONNECTING_WAIT; } /* @@ -308,11 +316,11 @@ static int nbd_handle_updated_info(BlockDriverState *bs, Error **errp) } int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, - Error **errp) + bool blocking, Error **errp) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; int ret; - bool blocking = nbd_client_connecting_wait(s); + IO_CODE(); assert(!s->ioc); @@ -346,34 +354,42 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, qio_channel_attach_aio_context(s->ioc, bdrv_get_aio_context(bs)); /* successfully connected */ - s->state = NBD_CLIENT_CONNECTED; - qemu_co_queue_restart_all(&s->free_sema); + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + s->state = NBD_CLIENT_CONNECTED; + } return 0; } -/* called under s->send_mutex */ +/* Called with s->requests_lock held. */ +static bool nbd_client_connecting(BDRVNBDState *s) +{ + return s->state == NBD_CLIENT_CONNECTING_WAIT || + s->state == NBD_CLIENT_CONNECTING_NOWAIT; +} + +/* Called with s->requests_lock taken. */ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) { - assert(nbd_client_connecting(s)); - assert(s->in_flight == 0); - - if (nbd_client_connecting_wait(s) && s->reconnect_delay && - !s->reconnect_delay_timer) - { - /* - * It's first reconnect attempt after switching to - * NBD_CLIENT_CONNECTING_WAIT - */ - reconnect_delay_timer_init(s, - qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + - s->reconnect_delay * NANOSECONDS_PER_SECOND); - } + bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT; /* * Now we are sure that nobody is accessing the channel, and no one will * try until we set the state to CONNECTED. */ + assert(nbd_client_connecting(s)); + assert(s->in_flight == 1); + + if (blocking && !s->reconnect_delay_timer) { + /* + * It's the first reconnect attempt after switching to + * NBD_CLIENT_CONNECTING_WAIT + */ + g_assert(s->reconnect_delay); + reconnect_delay_timer_init(s, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + + s->reconnect_delay * NANOSECONDS_PER_SECOND); + } /* Finalize previous connection if any */ if (s->ioc) { @@ -384,7 +400,9 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) s->ioc = NULL; } - nbd_co_do_establish_connection(s->bs, NULL); + qemu_mutex_unlock(&s->requests_lock); + nbd_co_do_establish_connection(s->bs, blocking, NULL); + qemu_mutex_lock(&s->requests_lock); /* * The reconnect attempt is done (maybe successfully, maybe not), so @@ -406,10 +424,6 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) return 0; } - if (!nbd_client_connected(s)) { - return -EIO; - } - if (s->reply.handle != 0) { /* * Some other request is being handled now. It should already be @@ -424,11 +438,10 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) qemu_coroutine_yield(); /* - * We may be woken for 3 reasons: + * We may be woken for 2 reasons: * 1. From this function, executing in parallel coroutine, when our * handle is received. - * 2. From nbd_channel_error(), when connection is lost. - * 3. From nbd_co_receive_one_chunk(), when previous request is + * 2. From nbd_co_receive_one_chunk(), when previous request is * finished and s->reply.handle set to 0. * Anyway, it's OK to lock the mutex and go to the next iteration. */ @@ -450,44 +463,43 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) nbd_channel_error(s, -EINVAL); return -EINVAL; } + ind2 = HANDLE_TO_INDEX(s, s->reply.handle); + if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { + nbd_channel_error(s, -EINVAL); + return -EINVAL; + } if (s->reply.handle == handle) { /* We are done */ return 0; } - ind2 = HANDLE_TO_INDEX(s, s->reply.handle); - if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].reply_possible) { - nbd_channel_error(s, -EINVAL); - return -EINVAL; - } nbd_recv_coroutine_wake_one(&s->requests[ind2]); } } -static int nbd_co_send_request(BlockDriverState *bs, - NBDRequest *request, - QEMUIOVector *qiov) +static int coroutine_fn nbd_co_send_request(BlockDriverState *bs, + NBDRequest *request, + QEMUIOVector *qiov) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; int rc, i = -1; - qemu_co_mutex_lock(&s->send_mutex); - + qemu_mutex_lock(&s->requests_lock); while (s->in_flight == MAX_NBD_REQUESTS || - (!nbd_client_connected(s) && s->in_flight > 0)) - { - qemu_co_queue_wait(&s->free_sema, &s->send_mutex); - } - - if (nbd_client_connecting(s)) { - nbd_reconnect_attempt(s); - } - - if (!nbd_client_connected(s)) { - rc = -EIO; - goto err; + (s->state != NBD_CLIENT_CONNECTED && s->in_flight > 0)) { + qemu_co_queue_wait(&s->free_sema, &s->requests_lock); } s->in_flight++; + if (s->state != NBD_CLIENT_CONNECTED) { + if (nbd_client_connecting(s)) { + nbd_reconnect_attempt(s); + qemu_co_queue_restart_all(&s->free_sema); + } + if (s->state != NBD_CLIENT_CONNECTED) { + rc = -EIO; + goto err; + } + } for (i = 0; i < MAX_NBD_REQUESTS; i++) { if (s->requests[i].coroutine == NULL) { @@ -495,14 +507,13 @@ static int nbd_co_send_request(BlockDriverState *bs, } } - g_assert(qemu_in_coroutine()); assert(i < MAX_NBD_REQUESTS); - s->requests[i].coroutine = qemu_coroutine_self(); s->requests[i].offset = request->from; s->requests[i].receiving = false; - s->requests[i].reply_possible = true; + qemu_mutex_unlock(&s->requests_lock); + qemu_co_mutex_lock(&s->send_mutex); request->handle = INDEX_TO_HANDLE(s, i); assert(s->ioc); @@ -510,7 +521,7 @@ static int nbd_co_send_request(BlockDriverState *bs, if (qiov) { qio_channel_set_cork(s->ioc, true); rc = nbd_send_request(s->ioc, request); - if (nbd_client_connected(s) && rc >= 0) { + if (rc >= 0) { if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, NULL) < 0) { rc = -EIO; @@ -522,17 +533,19 @@ static int nbd_co_send_request(BlockDriverState *bs, } else { rc = nbd_send_request(s->ioc, request); } + qemu_co_mutex_unlock(&s->send_mutex); -err: if (rc < 0) { - nbd_channel_error(s, rc); + qemu_mutex_lock(&s->requests_lock); +err: + nbd_channel_error_locked(s, rc); if (i != -1) { s->requests[i].coroutine = NULL; - s->in_flight--; - qemu_co_queue_next(&s->free_sema); } + s->in_flight--; + qemu_co_queue_next(&s->free_sema); + qemu_mutex_unlock(&s->requests_lock); } - qemu_co_mutex_unlock(&s->send_mutex); return rc; } @@ -719,9 +732,9 @@ static int nbd_parse_error_payload(NBDStructuredReplyChunk *chunk, return 0; } -static int nbd_co_receive_offset_data_payload(BDRVNBDState *s, - uint64_t orig_offset, - QEMUIOVector *qiov, Error **errp) +static int coroutine_fn +nbd_co_receive_offset_data_payload(BDRVNBDState *s, uint64_t orig_offset, + QEMUIOVector *qiov, Error **errp) { QEMUIOVector sub_qiov; uint64_t offset; @@ -827,8 +840,8 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } *request_ret = 0; - nbd_receive_replies(s, handle); - if (!nbd_client_connected(s)) { + ret = nbd_receive_replies(s, handle); + if (ret < 0) { error_setg(errp, "Connection closed"); return -EIO; } @@ -920,7 +933,7 @@ static coroutine_fn int nbd_co_receive_one_chunk( } s->reply.handle = 0; - nbd_recv_coroutines_wake(s, false); + nbd_recv_coroutines_wake(s); return ret; } @@ -980,11 +993,6 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, NBDReply local_reply; NBDStructuredReplyChunk *chunk; Error *local_err = NULL; - if (!nbd_client_connected(s)) { - error_setg(&local_err, "Connection closed"); - nbd_iter_channel_error(iter, -EIO, &local_err); - goto break_loop; - } if (iter->done) { /* Previous iteration was last. */ @@ -1005,7 +1013,7 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, } /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ - if (nbd_reply_is_simple(reply) || !nbd_client_connected(s)) { + if (nbd_reply_is_simple(reply) || iter->ret < 0) { goto break_loop; } @@ -1027,18 +1035,17 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, return true; break_loop: + qemu_mutex_lock(&s->requests_lock); s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL; - - qemu_co_mutex_lock(&s->send_mutex); s->in_flight--; qemu_co_queue_next(&s->free_sema); - qemu_co_mutex_unlock(&s->send_mutex); + qemu_mutex_unlock(&s->requests_lock); return false; } -static int nbd_co_receive_return_code(BDRVNBDState *s, uint64_t handle, - int *request_ret, Error **errp) +static int coroutine_fn nbd_co_receive_return_code(BDRVNBDState *s, uint64_t handle, + int *request_ret, Error **errp) { NBDReplyChunkIter iter; @@ -1051,9 +1058,9 @@ static int nbd_co_receive_return_code(BDRVNBDState *s, uint64_t handle, return iter.ret; } -static int nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t handle, - uint64_t offset, QEMUIOVector *qiov, - int *request_ret, Error **errp) +static int coroutine_fn nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t handle, + uint64_t offset, QEMUIOVector *qiov, + int *request_ret, Error **errp) { NBDReplyChunkIter iter; NBDReply reply; @@ -1103,10 +1110,10 @@ static int nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t handle, return iter.ret; } -static int nbd_co_receive_blockstatus_reply(BDRVNBDState *s, - uint64_t handle, uint64_t length, - NBDExtent *extent, - int *request_ret, Error **errp) +static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s, + uint64_t handle, uint64_t length, + NBDExtent *extent, + int *request_ret, Error **errp) { NBDReplyChunkIter iter; NBDReply reply; @@ -1163,8 +1170,8 @@ static int nbd_co_receive_blockstatus_reply(BDRVNBDState *s, return iter.ret; } -static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, - QEMUIOVector *write_qiov) +static int coroutine_fn nbd_co_request(BlockDriverState *bs, NBDRequest *request, + QEMUIOVector *write_qiov) { int ret, request_ret; Error *local_err = NULL; @@ -1195,14 +1202,14 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, error_free(local_err); local_err = NULL; } - } while (ret < 0 && nbd_client_connecting_wait(s)); + } while (ret < 0 && nbd_client_will_reconnect(s)); return ret ? ret : request_ret; } -static int nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { int ret, request_ret; Error *local_err = NULL; @@ -1254,14 +1261,14 @@ static int nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, error_free(local_err); local_err = NULL; } - } while (ret < 0 && nbd_client_connecting_wait(s)); + } while (ret < 0 && nbd_client_will_reconnect(s)); return ret ? ret : request_ret; } -static int nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { @@ -1284,8 +1291,8 @@ static int nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, return nbd_co_request(bs, &request, qiov); } -static int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, + int64_t bytes, BdrvRequestFlags flags) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { @@ -1319,7 +1326,7 @@ static int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, return nbd_co_request(bs, &request, NULL); } -static int nbd_client_co_flush(BlockDriverState *bs) +static int coroutine_fn nbd_client_co_flush(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { .type = NBD_CMD_FLUSH }; @@ -1334,8 +1341,8 @@ static int nbd_client_co_flush(BlockDriverState *bs) return nbd_co_request(bs, &request, NULL); } -static int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, - int64_t bytes) +static int coroutine_fn nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, + int64_t bytes) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { @@ -1412,7 +1419,7 @@ static int coroutine_fn nbd_client_co_block_status( error_free(local_err); local_err = NULL; } - } while (ret < 0 && nbd_client_connecting_wait(s)); + } while (ret < 0 && nbd_client_will_reconnect(s)); if (ret < 0 || request_ret < 0) { return ret ? ret : request_ret; @@ -1444,8 +1451,9 @@ static void nbd_yank(void *opaque) BlockDriverState *bs = opaque; BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - qatomic_store_release(&s->state, NBD_CLIENT_QUIT); + QEMU_LOCK_GUARD(&s->requests_lock); qio_channel_shutdown(QIO_CHANNEL(s->ioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + s->state = NBD_CLIENT_QUIT; } static void nbd_client_close(BlockDriverState *bs) @@ -1764,6 +1772,11 @@ static QemuOptsList nbd_runtime_opts = { .type = QEMU_OPT_STRING, .help = "ID of the TLS credentials to use", }, + { + .name = "tls-hostname", + .type = QEMU_OPT_STRING, + .help = "Override hostname for validating TLS x509 certificate", + }, { .name = "x-dirty-bitmap", .type = QEMU_OPT_STRING, @@ -1830,12 +1843,11 @@ static int nbd_process_options(BlockDriverState *bs, QDict *options, goto error; } - /* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */ - if (s->saddr->type != SOCKET_ADDRESS_TYPE_INET) { - error_setg(errp, "TLS only supported over IP sockets"); - goto error; + s->tlshostname = g_strdup(qemu_opt_get(opts, "tls-hostname")); + if (!s->tlshostname && + s->saddr->type == SOCKET_ADDRESS_TYPE_INET) { + s->tlshostname = g_strdup(s->saddr->u.inet.host); } - s->hostname = s->saddr->u.inet.host; } s->x_dirty_bitmap = g_strdup(qemu_opt_get(opts, "x-dirty-bitmap")); @@ -1861,8 +1873,9 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, BDRVNBDState *s = (BDRVNBDState *)bs->opaque; s->bs = bs; - qemu_co_mutex_init(&s->send_mutex); + qemu_mutex_init(&s->requests_lock); qemu_co_queue_init(&s->free_sema); + qemu_co_mutex_init(&s->send_mutex); qemu_co_mutex_init(&s->receive_mutex); if (!yank_register_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name), errp)) { @@ -1875,7 +1888,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, } s->conn = nbd_client_connection_new(s->saddr, true, s->export, - s->x_dirty_bitmap, s->tlscreds); + s->x_dirty_bitmap, s->tlscreds, + s->tlshostname); if (s->open_timeout) { nbd_client_connection_enable_retry(s->conn); @@ -1884,7 +1898,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, } s->state = NBD_CLIENT_CONNECTING_WAIT; - ret = nbd_do_establish_connection(bs, errp); + ret = nbd_do_establish_connection(bs, true, errp); if (ret < 0) { goto fail; } @@ -1906,7 +1920,7 @@ fail: return ret; } -static int nbd_co_flush(BlockDriverState *bs) +static int coroutine_fn nbd_co_flush(BlockDriverState *bs) { return nbd_client_co_flush(bs); } @@ -2036,6 +2050,7 @@ static const char *const nbd_strong_runtime_opts[] = { "port", "export", "tls-creds", + "tls-hostname", "server.", NULL @@ -2047,10 +2062,11 @@ static void nbd_cancel_in_flight(BlockDriverState *bs) reconnect_delay_timer_del(s); + qemu_mutex_lock(&s->requests_lock); if (s->state == NBD_CLIENT_CONNECTING_WAIT) { s->state = NBD_CLIENT_CONNECTING_NOWAIT; - qemu_co_queue_restart_all(&s->free_sema); } + qemu_mutex_unlock(&s->requests_lock); nbd_co_establish_connection_cancel(s->conn); } diff --git a/block/nvme.c b/block/nvme.c index dd20de3865..01fb28aa63 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -21,6 +21,7 @@ #include "qemu/module.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "qemu/memalign.h" #include "qemu/vfio-helpers.h" #include "block/block_int.h" #include "sysemu/replay.h" @@ -168,9 +169,9 @@ static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q, size_t bytes; int r; - bytes = ROUND_UP(nentries * entry_bytes, qemu_real_host_page_size); + bytes = ROUND_UP(nentries * entry_bytes, qemu_real_host_page_size()); q->head = q->tail = 0; - q->queue = qemu_try_memalign(qemu_real_host_page_size, bytes); + q->queue = qemu_try_memalign(qemu_real_host_page_size(), bytes); if (!q->queue) { error_setg(errp, "Cannot allocate queue"); return false; @@ -231,8 +232,8 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s, trace_nvme_create_queue_pair(idx, q, size, aio_context, event_notifier_get_fd(s->irq_notifier)); bytes = QEMU_ALIGN_UP(s->page_size * NVME_NUM_REQS, - qemu_real_host_page_size); - q->prp_list_pages = qemu_try_memalign(qemu_real_host_page_size, bytes); + qemu_real_host_page_size()); + q->prp_list_pages = qemu_try_memalign(qemu_real_host_page_size(), bytes); if (!q->prp_list_pages) { error_setg(errp, "Cannot allocate PRP page list"); goto fail; @@ -532,9 +533,9 @@ static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp) .opcode = NVME_ADM_CMD_IDENTIFY, .cdw10 = cpu_to_le32(0x1), }; - size_t id_size = QEMU_ALIGN_UP(sizeof(*id), qemu_real_host_page_size); + size_t id_size = QEMU_ALIGN_UP(sizeof(*id), qemu_real_host_page_size()); - id = qemu_try_memalign(qemu_real_host_page_size, id_size); + id = qemu_try_memalign(qemu_real_host_page_size(), id_size); if (!id) { error_setg(errp, "Cannot allocate buffer for identify response"); goto out; @@ -1047,7 +1048,7 @@ static coroutine_fn int nvme_cmd_map_qiov(BlockDriverState *bs, NvmeCmd *cmd, bool retry = true; uint64_t iova; size_t len = QEMU_ALIGN_UP(qiov->iov[i].iov_len, - qemu_real_host_page_size); + qemu_real_host_page_size()); try_map: r = qemu_vfio_dma_map(s->vfio, qiov->iov[i].iov_base, @@ -1223,8 +1224,8 @@ static inline bool nvme_qiov_aligned(BlockDriverState *bs, for (i = 0; i < qiov->niov; ++i) { if (!QEMU_PTR_IS_ALIGNED(qiov->iov[i].iov_base, - qemu_real_host_page_size) || - !QEMU_IS_ALIGNED(qiov->iov[i].iov_len, qemu_real_host_page_size)) { + qemu_real_host_page_size()) || + !QEMU_IS_ALIGNED(qiov->iov[i].iov_len, qemu_real_host_page_size())) { trace_nvme_qiov_unaligned(qiov, i, qiov->iov[i].iov_base, qiov->iov[i].iov_len, s->page_size); return false; @@ -1240,7 +1241,7 @@ static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, int r; QEMU_AUTO_VFREE uint8_t *buf = NULL; QEMUIOVector local_qiov; - size_t len = QEMU_ALIGN_UP(bytes, qemu_real_host_page_size); + size_t len = QEMU_ALIGN_UP(bytes, qemu_real_host_page_size()); assert(QEMU_IS_ALIGNED(offset, s->page_size)); assert(QEMU_IS_ALIGNED(bytes, s->page_size)); assert(bytes <= s->max_transfer); @@ -1250,7 +1251,7 @@ static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, } s->stats.unaligned_accesses++; trace_nvme_prw_buffered(s, offset, bytes, qiov->niov, is_write); - buf = qemu_try_memalign(qemu_real_host_page_size, len); + buf = qemu_try_memalign(qemu_real_host_page_size(), len); if (!buf) { return -ENOMEM; diff --git a/block/parallels-ext.c b/block/parallels-ext.c index e0dd0975c6..5122f67ac2 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -29,6 +29,7 @@ #include "parallels.h" #include "crypto/hash.h" #include "qemu/uuid.h" +#include "qemu/memalign.h" #define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL @@ -260,7 +261,7 @@ static int parallels_parse_format_extension(BlockDriverState *bs, break; default: - error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic); + error_setg(errp, "Unknown feature: 0x%" PRIx64, fh.magic); goto fail; } diff --git a/block/parallels.c b/block/parallels.c index 6ebad2a2bb..8879b7027a 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -41,6 +41,7 @@ #include "qapi/qapi-visit-block-core.h" #include "qemu/bswap.h" #include "qemu/bitmap.h" +#include "qemu/memalign.h" #include "migration/blocker.h" #include "parallels.h" @@ -869,11 +870,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, } } - s->bat_dirty_block = 4 * qemu_real_host_page_size; + s->bat_dirty_block = 4 * qemu_real_host_page_size(); s->bat_dirty_bmap = bitmap_new(DIV_ROUND_UP(s->header_size, s->bat_dirty_block)); - /* Disable migration until bdrv_invalidate_cache method is added */ + /* Disable migration until bdrv_activate method is added */ error_setg(&s->migration_blocker, "The Parallels format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); diff --git a/block/preallocate.c b/block/preallocate.c index 1d4233f730..e15cb8c74a 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -276,6 +276,10 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, int64_t end = offset + bytes; int64_t prealloc_start, prealloc_end; int ret; + uint32_t file_align = bs->file->bs->bl.request_alignment; + uint32_t prealloc_align = MAX(s->opts.prealloc_align, file_align); + + assert(QEMU_IS_ALIGNED(prealloc_align, file_align)); if (!has_prealloc_perms(bs)) { /* We don't have state neither should try to recover it */ @@ -320,9 +324,14 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, /* Now we want new preallocation, as request writes beyond s->file_end. */ - prealloc_start = want_merge_zero ? MIN(offset, s->file_end) : s->file_end; - prealloc_end = QEMU_ALIGN_UP(end + s->opts.prealloc_size, - s->opts.prealloc_align); + prealloc_start = QEMU_ALIGN_UP( + want_merge_zero ? MIN(offset, s->file_end) : s->file_end, + file_align); + prealloc_end = QEMU_ALIGN_UP( + MAX(prealloc_start, end) + s->opts.prealloc_size, + prealloc_align); + + want_merge_zero = want_merge_zero && (prealloc_start <= offset); ret = bdrv_co_pwrite_zeroes( bs->file, prealloc_start, prealloc_end - prealloc_start, diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c index 8498402ad4..680c7ee342 100644 --- a/block/qapi-sysemu.c +++ b/block/qapi-sysemu.c @@ -318,6 +318,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, bool has_id, const char *id, const char *filename, bool has_format, const char *format, + bool has_force, bool force, bool has_read_only, BlockdevChangeReadOnlyMode read_only, Error **errp) @@ -380,7 +381,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, rc = do_open_tray(has_device ? device : NULL, has_id ? id : NULL, - false, &err); + force, &err); if (rc && rc != -ENOSYS) { error_propagate(errp, err); goto fail; diff --git a/block/qcow.c b/block/qcow.c index c39940f33e..4fba1b9e36 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -32,6 +32,7 @@ #include "qemu/option.h" #include "qemu/bswap.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #include #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 7444b9c4ab..539f9ca2d5 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/memalign.h" #include "qcow2.h" #include "trace.h" @@ -74,7 +75,7 @@ static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables) /* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ #ifdef CONFIG_LINUX void *t = qcow2_cache_get_table_addr(c, i); - int align = qemu_real_host_page_size; + int align = qemu_real_host_page_size(); size_t mem_size = (size_t) c->table_size * num_tables; size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 21884a1ab9..20a16ba6ee 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "qcow2.h" #include "qemu/bswap.h" +#include "qemu/memalign.h" #include "trace.h" int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 4614572252..ed0ecfaa89 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -28,13 +28,16 @@ #include "qemu/range.h" #include "qemu/bswap.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #include "trace.h" static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, uint64_t max); -static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, uint64_t addend, - bool decrease, enum qcow2_discard_type type); + +G_GNUC_WARN_UNUSED_RESULT +static int update_refcount(BlockDriverState *bs, + int64_t offset, int64_t length, uint64_t addend, + bool decrease, enum qcow2_discard_type type); static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index); static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index); @@ -802,12 +805,12 @@ found: /* XXX: cache several refcount block clusters ? */ /* @addend is the absolute value of the addend; if @decrease is set, @addend * will be subtracted from the current refcount, otherwise it will be added */ -static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, - int64_t length, - uint64_t addend, - bool decrease, - enum qcow2_discard_type type) +static int update_refcount(BlockDriverState *bs, + int64_t offset, + int64_t length, + uint64_t addend, + bool decrease, + enum qcow2_discard_type type) { BDRVQcow2State *s = bs->opaque; int64_t start, last, cluster_offset; @@ -2434,10 +2437,165 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, return cluster << s->cluster_bits; } +/* + * Helper function for rebuild_refcount_structure(). + * + * Scan the range of clusters [first_cluster, end_cluster) for allocated + * clusters and write all corresponding refblocks to disk. The refblock + * and allocation data is taken from the in-memory refcount table + * *refcount_table[] (of size *nb_clusters), which is basically one big + * (unlimited size) refblock for the whole image. + * + * For these refblocks, clusters are allocated using said in-memory + * refcount table. Care is taken that these allocations are reflected + * in the refblocks written to disk. + * + * The refblocks' offsets are written into a reftable, which is + * *on_disk_reftable_ptr[] (of size *on_disk_reftable_entries_ptr). If + * that reftable is of insufficient size, it will be resized to fit. + * This reftable is not written to disk. + * + * (If *on_disk_reftable_ptr is not NULL, the entries within are assumed + * to point to existing valid refblocks that do not need to be allocated + * again.) + * + * Return whether the on-disk reftable array was resized (true/false), + * or -errno on error. + */ +static int rebuild_refcounts_write_refblocks( + BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters, + int64_t first_cluster, int64_t end_cluster, + uint64_t **on_disk_reftable_ptr, uint32_t *on_disk_reftable_entries_ptr, + Error **errp + ) +{ + BDRVQcow2State *s = bs->opaque; + int64_t cluster; + int64_t refblock_offset, refblock_start, refblock_index; + int64_t first_free_cluster = 0; + uint64_t *on_disk_reftable = *on_disk_reftable_ptr; + uint32_t on_disk_reftable_entries = *on_disk_reftable_entries_ptr; + void *on_disk_refblock; + bool reftable_grown = false; + int ret; + + for (cluster = first_cluster; cluster < end_cluster; cluster++) { + /* Check all clusters to find refblocks that contain non-zero entries */ + if (!s->get_refcount(*refcount_table, cluster)) { + continue; + } + + /* + * This cluster is allocated, so we need to create a refblock + * for it. The data we will write to disk is just the + * respective slice from *refcount_table, so it will contain + * accurate refcounts for all clusters belonging to this + * refblock. After we have written it, we will therefore skip + * all remaining clusters in this refblock. + */ + + refblock_index = cluster >> s->refcount_block_bits; + refblock_start = refblock_index << s->refcount_block_bits; + + if (on_disk_reftable_entries > refblock_index && + on_disk_reftable[refblock_index]) + { + /* + * We can get here after a `goto write_refblocks`: We have a + * reftable from a previous run, and the refblock is already + * allocated. No need to allocate it again. + */ + refblock_offset = on_disk_reftable[refblock_index]; + } else { + int64_t refblock_cluster_index; + + /* Don't allocate a cluster in a refblock already written to disk */ + if (first_free_cluster < refblock_start) { + first_free_cluster = refblock_start; + } + refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table, + nb_clusters, + &first_free_cluster); + if (refblock_offset < 0) { + error_setg_errno(errp, -refblock_offset, + "ERROR allocating refblock"); + return refblock_offset; + } + + refblock_cluster_index = refblock_offset / s->cluster_size; + if (refblock_cluster_index >= end_cluster) { + /* + * We must write the refblock that holds this refblock's + * refcount + */ + end_cluster = refblock_cluster_index + 1; + } + + if (on_disk_reftable_entries <= refblock_index) { + on_disk_reftable_entries = + ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE, + s->cluster_size) / REFTABLE_ENTRY_SIZE; + on_disk_reftable = + g_try_realloc(on_disk_reftable, + on_disk_reftable_entries * + REFTABLE_ENTRY_SIZE); + if (!on_disk_reftable) { + error_setg(errp, "ERROR allocating reftable memory"); + return -ENOMEM; + } + + memset(on_disk_reftable + *on_disk_reftable_entries_ptr, 0, + (on_disk_reftable_entries - + *on_disk_reftable_entries_ptr) * + REFTABLE_ENTRY_SIZE); + + *on_disk_reftable_ptr = on_disk_reftable; + *on_disk_reftable_entries_ptr = on_disk_reftable_entries; + + reftable_grown = true; + } else { + assert(on_disk_reftable); + } + on_disk_reftable[refblock_index] = refblock_offset; + } + + /* Refblock is allocated, write it to disk */ + + ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset, + s->cluster_size, false); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR writing refblock"); + return ret; + } + + /* + * The refblock is simply a slice of *refcount_table. + * Note that the size of *refcount_table is always aligned to + * whole clusters, so the write operation will not result in + * out-of-bounds accesses. + */ + on_disk_refblock = (void *)((char *) *refcount_table + + refblock_index * s->cluster_size); + + ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock, + s->cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR writing refblock"); + return ret; + } + + /* This refblock is done, skip to its end */ + cluster = refblock_start + s->refcount_block_size - 1; + } + + return reftable_grown; +} + /* * Creates a new refcount structure based solely on the in-memory information - * given through *refcount_table. All necessary allocations will be reflected - * in that array. + * given through *refcount_table (this in-memory information is basically just + * the concatenation of all refblocks). All necessary allocations will be + * reflected in that array. * * On success, the old refcount structure is leaked (it will be covered by the * new refcount structure). @@ -2445,15 +2603,18 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, static int rebuild_refcount_structure(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, - int64_t *nb_clusters) + int64_t *nb_clusters, + Error **errp) { BDRVQcow2State *s = bs->opaque; - int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0; - int64_t refblock_offset, refblock_start, refblock_index; - uint32_t reftable_size = 0; + int64_t reftable_offset = -1; + int64_t reftable_length = 0; + int64_t reftable_clusters; + int64_t refblock_index; + uint32_t on_disk_reftable_entries = 0; uint64_t *on_disk_reftable = NULL; - void *on_disk_refblock; int ret = 0; + int reftable_size_changed = 0; struct { uint64_t reftable_offset; uint32_t reftable_clusters; @@ -2461,162 +2622,145 @@ static int rebuild_refcount_structure(BlockDriverState *bs, qcow2_cache_empty(bs, s->refcount_block_cache); -write_refblocks: - for (; cluster < *nb_clusters; cluster++) { - if (!s->get_refcount(*refcount_table, cluster)) { - continue; - } + /* + * For each refblock containing entries, we try to allocate a + * cluster (in the in-memory refcount table) and write its offset + * into on_disk_reftable[]. We then write the whole refblock to + * disk (as a slice of the in-memory refcount table). + * This is done by rebuild_refcounts_write_refblocks(). + * + * Once we have scanned all clusters, we try to find space for the + * reftable. This will dirty the in-memory refcount table (i.e. + * make it differ from the refblocks we have already written), so we + * need to run rebuild_refcounts_write_refblocks() again for the + * range of clusters where the reftable has been allocated. + * + * This second run might make the reftable grow again, in which case + * we will need to allocate another space for it, which is why we + * repeat all this until the reftable stops growing. + * + * (This loop will terminate, because with every cluster the + * reftable grows, it can accomodate a multitude of more refcounts, + * so that at some point this must be able to cover the reftable + * and all refblocks describing it.) + * + * We then convert the reftable to big-endian and write it to disk. + * + * Note that we never free any reftable allocations. Doing so would + * needlessly complicate the algorithm: The eventual second check + * run we do will clean up all leaks we have caused. + */ - refblock_index = cluster >> s->refcount_block_bits; - refblock_start = refblock_index << s->refcount_block_bits; - - /* Don't allocate a cluster in a refblock already written to disk */ - if (first_free_cluster < refblock_start) { - first_free_cluster = refblock_start; - } - refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table, - nb_clusters, &first_free_cluster); - if (refblock_offset < 0) { - fprintf(stderr, "ERROR allocating refblock: %s\n", - strerror(-refblock_offset)); - res->check_errors++; - ret = refblock_offset; - goto fail; - } - - if (reftable_size <= refblock_index) { - uint32_t old_reftable_size = reftable_size; - uint64_t *new_on_disk_reftable; - - reftable_size = ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE, - s->cluster_size) / REFTABLE_ENTRY_SIZE; - new_on_disk_reftable = g_try_realloc(on_disk_reftable, - reftable_size * - REFTABLE_ENTRY_SIZE); - if (!new_on_disk_reftable) { - res->check_errors++; - ret = -ENOMEM; - goto fail; - } - on_disk_reftable = new_on_disk_reftable; - - memset(on_disk_reftable + old_reftable_size, 0, - (reftable_size - old_reftable_size) * REFTABLE_ENTRY_SIZE); - - /* The offset we have for the reftable is now no longer valid; - * this will leak that range, but we can easily fix that by running - * a leak-fixing check after this rebuild operation */ - reftable_offset = -1; - } else { - assert(on_disk_reftable); - } - on_disk_reftable[refblock_index] = refblock_offset; - - /* If this is apparently the last refblock (for now), try to squeeze the - * reftable in */ - if (refblock_index == (*nb_clusters - 1) >> s->refcount_block_bits && - reftable_offset < 0) - { - uint64_t reftable_clusters = size_to_clusters(s, reftable_size * - REFTABLE_ENTRY_SIZE); - reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, - refcount_table, nb_clusters, - &first_free_cluster); - if (reftable_offset < 0) { - fprintf(stderr, "ERROR allocating reftable: %s\n", - strerror(-reftable_offset)); - res->check_errors++; - ret = reftable_offset; - goto fail; - } - } - - ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset, - s->cluster_size, false); - if (ret < 0) { - fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); - goto fail; - } - - /* The size of *refcount_table is always cluster-aligned, therefore the - * write operation will not overflow */ - on_disk_refblock = (void *)((char *) *refcount_table + - refblock_index * s->cluster_size); - - ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock, - s->cluster_size); - if (ret < 0) { - fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); - goto fail; - } - - /* Go to the end of this refblock */ - cluster = refblock_start + s->refcount_block_size - 1; + reftable_size_changed = + rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters, + 0, *nb_clusters, + &on_disk_reftable, + &on_disk_reftable_entries, errp); + if (reftable_size_changed < 0) { + res->check_errors++; + ret = reftable_size_changed; + goto fail; } - if (reftable_offset < 0) { - uint64_t post_refblock_start, reftable_clusters; + /* + * There was no reftable before, so rebuild_refcounts_write_refblocks() + * must have increased its size (from 0 to something). + */ + assert(reftable_size_changed); + + do { + int64_t reftable_start_cluster, reftable_end_cluster; + int64_t first_free_cluster = 0; + + reftable_length = on_disk_reftable_entries * REFTABLE_ENTRY_SIZE; + reftable_clusters = size_to_clusters(s, reftable_length); - post_refblock_start = ROUND_UP(*nb_clusters, s->refcount_block_size); - reftable_clusters = - size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE); - /* Not pretty but simple */ - if (first_free_cluster < post_refblock_start) { - first_free_cluster = post_refblock_start; - } reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, refcount_table, nb_clusters, &first_free_cluster); if (reftable_offset < 0) { - fprintf(stderr, "ERROR allocating reftable: %s\n", - strerror(-reftable_offset)); + error_setg_errno(errp, -reftable_offset, + "ERROR allocating reftable"); res->check_errors++; ret = reftable_offset; goto fail; } - goto write_refblocks; - } + /* + * We need to update the affected refblocks, so re-run the + * write_refblocks loop for the reftable's range of clusters. + */ + assert(offset_into_cluster(s, reftable_offset) == 0); + reftable_start_cluster = reftable_offset / s->cluster_size; + reftable_end_cluster = reftable_start_cluster + reftable_clusters; + reftable_size_changed = + rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters, + reftable_start_cluster, + reftable_end_cluster, + &on_disk_reftable, + &on_disk_reftable_entries, errp); + if (reftable_size_changed < 0) { + res->check_errors++; + ret = reftable_size_changed; + goto fail; + } - for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) { + /* + * If the reftable size has changed, we will need to find a new + * allocation, repeating the loop. + */ + } while (reftable_size_changed); + + /* The above loop must have run at least once */ + assert(reftable_offset >= 0); + + /* + * All allocations are done, all refblocks are written, convert the + * reftable to big-endian and write it to disk. + */ + + for (refblock_index = 0; refblock_index < on_disk_reftable_entries; + refblock_index++) + { cpu_to_be64s(&on_disk_reftable[refblock_index]); } - ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, - reftable_size * REFTABLE_ENTRY_SIZE, + ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, reftable_length, false); if (ret < 0) { - fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); + error_setg_errno(errp, -ret, "ERROR writing reftable"); goto fail; } - assert(reftable_size < INT_MAX / REFTABLE_ENTRY_SIZE); + assert(reftable_length < INT_MAX); ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, - reftable_size * REFTABLE_ENTRY_SIZE); + reftable_length); if (ret < 0) { - fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); + error_setg_errno(errp, -ret, "ERROR writing reftable"); goto fail; } /* Enter new reftable into the image header */ reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset); reftable_offset_and_clusters.reftable_clusters = - cpu_to_be32(size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE)); + cpu_to_be32(reftable_clusters); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), &reftable_offset_and_clusters, sizeof(reftable_offset_and_clusters)); if (ret < 0) { - fprintf(stderr, "ERROR setting reftable: %s\n", strerror(-ret)); + error_setg_errno(errp, -ret, "ERROR setting reftable"); goto fail; } - for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) { + for (refblock_index = 0; refblock_index < on_disk_reftable_entries; + refblock_index++) + { be64_to_cpus(&on_disk_reftable[refblock_index]); } s->refcount_table = on_disk_reftable; s->refcount_table_offset = reftable_offset; - s->refcount_table_size = reftable_size; + s->refcount_table_size = on_disk_reftable_entries; update_max_refcount_table_index(s); return 0; @@ -2673,11 +2817,13 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, if (rebuild && (fix & BDRV_FIX_ERRORS)) { BdrvCheckResult old_res = *res; int fresh_leaks = 0; + Error *local_err = NULL; fprintf(stderr, "Rebuilding refcount structure\n"); ret = rebuild_refcount_structure(bs, res, &refcount_table, - &nb_clusters); + &nb_clusters, &local_err); if (ret < 0) { + error_report_err(local_err); goto fail; } diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 71ddb08c21..075269a023 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -29,6 +29,7 @@ #include "qemu/bswap.h" #include "qemu/error-report.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" static void qcow2_free_single_snapshot(BlockDriverState *bs, int i) { diff --git a/block/qcow2.c b/block/qcow2.c index c8115e1cba..b5c47931ef 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -38,6 +38,7 @@ #include "qemu/option_int.h" #include "qemu/cutils.h" #include "qemu/bswap.h" +#include "qemu/memalign.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "crypto.h" diff --git a/block/qcow2.h b/block/qcow2.h index fd48a89d45..ba436a8d0d 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -838,7 +838,7 @@ int qcow2_update_header(BlockDriverState *bs); void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int64_t size, const char *message_format, ...) - GCC_FMT_ATTR(5, 6); + G_GNUC_PRINTF(5, 6); int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, uint64_t entries, size_t entry_len, diff --git a/block/qed-l2-cache.c b/block/qed-l2-cache.c index b548362398..caf2c024c2 100644 --- a/block/qed-l2-cache.c +++ b/block/qed-l2-cache.c @@ -51,6 +51,7 @@ */ #include "qemu/osdep.h" +#include "qemu/memalign.h" #include "trace.h" #include "qed.h" diff --git a/block/qed-table.c b/block/qed-table.c index 405d446cbe..1cc844b1a5 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -17,6 +17,7 @@ #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ #include "qed.h" #include "qemu/bswap.h" +#include "qemu/memalign.h" /* Called with table_lock held. */ static int coroutine_fn qed_read_table(BDRVQEDState *s, uint64_t offset, diff --git a/block/qed.c b/block/qed.c index 558d3646c4..f34d9a3ac1 100644 --- a/block/qed.c +++ b/block/qed.c @@ -20,6 +20,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/memalign.h" #include "trace.h" #include "qed.h" #include "sysemu/block-backend.h" diff --git a/block/quorum.c b/block/quorum.c index c28dda7baa..f33f30d36b 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -17,6 +17,7 @@ #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/memalign.h" #include "block/block_int.h" #include "block/coroutines.h" #include "block/qdict.h" diff --git a/block/raw-format.c b/block/raw-format.c index bda757fd19..69fd650eaf 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -31,6 +31,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/memalign.h" typedef struct BDRVRawState { uint64_t offset; diff --git a/block/rbd.c b/block/rbd.c index 8f183eba2a..6caf35cbba 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1107,6 +1107,20 @@ static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs, assert(!qiov || qiov->size == bytes); + if (cmd == RBD_AIO_WRITE || cmd == RBD_AIO_WRITE_ZEROES) { + /* + * RBD APIs don't allow us to write more than actual size, so in order + * to support growing images, we resize the image before write + * operations that exceed the current size. + */ + if (offset + bytes > s->image_size) { + int r = qemu_rbd_resize(bs, offset + bytes); + if (r < 0) { + return r; + } + } + } + r = rbd_aio_create_completion(&task, (rbd_callback_t) qemu_rbd_completion_cb, &c); if (r < 0) { @@ -1182,18 +1196,6 @@ coroutine_fn qemu_rbd_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - BDRVRBDState *s = bs->opaque; - /* - * RBD APIs don't allow us to write more than actual size, so in order - * to support growing images, we resize the image before write - * operations that exceed the current size. - */ - if (offset + bytes > s->image_size) { - int r = qemu_rbd_resize(bs, offset + bytes); - if (r < 0) { - return r; - } - } return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_WRITE); } diff --git a/block/reqlist.c b/block/reqlist.c new file mode 100644 index 0000000000..08cb57cfa4 --- /dev/null +++ b/block/reqlist.c @@ -0,0 +1,85 @@ +/* + * reqlist API + * + * Copyright (C) 2013 Proxmox Server Solutions + * Copyright (c) 2021 Virtuozzo International GmbH. + * + * Authors: + * Dietmar Maurer (dietmar@proxmox.com) + * Vladimir Sementsov-Ogievskiy + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/range.h" + +#include "block/reqlist.h" + +void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset, + int64_t bytes) +{ + assert(!reqlist_find_conflict(reqs, offset, bytes)); + + *req = (BlockReq) { + .offset = offset, + .bytes = bytes, + }; + qemu_co_queue_init(&req->wait_queue); + QLIST_INSERT_HEAD(reqs, req, list); +} + +BlockReq *reqlist_find_conflict(BlockReqList *reqs, int64_t offset, + int64_t bytes) +{ + BlockReq *r; + + QLIST_FOREACH(r, reqs, list) { + if (ranges_overlap(offset, bytes, r->offset, r->bytes)) { + return r; + } + } + + return NULL; +} + +bool coroutine_fn reqlist_wait_one(BlockReqList *reqs, int64_t offset, + int64_t bytes, CoMutex *lock) +{ + BlockReq *r = reqlist_find_conflict(reqs, offset, bytes); + + if (!r) { + return false; + } + + qemu_co_queue_wait(&r->wait_queue, lock); + + return true; +} + +void coroutine_fn reqlist_wait_all(BlockReqList *reqs, int64_t offset, + int64_t bytes, CoMutex *lock) +{ + while (reqlist_wait_one(reqs, offset, bytes, lock)) { + /* continue */ + } +} + +void coroutine_fn reqlist_shrink_req(BlockReq *req, int64_t new_bytes) +{ + if (new_bytes == req->bytes) { + return; + } + + assert(new_bytes > 0 && new_bytes < req->bytes); + + req->bytes = new_bytes; + qemu_co_queue_restart_all(&req->wait_queue); +} + +void coroutine_fn reqlist_remove_req(BlockReq *req) +{ + QLIST_REMOVE(req, list); + qemu_co_queue_restart_all(&req->wait_queue); +} diff --git a/block/snapshot-access.c b/block/snapshot-access.c new file mode 100644 index 0000000000..77b87c1946 --- /dev/null +++ b/block/snapshot-access.c @@ -0,0 +1,132 @@ +/* + * snapshot_access block driver + * + * Copyright (c) 2022 Virtuozzo International GmbH. + * + * Author: + * Sementsov-Ogievskiy Vladimir + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" + +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "block/block_int.h" + +static coroutine_fn int +snapshot_access_co_preadv_part(BlockDriverState *bs, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) +{ + if (flags) { + return -ENOTSUP; + } + + return bdrv_co_preadv_snapshot(bs->file, offset, bytes, qiov, qiov_offset); +} + +static int coroutine_fn +snapshot_access_co_block_status(BlockDriverState *bs, + bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) +{ + return bdrv_co_snapshot_block_status(bs->file->bs, want_zero, offset, + bytes, pnum, map, file); +} + +static int coroutine_fn snapshot_access_co_pdiscard(BlockDriverState *bs, + int64_t offset, int64_t bytes) +{ + return bdrv_co_pdiscard_snapshot(bs->file->bs, offset, bytes); +} + +static int coroutine_fn +snapshot_access_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int64_t bytes, + BdrvRequestFlags flags) +{ + return -ENOTSUP; +} + +static coroutine_fn int +snapshot_access_co_pwritev_part(BlockDriverState *bs, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) +{ + return -ENOTSUP; +} + + +static void snapshot_access_refresh_filename(BlockDriverState *bs) +{ + pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), + bs->file->bs->filename); +} + +static int snapshot_access_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, + false, errp); + if (!bs->file) { + return -EINVAL; + } + + bs->total_sectors = bs->file->bs->total_sectors; + + return 0; +} + +static void snapshot_access_child_perm(BlockDriverState *bs, BdrvChild *c, + BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + /* + * Currently, we don't need any permissions. If bs->file provides + * snapshot-access API, we can use it. + */ + *nperm = 0; + *nshared = BLK_PERM_ALL; +} + +BlockDriver bdrv_snapshot_access_drv = { + .format_name = "snapshot-access", + + .bdrv_open = snapshot_access_open, + + .bdrv_co_preadv_part = snapshot_access_co_preadv_part, + .bdrv_co_pwritev_part = snapshot_access_co_pwritev_part, + .bdrv_co_pwrite_zeroes = snapshot_access_co_pwrite_zeroes, + .bdrv_co_pdiscard = snapshot_access_co_pdiscard, + .bdrv_co_block_status = snapshot_access_co_block_status, + + .bdrv_refresh_filename = snapshot_access_refresh_filename, + + .bdrv_child_perm = snapshot_access_child_perm, +}; + +static void snapshot_access_init(void) +{ + bdrv_register(&bdrv_snapshot_access_drv); +} + +block_init(snapshot_access_init); diff --git a/block/snapshot.c b/block/snapshot.c index ccacda8bd5..d6f53c3065 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -57,6 +57,8 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, QEMUSnapshotInfo *sn_tab, *sn; int nb_sns, i, ret; + GLOBAL_STATE_CODE(); + ret = -ENOENT; nb_sns = bdrv_snapshot_list(bs, &sn_tab); if (nb_sns < 0) { @@ -105,6 +107,7 @@ bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, bool ret = false; assert(id || name); + GLOBAL_STATE_CODE(); nb_sns = bdrv_snapshot_list(bs, &sn_tab); if (nb_sns < 0) { @@ -200,6 +203,7 @@ static BlockDriverState *bdrv_snapshot_fallback(BlockDriverState *bs) int bdrv_can_snapshot(BlockDriverState *bs) { BlockDriver *drv = bs->drv; + GLOBAL_STATE_CODE(); if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { return 0; } @@ -220,6 +224,9 @@ int bdrv_snapshot_create(BlockDriverState *bs, { BlockDriver *drv = bs->drv; BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); + + GLOBAL_STATE_CODE(); + if (!drv) { return -ENOMEDIUM; } @@ -240,6 +247,8 @@ int bdrv_snapshot_goto(BlockDriverState *bs, BdrvChild **fallback_ptr; int ret, open_ret; + GLOBAL_STATE_CODE(); + if (!drv) { error_setg(errp, "Block driver is closed"); return -ENOMEDIUM; @@ -348,6 +357,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); int ret; + GLOBAL_STATE_CODE(); + if (!drv) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; @@ -380,6 +391,8 @@ int bdrv_snapshot_list(BlockDriverState *bs, { BlockDriver *drv = bs->drv; BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); + + GLOBAL_STATE_CODE(); if (!drv) { return -ENOMEDIUM; } @@ -419,6 +432,8 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs, { BlockDriver *drv = bs->drv; + GLOBAL_STATE_CODE(); + if (!drv) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; @@ -447,6 +462,8 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, int ret; Error *local_err = NULL; + GLOBAL_STATE_CODE(); + ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err); if (ret == -ENOENT || ret == -EINVAL) { error_free(local_err); @@ -515,6 +532,8 @@ bool bdrv_all_can_snapshot(bool has_devices, strList *devices, g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + GLOBAL_STATE_CODE(); + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return false; } @@ -549,6 +568,8 @@ int bdrv_all_delete_snapshot(const char *name, g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + GLOBAL_STATE_CODE(); + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return -1; } @@ -588,6 +609,8 @@ int bdrv_all_goto_snapshot(const char *name, g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + GLOBAL_STATE_CODE(); + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return -1; } @@ -622,6 +645,8 @@ int bdrv_all_has_snapshot(const char *name, g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + GLOBAL_STATE_CODE(); + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return -1; } @@ -663,6 +688,7 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, { g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + GLOBAL_STATE_CODE(); if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return -1; @@ -703,6 +729,8 @@ BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + GLOBAL_STATE_CODE(); + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return NULL; } diff --git a/block/ssh.c b/block/ssh.c index a64db146db..a2dc646536 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -108,7 +108,7 @@ static void ssh_state_free(BDRVSSHState *s) } } -static void GCC_FMT_ATTR(3, 4) +static void G_GNUC_PRINTF(3, 4) session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...) { va_list args; @@ -133,7 +133,7 @@ session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...) g_free(msg); } -static void GCC_FMT_ATTR(3, 4) +static void G_GNUC_PRINTF(3, 4) sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...) { va_list args; diff --git a/block/stream.c b/block/stream.c index 7c6b173ddd..694709bd25 100644 --- a/block/stream.c +++ b/block/stream.c @@ -64,7 +64,13 @@ static int stream_prepare(Job *job) bdrv_cor_filter_drop(s->cor_filter_bs); s->cor_filter_bs = NULL; + bdrv_subtree_drained_begin(s->above_base); + base = bdrv_filter_or_cow_bs(s->above_base); + if (base) { + bdrv_ref(base); + } + unfiltered_base = bdrv_skip_filters(base); if (bdrv_cow_child(unfiltered_bs)) { @@ -75,14 +81,21 @@ static int stream_prepare(Job *job) base_fmt = unfiltered_base->drv->format_name; } } + bdrv_set_backing_hd(unfiltered_bs, base, &local_err); ret = bdrv_change_backing_file(unfiltered_bs, base_id, base_fmt, false); if (local_err) { error_report_err(local_err); - return -EPERM; + ret = -EPERM; + goto out; } } +out: + if (base) { + bdrv_unref(base); + } + bdrv_subtree_drained_end(s->above_base); return ret; } @@ -220,6 +233,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, QDict *opts; int ret; + GLOBAL_STATE_CODE(); + assert(!(base && bottom)); assert(!(backing_file_str && bottom)); diff --git a/block/vdi.c b/block/vdi.c index bdc58d726e..cca3a3a356 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -64,6 +64,7 @@ #include "qemu/coroutine.h" #include "qemu/cutils.h" #include "qemu/uuid.h" +#include "qemu/memalign.h" /* Code configuration options. */ diff --git a/block/vhdx-log.c b/block/vhdx-log.c index 7672161d95..ff0d4e0da0 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -23,6 +23,7 @@ #include "block/block_int.h" #include "qemu/error-report.h" #include "qemu/bswap.h" +#include "qemu/memalign.h" #include "vhdx.h" diff --git a/block/vhdx.c b/block/vhdx.c index 356ec4c455..410c6f9610 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -25,6 +25,7 @@ #include "qemu/crc32c.h" #include "qemu/bswap.h" #include "qemu/error-report.h" +#include "qemu/memalign.h" #include "vhdx.h" #include "migration/blocker.h" #include "qemu/uuid.h" diff --git a/block/vmdk.c b/block/vmdk.c index 0dfab6e941..37c0946066 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -33,6 +33,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" +#include "qemu/memalign.h" #include "migration/blocker.h" #include "qemu/cutils.h" #include diff --git a/block/vpc.c b/block/vpc.c index 297a26262a..4d8f16e199 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -33,6 +33,7 @@ #include "migration/blocker.h" #include "qemu/bswap.h" #include "qemu/uuid.h" +#include "qemu/memalign.h" #include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" diff --git a/block/win32-aio.c b/block/win32-aio.c index c57e10c997..aadc7b1bc3 100644 --- a/block/win32-aio.c +++ b/block/win32-aio.c @@ -29,6 +29,7 @@ #include "block/raw-aio.h" #include "qemu/event_notifier.h" #include "qemu/iov.h" +#include "qemu/memalign.h" #include #include diff --git a/blockdev-nbd.c b/blockdev-nbd.c index bdfa7ed3a5..7f6531cba0 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -148,12 +148,6 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, if (!nbd_server->tlscreds) { goto error; } - - /* TODO SOCKET_ADDRESS_TYPE_FD where fd has AF_INET or AF_INET6 */ - if (addr->type != SOCKET_ADDRESS_TYPE_INET) { - error_setg(errp, "TLS is only supported with IPv4/IPv6"); - goto error; - } } nbd_server->tlsauthz = g_strdup(tls_authz); @@ -217,8 +211,14 @@ void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp) QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd, qapi_NbdServerAddOptions_base(arg)); if (arg->has_bitmap) { + BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1); + + *el = (BlockDirtyBitmapOrStr) { + .type = QTYPE_QSTRING, + .u.local = g_strdup(arg->bitmap), + }; export_opts->u.nbd.has_bitmaps = true; - QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, g_strdup(arg->bitmap)); + QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, el); } /* diff --git a/blockdev.c b/blockdev.c index 42e098b458..9230888e34 100644 --- a/blockdev.c +++ b/blockdev.c @@ -63,11 +63,13 @@ #include "qemu/main-loop.h" #include "qemu/throttle-options.h" +/* Protected by BQL */ QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); void bdrv_set_monitor_owned(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); } @@ -111,6 +113,8 @@ void override_max_devs(BlockInterfaceType type, int max_devs) BlockBackend *blk; DriveInfo *dinfo; + GLOBAL_STATE_CODE(); + if (max_devs <= 0) { return; } @@ -140,6 +144,8 @@ void blockdev_mark_auto_del(BlockBackend *blk) DriveInfo *dinfo = blk_legacy_dinfo(blk); BlockJob *job; + GLOBAL_STATE_CODE(); + if (!dinfo) { return; } @@ -161,6 +167,7 @@ void blockdev_mark_auto_del(BlockBackend *blk) void blockdev_auto_del(BlockBackend *blk) { DriveInfo *dinfo = blk_legacy_dinfo(blk); + GLOBAL_STATE_CODE(); if (dinfo && dinfo->auto_del) { monitor_remove_blk(blk); @@ -185,6 +192,8 @@ QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, { QemuOpts *opts; + GLOBAL_STATE_CODE(); + opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false); if (!opts) { return NULL; @@ -205,6 +214,8 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) BlockBackend *blk; DriveInfo *dinfo; + GLOBAL_STATE_CODE(); + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { dinfo = blk_legacy_dinfo(blk); if (dinfo && dinfo->type == type @@ -227,6 +238,8 @@ void drive_check_orphaned(void) Location loc; bool orphans = false; + GLOBAL_STATE_CODE(); + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { dinfo = blk_legacy_dinfo(blk); /* @@ -260,6 +273,7 @@ void drive_check_orphaned(void) DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) { + GLOBAL_STATE_CODE(); return drive_get(type, drive_index_to_bus_id(type, index), drive_index_to_unit_id(type, index)); @@ -271,6 +285,8 @@ int drive_get_max_bus(BlockInterfaceType type) BlockBackend *blk; DriveInfo *dinfo; + GLOBAL_STATE_CODE(); + max_bus = -1; for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { dinfo = blk_legacy_dinfo(blk); @@ -628,6 +644,7 @@ BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) { int bdrv_flags = 0; + GLOBAL_STATE_CODE(); /* bdrv_open() defaults to the values in bdrv_flags (for compatibility * with other callers) rather than what we want as the real defaults. * Apply the defaults here instead. */ @@ -646,6 +663,7 @@ void blockdev_close_all_bdrv_states(void) { BlockDriverState *bs, *next_bs; + GLOBAL_STATE_CODE(); QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) { AioContext *ctx = bdrv_get_aio_context(bs); @@ -658,6 +676,7 @@ void blockdev_close_all_bdrv_states(void) /* Iterates over the list of monitor-owned BlockDriverStates */ BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); return bs ? QTAILQ_NEXT(bs, monitor_list) : QTAILQ_FIRST(&monitor_bdrv_states); } @@ -754,6 +773,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, const char *filename; int i; + GLOBAL_STATE_CODE(); + /* Change legacy command line options into QMP ones */ static const struct { const char *from; @@ -1174,6 +1195,8 @@ typedef struct BlkActionState BlkActionState; * * Only prepare() may fail. In a single transaction, only one of commit() or * abort() will be called. clean() will always be called if it is present. + * + * Always run under BQL. */ typedef struct BlkActionOps { size_t instance_size; @@ -1235,7 +1258,7 @@ static void internal_snapshot_prepare(BlkActionState *common, BlockDriverState *bs; QEMUSnapshotInfo old_sn, *sn; bool ret; - qemu_timeval tv; + int64_t rt; BlockdevSnapshotInternal *internal; InternalSnapshotState *state; AioContext *aio_context; @@ -1305,9 +1328,9 @@ static void internal_snapshot_prepare(BlkActionState *common, /* 3. take the snapshot */ sn = &state->sn; pstrcpy(sn->name, sizeof(sn->name), name); - qemu_gettimeofday(&tv); - sn->date_sec = tv.tv_sec; - sn->date_nsec = tv.tv_usec * 1000; + rt = g_get_real_time(); + sn->date_sec = rt / G_USEC_PER_SEC; + sn->date_nsec = (rt % G_USEC_PER_SEC) * 1000; sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (replay_mode != REPLAY_MODE_NONE) { sn->icount = replay_get_current_icount(); @@ -2283,6 +2306,8 @@ static TransactionProperties *get_transaction_properties( /* * 'Atomic' group operations. The operations are performed as a set, and if * any fail then we roll back all operations in the group. + * + * Always run under BQL. */ void qmp_transaction(TransactionActionList *dev_list, bool has_props, @@ -2294,6 +2319,8 @@ void qmp_transaction(TransactionActionList *dev_list, BlkActionState *state, *next; Error *local_err = NULL; + GLOBAL_STATE_CODE(); + QTAILQ_HEAD(, BlkActionState) snap_bdrv_states; QTAILQ_INIT(&snap_bdrv_states); @@ -3596,6 +3623,8 @@ void qmp_blockdev_del(const char *node_name, Error **errp) AioContext *aio_context; BlockDriverState *bs; + GLOBAL_STATE_CODE(); + bs = bdrv_find_node(node_name); if (!bs) { error_setg(errp, "Failed to find node with node-name='%s'", node_name); diff --git a/blockjob.c b/blockjob.c index 10815a89fe..4868453d74 100644 --- a/blockjob.c +++ b/blockjob.c @@ -62,6 +62,7 @@ static bool is_block_job(Job *job) BlockJob *block_job_next(BlockJob *bjob) { Job *job = bjob ? &bjob->job : NULL; + GLOBAL_STATE_CODE(); do { job = job_next(job); @@ -73,6 +74,7 @@ BlockJob *block_job_next(BlockJob *bjob) BlockJob *block_job_get(const char *id) { Job *job = job_get(id); + GLOBAL_STATE_CODE(); if (job && is_block_job(job)) { return container_of(job, BlockJob, job); @@ -84,6 +86,7 @@ BlockJob *block_job_get(const char *id) void block_job_free(Job *job) { BlockJob *bjob = container_of(job, BlockJob, job); + GLOBAL_STATE_CODE(); block_job_remove_all_bdrv(bjob); ratelimit_destroy(&bjob->limit); @@ -183,6 +186,7 @@ static const BdrvChildClass child_job = { void block_job_remove_all_bdrv(BlockJob *job) { + GLOBAL_STATE_CODE(); /* * bdrv_root_unref_child() may reach child_job_[can_]set_aio_ctx(), * which will also traverse job->nodes, so consume the list one by @@ -205,6 +209,7 @@ void block_job_remove_all_bdrv(BlockJob *job) bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) { GSList *el; + GLOBAL_STATE_CODE(); for (el = job->nodes; el; el = el->next) { BdrvChild *c = el->data; @@ -221,6 +226,7 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, { BdrvChild *c; bool need_context_ops; + GLOBAL_STATE_CODE(); bdrv_ref(bs); @@ -270,6 +276,8 @@ bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) const BlockJobDriver *drv = block_job_driver(job); int64_t old_speed = job->speed; + GLOBAL_STATE_CODE(); + if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { return false; } @@ -299,6 +307,7 @@ bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) { + IO_CODE(); return ratelimit_calculate_delay(&job->limit, n); } @@ -307,6 +316,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) BlockJobInfo *info; uint64_t progress_current, progress_total; + GLOBAL_STATE_CODE(); + if (block_job_is_internal(job)) { error_setg(errp, "Cannot query QEMU internal jobs"); return NULL; @@ -434,6 +445,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, { BlockJob *job; int ret; + GLOBAL_STATE_CODE(); if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); @@ -488,6 +500,7 @@ fail: void block_job_iostatus_reset(BlockJob *job) { + GLOBAL_STATE_CODE(); if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { return; } @@ -498,6 +511,7 @@ void block_job_iostatus_reset(BlockJob *job) void block_job_user_resume(Job *job) { BlockJob *bjob = container_of(job, BlockJob, job); + GLOBAL_STATE_CODE(); block_job_iostatus_reset(bjob); } @@ -505,6 +519,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, int is_read, int error) { BlockErrorAction action; + IO_CODE(); switch (on_err) { case BLOCKDEV_ON_ERROR_ENOSPC: @@ -543,5 +558,6 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, AioContext *block_job_get_aio_context(BlockJob *job) { + GLOBAL_STATE_CODE(); return job->job.aio_context; } diff --git a/bsd-user/arm/target.h b/bsd-user/arm/target.h new file mode 100644 index 0000000000..419c039b68 --- /dev/null +++ b/bsd-user/arm/target.h @@ -0,0 +1,21 @@ +/* + * Intel general target stuff that's common to all i386 details + * + * Copyright (c) 2022 M. Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_H +#define TARGET_H + +/* + * arm EABI 'lumps' the registers for 64-bit args. + */ +static inline bool regpairs_aligned(void *cpu_env) +{ + return true; +} + +#endif /* ! TARGET_H */ + diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index b087db48fa..afb7814a8d 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -40,7 +40,6 @@ static inline void target_cpu_init(CPUARMState *env, static inline void target_cpu_loop(CPUARMState *env) { int trapnr, si_signo, si_code; - unsigned int n; CPUState *cs = env_cpu(env); for (;;) { @@ -66,82 +65,76 @@ static inline void target_cpu_loop(CPUARMState *env) break; case EXCP_SWI: { - n = env->regs[7]; - if (bsd_type == target_freebsd) { - int ret; - abi_ulong params = get_sp_from_cpustate(env); - int32_t syscall_nr = n; - int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; + int ret; + abi_ulong params = get_sp_from_cpustate(env); + int32_t syscall_nr = env->regs[7]; + int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; - /* See arm/arm/syscall.c cpu_fetch_syscall_args() */ - if (syscall_nr == TARGET_FREEBSD_NR_syscall) { - syscall_nr = env->regs[0]; - arg1 = env->regs[1]; - arg2 = env->regs[2]; - arg3 = env->regs[3]; - get_user_s32(arg4, params); - params += sizeof(int32_t); - get_user_s32(arg5, params); - params += sizeof(int32_t); - get_user_s32(arg6, params); - params += sizeof(int32_t); - get_user_s32(arg7, params); - arg8 = 0; - } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { - syscall_nr = env->regs[0]; - arg1 = env->regs[2]; - arg2 = env->regs[3]; - get_user_s32(arg3, params); - params += sizeof(int32_t); - get_user_s32(arg4, params); - params += sizeof(int32_t); - get_user_s32(arg5, params); - params += sizeof(int32_t); - get_user_s32(arg6, params); - arg7 = 0; - arg8 = 0; - } else { - arg1 = env->regs[0]; - arg2 = env->regs[1]; - arg3 = env->regs[2]; - arg4 = env->regs[3]; - get_user_s32(arg5, params); - params += sizeof(int32_t); - get_user_s32(arg6, params); - params += sizeof(int32_t); - get_user_s32(arg7, params); - params += sizeof(int32_t); - get_user_s32(arg8, params); - } - ret = do_freebsd_syscall(env, syscall_nr, arg1, arg2, arg3, - arg4, arg5, arg6, arg7, arg8); - /* - * Compare to arm/arm/vm_machdep.c - * cpu_set_syscall_retval() - */ - if (-TARGET_EJUSTRETURN == ret) { - /* - * Returning from a successful sigreturn syscall. - * Avoid clobbering register state. - */ - break; - } - if (-TARGET_ERESTART == ret) { - env->regs[15] -= env->thumb ? 2 : 4; - break; - } - if ((unsigned int)ret >= (unsigned int)(-515)) { - ret = -ret; - cpsr_write(env, CPSR_C, CPSR_C, CPSRWriteByInstr); - env->regs[0] = ret; - } else { - cpsr_write(env, 0, CPSR_C, CPSRWriteByInstr); - env->regs[0] = ret; /* XXX need to handle lseek()? */ - /* env->regs[1] = 0; */ - } + /* See arm/arm/syscall.c cpu_fetch_syscall_args() */ + if (syscall_nr == TARGET_FREEBSD_NR_syscall) { + syscall_nr = env->regs[0]; + arg1 = env->regs[1]; + arg2 = env->regs[2]; + arg3 = env->regs[3]; + get_user_s32(arg4, params); + params += sizeof(int32_t); + get_user_s32(arg5, params); + params += sizeof(int32_t); + get_user_s32(arg6, params); + params += sizeof(int32_t); + get_user_s32(arg7, params); + arg8 = 0; + } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { + syscall_nr = env->regs[0]; + arg1 = env->regs[2]; + arg2 = env->regs[3]; + get_user_s32(arg3, params); + params += sizeof(int32_t); + get_user_s32(arg4, params); + params += sizeof(int32_t); + get_user_s32(arg5, params); + params += sizeof(int32_t); + get_user_s32(arg6, params); + arg7 = 0; + arg8 = 0; } else { - fprintf(stderr, "qemu: bsd_type (= %d) syscall " - "not supported\n", bsd_type); + arg1 = env->regs[0]; + arg2 = env->regs[1]; + arg3 = env->regs[2]; + arg4 = env->regs[3]; + get_user_s32(arg5, params); + params += sizeof(int32_t); + get_user_s32(arg6, params); + params += sizeof(int32_t); + get_user_s32(arg7, params); + params += sizeof(int32_t); + get_user_s32(arg8, params); + } + ret = do_freebsd_syscall(env, syscall_nr, arg1, arg2, arg3, + arg4, arg5, arg6, arg7, arg8); + /* + * Compare to arm/arm/vm_machdep.c + * cpu_set_syscall_retval() + */ + if (-TARGET_EJUSTRETURN == ret) { + /* + * Returning from a successful sigreturn syscall. + * Avoid clobbering register state. + */ + break; + } + if (-TARGET_ERESTART == ret) { + env->regs[15] -= env->thumb ? 2 : 4; + break; + } + if ((unsigned int)ret >= (unsigned int)(-515)) { + ret = -ret; + cpsr_write(env, CPSR_C, CPSR_C, CPSRWriteByInstr); + env->regs[0] = ret; + } else { + cpsr_write(env, 0, CPSR_C, CPSRWriteByInstr); + env->regs[0] = ret; /* XXX need to handle lseek()? */ + /* env->regs[1] = 0; */ } } break; diff --git a/bsd-user/arm/target_arch_thread.h b/bsd-user/arm/target_arch_thread.h index 11c7f76583..fcafca2408 100644 --- a/bsd-user/arm/target_arch_thread.h +++ b/bsd-user/arm/target_arch_thread.h @@ -62,9 +62,7 @@ static inline void target_thread_init(struct target_pt_regs *regs, } regs->ARM_pc = infop->entry & 0xfffffffe; regs->ARM_sp = stack; - if (bsd_type == target_freebsd) { - regs->ARM_lr = infop->entry & 0xfffffffe; - } + regs->ARM_lr = infop->entry & 0xfffffffe; /* * FreeBSD kernel passes the ps_strings pointer in r0. This is used by some * programs to set status messages that we see in ps. bsd-user doesn't diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h new file mode 100644 index 0000000000..f0c3f347ec --- /dev/null +++ b/bsd-user/bsd-file.h @@ -0,0 +1,30 @@ +/* + * file related system call shims and definitions + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_FILE_H_ +#define BSD_FILE_H_ + +#include "qemu/path.h" + +extern struct iovec *lock_iovec(int type, abi_ulong target_addr, int count, + int copy); +extern void unlock_iovec(struct iovec *vec, abi_ulong target_addr, int count, + int copy); + +#endif /* !BSD_FILE_H_ */ diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 142a5bfac2..f8edb22f2a 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -246,7 +246,7 @@ static void padzero(abi_ulong elf_bss, abi_ulong last_bss) * patch target_mmap(), but it is more complicated as the file * size must be known. */ - if (qemu_real_host_page_size < qemu_host_page_size) { + if (qemu_real_host_page_size() < qemu_host_page_size) { abi_ulong end_addr, end_addr1; end_addr1 = REAL_HOST_PAGE_ALIGN(elf_bss); end_addr = HOST_PAGE_ALIGN(elf_bss); diff --git a/bsd-user/freebsd/meson.build b/bsd-user/freebsd/meson.build index 4b69cca7b9..f87c788e84 100644 --- a/bsd-user/freebsd/meson.build +++ b/bsd-user/freebsd/meson.build @@ -1,3 +1,4 @@ bsd_user_ss.add(files( 'os-sys.c', + 'os-syscall.c', )) diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c new file mode 100644 index 0000000000..d272478e7b --- /dev/null +++ b/bsd-user/freebsd/os-syscall.c @@ -0,0 +1,91 @@ +/* + * BSD syscalls + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2013-2014 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * We need the FreeBSD "legacy" definitions. Rust needs the FreeBSD 11 system + * calls since it doesn't use libc at all, so we have to emulate that despite + * FreeBSD 11 being EOL'd. + */ +#define _WANT_FREEBSD11_STAT +#define _WANT_FREEBSD11_STATFS +#define _WANT_FREEBSD11_DIRENT +#define _WANT_KERNEL_ERRNO +#define _WANT_SEMUN +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/path.h" +#include +#include +#include +#include + +#include "qemu.h" +#include "signal-common.h" +#include "user/syscall-trace.h" + +#include "bsd-file.h" + +void target_set_brk(abi_ulong new_brk) +{ +} + +/* + * errno conversion. + */ +abi_long get_errno(abi_long ret) +{ + if (ret == -1) { + return -host_to_target_errno(errno); + } else { + return ret; + } +} + +int host_to_target_errno(int err) +{ + /* + * All the BSDs have the property that the error numbers are uniform across + * all architectures for a given BSD, though they may vary between different + * BSDs. + */ + return err; +} + +bool is_error(abi_long ret) +{ + return (abi_ulong)ret >= (abi_ulong)(-4096); +} + +/* + * do_syscall() should always have a single exit point at the end so that + * actions, such as logging of syscall results, can be performed. All errnos + * that do_syscall() returns must be -TARGET_. + */ +abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) +{ + return 0; +} + +void syscall_init(void) +{ +} diff --git a/bsd-user/i386/target.h b/bsd-user/i386/target.h new file mode 100644 index 0000000000..9b9df047a3 --- /dev/null +++ b/bsd-user/i386/target.h @@ -0,0 +1,21 @@ +/* + * Intel general target stuff that's common to all i386 details + * + * Copyright (c) 2022 M. Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_ARCH_H +#define TARGET_ARCH_H + +/* + * i386 doesn't 'lump' the registers for 64-bit args. + */ +static inline bool regpairs_aligned(void *cpu_env) +{ + return false; +} + +#endif /* ! TARGET_ARCH_H */ + diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 3cbf69d8af..9da22202d4 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -116,55 +116,45 @@ static inline void target_cpu_loop(CPUX86State *env) process_queued_cpu_work(cs); switch (trapnr) { - case 0x80: + case 0x80: { /* syscall from int $0x80 */ - if (bsd_type == target_freebsd) { - abi_ulong params = (abi_ulong) env->regs[R_ESP] + - sizeof(int32_t); - int32_t syscall_nr = env->regs[R_EAX]; - int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; + abi_ulong params = (abi_ulong) env->regs[R_ESP] + + sizeof(int32_t); + int32_t syscall_nr = env->regs[R_EAX]; + int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; - if (syscall_nr == TARGET_FREEBSD_NR_syscall) { - get_user_s32(syscall_nr, params); - params += sizeof(int32_t); - } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { - get_user_s32(syscall_nr, params); - params += sizeof(int64_t); - } - get_user_s32(arg1, params); + if (syscall_nr == TARGET_FREEBSD_NR_syscall) { + get_user_s32(syscall_nr, params); params += sizeof(int32_t); - get_user_s32(arg2, params); - params += sizeof(int32_t); - get_user_s32(arg3, params); - params += sizeof(int32_t); - get_user_s32(arg4, params); - params += sizeof(int32_t); - get_user_s32(arg5, params); - params += sizeof(int32_t); - get_user_s32(arg6, params); - params += sizeof(int32_t); - get_user_s32(arg7, params); - params += sizeof(int32_t); - get_user_s32(arg8, params); - env->regs[R_EAX] = do_freebsd_syscall(env, - syscall_nr, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8); - } else { /* if (bsd_type == target_openbsd) */ - env->regs[R_EAX] = do_openbsd_syscall(env, - env->regs[R_EAX], - env->regs[R_EBX], - env->regs[R_ECX], - env->regs[R_EDX], - env->regs[R_ESI], - env->regs[R_EDI], - env->regs[R_EBP]); + } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { + get_user_s32(syscall_nr, params); + params += sizeof(int64_t); + } + get_user_s32(arg1, params); + params += sizeof(int32_t); + get_user_s32(arg2, params); + params += sizeof(int32_t); + get_user_s32(arg3, params); + params += sizeof(int32_t); + get_user_s32(arg4, params); + params += sizeof(int32_t); + get_user_s32(arg5, params); + params += sizeof(int32_t); + get_user_s32(arg6, params); + params += sizeof(int32_t); + get_user_s32(arg7, params); + params += sizeof(int32_t); + get_user_s32(arg8, params); + env->regs[R_EAX] = do_freebsd_syscall(env, + syscall_nr, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8); } if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) { env->regs[R_EAX] = -env->regs[R_EAX]; diff --git a/bsd-user/main.c b/bsd-user/main.c index f1d58e905e..6f09180d65 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -24,10 +24,9 @@ #include #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qemu/units.h" #include "qemu/accel.h" -#include "sysemu/tcg.h" #include "qemu-version.h" #include @@ -96,7 +95,6 @@ unsigned long reserved_va; static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; -enum BSDType bsd_type; char qemu_proc_pathname[PATH_MAX]; /* full path to exeutable */ unsigned long target_maxtsiz = TARGET_MAXTSIZ; /* max text size */ @@ -164,7 +162,6 @@ static void usage(void) "-E var=value sets/modifies targets environment variable(s)\n" "-U var unsets targets environment variable(s)\n" "-B address set guest_base address to address\n" - "-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n" "\n" "Debug options:\n" "-d item1[,...] enable logging of specified items\n" @@ -285,7 +282,6 @@ int main(int argc, char **argv) const char *gdbstub = NULL; char **target_environ, **wrk; envlist_t *envlist = NULL; - bsd_type = HOST_DEFAULT_BSD_TYPE; char *argv0 = NULL; adjust_ssize(); @@ -392,17 +388,6 @@ int main(int argc, char **argv) have_guest_base = true; } else if (!strcmp(r, "drop-ld-preload")) { (void) envlist_unsetenv(envlist, "LD_PRELOAD"); - } else if (!strcmp(r, "bsd")) { - if (!strcasecmp(argv[optind], "freebsd")) { - bsd_type = target_freebsd; - } else if (!strcasecmp(argv[optind], "netbsd")) { - bsd_type = target_netbsd; - } else if (!strcasecmp(argv[optind], "openbsd")) { - bsd_type = target_openbsd; - } else { - usage(); - } - optind++; } else if (!strcmp(r, "seed")) { seed_optarg = optarg; } else if (!strcmp(r, "singlestep")) { @@ -419,17 +404,16 @@ int main(int argc, char **argv) } /* init debug */ - qemu_log_needs_buffers(); - qemu_set_log_filename(log_file, &error_fatal); - if (log_mask) { - int mask; - - mask = qemu_str_to_log_mask(log_mask); - if (!mask) { - qemu_print_log_usage(stdout); - exit(1); + { + int mask = 0; + if (log_mask) { + mask = qemu_str_to_log_mask(log_mask); + if (!mask) { + qemu_print_log_usage(stdout); + exit(1); + } } - qemu_set_log(mask); + qemu_set_log_filename_flags(log_file, mask, &error_fatal); } if (optind >= argc) { @@ -518,20 +502,29 @@ int main(int argc, char **argv) g_free(target_environ); if (qemu_loglevel_mask(CPU_LOG_PAGE)) { - qemu_log("guest_base %p\n", (void *)guest_base); - log_page_dump("binary load"); + FILE *f = qemu_log_trylock(); + if (f) { + fprintf(f, "guest_base %p\n", (void *)guest_base); + fprintf(f, "page layout changed following binary load\n"); + page_dump(f); - qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); - qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); - qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n", - info->start_code); - qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n", - info->start_data); - qemu_log("end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data); - qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n", - info->start_stack); - qemu_log("brk 0x" TARGET_ABI_FMT_lx "\n", info->brk); - qemu_log("entry 0x" TARGET_ABI_FMT_lx "\n", info->entry); + fprintf(f, "start_brk 0x" TARGET_ABI_FMT_lx "\n", + info->start_brk); + fprintf(f, "end_code 0x" TARGET_ABI_FMT_lx "\n", + info->end_code); + fprintf(f, "start_code 0x" TARGET_ABI_FMT_lx "\n", + info->start_code); + fprintf(f, "start_data 0x" TARGET_ABI_FMT_lx "\n", + info->start_data); + fprintf(f, "end_data 0x" TARGET_ABI_FMT_lx "\n", + info->end_data); + fprintf(f, "start_stack 0x" TARGET_ABI_FMT_lx "\n", + info->start_stack); + fprintf(f, "brk 0x" TARGET_ABI_FMT_lx "\n", info->brk); + fprintf(f, "entry 0x" TARGET_ABI_FMT_lx "\n", info->entry); + + qemu_log_unlock(f); + } } /* build Task State */ diff --git a/bsd-user/meson.build b/bsd-user/meson.build index 8380fa44c2..5243122fc5 100644 --- a/bsd-user/meson.build +++ b/bsd-user/meson.build @@ -13,7 +13,6 @@ bsd_user_ss.add(files( 'mmap.c', 'signal.c', 'strace.c', - 'syscall.c', 'uaccess.c', )) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 13cb32dba1..e54e26de17 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "qemu.h" -#include "qemu-common.h" static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; static __thread int mmap_lock_count; @@ -515,7 +514,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, * up to the targets page boundary. */ - if ((qemu_real_host_page_size < qemu_host_page_size) && fd != -1) { + if ((qemu_real_host_page_size() < qemu_host_page_size) && fd != -1) { struct stat sb; if (fstat(fd, &sb) == -1) { diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 02921ac8b3..be6105385e 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -29,19 +29,13 @@ extern char **environ; -enum BSDType { - target_freebsd, - target_netbsd, - target_openbsd, -}; -extern enum BSDType bsd_type; - #include "exec/user/thunk.h" #include "target_arch.h" #include "syscall_defs.h" #include "target_syscall.h" #include "target_os_vmparam.h" #include "target_os_signal.h" +#include "target.h" #include "exec/gdbstub.h" /* @@ -181,7 +175,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); -void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +void gemu_log(const char *fmt, ...) G_GNUC_PRINTF(1, 2); extern __thread CPUState *thread_cpu; void cpu_loop(CPUArchState *env); char *target_strerror(int err); @@ -253,9 +247,10 @@ extern unsigned long target_dflssiz; extern unsigned long target_maxssiz; extern unsigned long target_sgrowsiz; -/* syscall.c */ +/* os-syscall.c */ abi_long get_errno(abi_long ret); bool is_error(abi_long ret); +int host_to_target_errno(int err); /* os-sys.c */ abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2); @@ -467,6 +462,19 @@ static inline void *lock_user_string(abi_ulong guest_addr) #define unlock_user_struct(host_ptr, guest_addr, copy) \ unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0) +static inline uint64_t target_arg64(uint32_t word0, uint32_t word1) +{ +#if TARGET_ABI_BITS == 32 +#if TARGET_BIG_ENDIAN + return ((uint64_t)word0 << 32) | word1; +#else + return ((uint64_t)word1 << 32) | word0; +#endif +#else /* TARGET_ABI_BITS != 32 */ + return word0; +#endif /* TARGET_ABI_BITS != 32 */ +} + #include #include "user/safe-syscall.h" diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 8a36b696d8..58a5386395 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -347,7 +347,8 @@ static int core_dump_signal(int sig) } /* Abort execution with signal. */ -static void QEMU_NORETURN dump_core_and_abort(int target_sig) +static G_NORETURN +void dump_core_and_abort(int target_sig) { CPUArchState *env = thread_cpu->env_ptr; CPUState *cpu = env_cpu(env); diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c deleted file mode 100644 index d3322760f4..0000000000 --- a/bsd-user/syscall.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * BSD syscalls - * - * Copyright (c) 2003 - 2008 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "qemu/path.h" -#include -#include -#include -#include - -#include "qemu.h" -#include "qemu-common.h" -#include "user/syscall-trace.h" - -//#define DEBUG - -static abi_ulong target_brk; -static abi_ulong target_original_brk; - -abi_long get_errno(abi_long ret) -{ - if (ret == -1) { - /* XXX need to translate host -> target errnos here */ - return -(errno); - } - return ret; -} - -#define target_to_host_bitmask(x, tbl) (x) - -bool is_error(abi_long ret) -{ - return (abi_ulong)ret >= (abi_ulong)(-4096); -} - -void target_set_brk(abi_ulong new_brk) -{ - target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk); -} - -/* do_obreak() must return target errnos. */ -static abi_long do_obreak(abi_ulong new_brk) -{ - abi_ulong brk_page; - abi_long mapped_addr; - int new_alloc_size; - - if (!new_brk) - return 0; - if (new_brk < target_original_brk) - return -TARGET_EINVAL; - - brk_page = HOST_PAGE_ALIGN(target_brk); - - /* If the new brk is less than this, set it and we're done... */ - if (new_brk < brk_page) { - target_brk = new_brk; - return 0; - } - - /* We need to allocate more memory after the brk... */ - new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1); - mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size, - PROT_READ|PROT_WRITE, - MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0)); - - if (!is_error(mapped_addr)) - target_brk = new_brk; - else - return mapped_addr; - - return 0; -} - -#ifdef __FreeBSD__ -/* - * XXX this uses the undocumented oidfmt interface to find the kind of - * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() - * (this is mostly copied from src/sbin/sysctl/sysctl.c) - */ -static int -oidfmt(int *oid, int len, char *fmt, uint32_t *kind) -{ - int qoid[CTL_MAXNAME+2]; - uint8_t buf[BUFSIZ]; - int i; - size_t j; - - qoid[0] = 0; - qoid[1] = 4; - memcpy(qoid + 2, oid, len * sizeof(int)); - - j = sizeof(buf); - i = sysctl(qoid, len + 2, buf, &j, 0, 0); - if (i) - return i; - - if (kind) - *kind = *(uint32_t *)buf; - - if (fmt) - strcpy(fmt, (char *)(buf + sizeof(uint32_t))); - return (0); -} - -/* - * try and convert sysctl return data for the target. - * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT. - */ -static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind) -{ - switch (kind & CTLTYPE) { - case CTLTYPE_INT: - case CTLTYPE_UINT: - *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp); - break; -#ifdef TARGET_ABI32 - case CTLTYPE_LONG: - case CTLTYPE_ULONG: - *(uint32_t *)holdp = tswap32(*(long *)holdp); - break; -#else - case CTLTYPE_LONG: - *(uint64_t *)holdp = tswap64(*(long *)holdp); - break; - case CTLTYPE_ULONG: - *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp); - break; -#endif -#ifdef CTLTYPE_U64 - case CTLTYPE_S64: - case CTLTYPE_U64: -#else - case CTLTYPE_QUAD: -#endif - *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp); - break; - case CTLTYPE_STRING: - break; - default: - /* XXX unhandled */ - return -1; - } - return 0; -} - -/* XXX this needs to be emulated on non-FreeBSD hosts... */ -static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp, - abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) -{ - abi_long ret; - void *hnamep, *holdp, *hnewp = NULL; - size_t holdlen; - abi_ulong oldlen = 0; - int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i; - uint32_t kind = 0; - - if (oldlenp) - get_user_ual(oldlen, oldlenp); - if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1))) - return -TARGET_EFAULT; - if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1))) - return -TARGET_EFAULT; - if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0))) - return -TARGET_EFAULT; - holdlen = oldlen; - for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++) - *q++ = tswap32(*p); - oidfmt(snamep, namelen, NULL, &kind); - /* XXX swap hnewp */ - ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen)); - if (!ret) - sysctl_oldcvt(holdp, holdlen, kind); - put_user_ual(holdlen, oldlenp); - unlock_user(hnamep, namep, 0); - unlock_user(holdp, oldp, holdlen); - if (hnewp) - unlock_user(hnewp, newp, 0); - g_free(snamep); - return ret; -} -#endif - -/* FIXME - * lock_iovec()/unlock_iovec() have a return code of 0 for success where - * other lock functions have a return code of 0 for failure. - */ -static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr, - int count, int copy) -{ - struct target_iovec *target_vec; - abi_ulong base; - int i; - - target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); - if (!target_vec) - return -TARGET_EFAULT; - for (i = 0;i < count; i++) { - base = tswapl(target_vec[i].iov_base); - vec[i].iov_len = tswapl(target_vec[i].iov_len); - if (vec[i].iov_len != 0) { - vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy); - /* Don't check lock_user return value. We must call writev even - if a element has invalid base address. */ - } else { - /* zero length pointer is ignored */ - vec[i].iov_base = NULL; - } - } - unlock_user (target_vec, target_addr, 0); - return 0; -} - -static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr, - int count, int copy) -{ - struct target_iovec *target_vec; - abi_ulong base; - int i; - - target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); - if (!target_vec) - return -TARGET_EFAULT; - for (i = 0;i < count; i++) { - if (target_vec[i].iov_base) { - base = tswapl(target_vec[i].iov_base); - unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0); - } - } - unlock_user (target_vec, target_addr, 0); - - return 0; -} - -/* do_syscall() should always have a single exit point at the end so - that actions, such as logging of syscall results, can be performed. - All errnos that do_syscall() returns must be -TARGET_. */ -abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, - abi_long arg2, abi_long arg3, abi_long arg4, - abi_long arg5, abi_long arg6, abi_long arg7, - abi_long arg8) -{ - CPUState *cpu = env_cpu(cpu_env); - abi_long ret; - void *p; - -#ifdef DEBUG - gemu_log("freebsd syscall %d\n", num); -#endif - record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0); - - if (do_strace) - print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - - switch (num) { - case TARGET_FREEBSD_NR_exit: -#ifdef CONFIG_GPROF - _mcleanup(); -#endif - gdb_exit(arg1); - qemu_plugin_user_exit(); - /* XXX: should free thread stack and CPU env */ - _exit(arg1); - ret = 0; /* avoid warning */ - break; - case TARGET_FREEBSD_NR_read: - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(read(arg1, p, arg3)); - unlock_user(p, arg2, ret); - break; - case TARGET_FREEBSD_NR_write: - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; - ret = get_errno(write(arg1, p, arg3)); - unlock_user(p, arg2, 0); - break; - case TARGET_FREEBSD_NR_writev: - { - int count = arg3; - struct iovec *vec; - - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) - goto efault; - ret = get_errno(writev(arg1, vec, count)); - unlock_iovec(vec, arg2, count, 0); - } - break; - case TARGET_FREEBSD_NR_open: - if (!(p = lock_user_string(arg1))) - goto efault; - ret = get_errno(open(path(p), - target_to_host_bitmask(arg2, fcntl_flags_tbl), - arg3)); - unlock_user(p, arg1, 0); - break; - case TARGET_FREEBSD_NR_mmap: - ret = get_errno(target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, - arg6)); - break; - case TARGET_FREEBSD_NR_mprotect: - ret = get_errno(target_mprotect(arg1, arg2, arg3)); - break; - case TARGET_FREEBSD_NR_break: - ret = do_obreak(arg1); - break; -#ifdef __FreeBSD__ - case TARGET_FREEBSD_NR___sysctl: - ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6); - break; -#endif - case TARGET_FREEBSD_NR_sysarch: - ret = do_freebsd_sysarch(cpu_env, arg1, arg2); - break; - case TARGET_FREEBSD_NR_syscall: - case TARGET_FREEBSD_NR___syscall: - ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0); - break; - default: - ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - break; - } - fail: -#ifdef DEBUG - gemu_log(" = %ld\n", ret); -#endif - if (do_strace) - print_freebsd_syscall_ret(num, ret); - - record_syscall_return(cpu, num, ret); - return ret; - efault: - ret = -TARGET_EFAULT; - goto fail; -} - -abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, - abi_long arg2, abi_long arg3, abi_long arg4, - abi_long arg5, abi_long arg6) -{ - CPUState *cpu = env_cpu(cpu_env); - abi_long ret; - void *p; - -#ifdef DEBUG - gemu_log("netbsd syscall %d\n", num); -#endif - - record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0); - - if (do_strace) - print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - - switch (num) { - case TARGET_NETBSD_NR_exit: -#ifdef CONFIG_GPROF - _mcleanup(); -#endif - gdb_exit(arg1); - qemu_plugin_user_exit(); - /* XXX: should free thread stack and CPU env */ - _exit(arg1); - ret = 0; /* avoid warning */ - break; - case TARGET_NETBSD_NR_read: - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(read(arg1, p, arg3)); - unlock_user(p, arg2, ret); - break; - case TARGET_NETBSD_NR_write: - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; - ret = get_errno(write(arg1, p, arg3)); - unlock_user(p, arg2, 0); - break; - case TARGET_NETBSD_NR_open: - if (!(p = lock_user_string(arg1))) - goto efault; - ret = get_errno(open(path(p), - target_to_host_bitmask(arg2, fcntl_flags_tbl), - arg3)); - unlock_user(p, arg1, 0); - break; - case TARGET_NETBSD_NR_mmap: - ret = get_errno(target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, - arg6)); - break; - case TARGET_NETBSD_NR_mprotect: - ret = get_errno(target_mprotect(arg1, arg2, arg3)); - break; - case TARGET_NETBSD_NR_syscall: - case TARGET_NETBSD_NR___syscall: - ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); - break; - default: - ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - break; - } - fail: -#ifdef DEBUG - gemu_log(" = %ld\n", ret); -#endif - if (do_strace) - print_netbsd_syscall_ret(num, ret); - - record_syscall_return(cpu, num, ret); - return ret; - efault: - ret = -TARGET_EFAULT; - goto fail; -} - -abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, - abi_long arg2, abi_long arg3, abi_long arg4, - abi_long arg5, abi_long arg6) -{ - CPUState *cpu = env_cpu(cpu_env); - abi_long ret; - void *p; - -#ifdef DEBUG - gemu_log("openbsd syscall %d\n", num); -#endif - - record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0); - - if (do_strace) - print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - - switch (num) { - case TARGET_OPENBSD_NR_exit: -#ifdef CONFIG_GPROF - _mcleanup(); -#endif - gdb_exit(arg1); - qemu_plugin_user_exit(); - /* XXX: should free thread stack and CPU env */ - _exit(arg1); - ret = 0; /* avoid warning */ - break; - case TARGET_OPENBSD_NR_read: - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(read(arg1, p, arg3)); - unlock_user(p, arg2, ret); - break; - case TARGET_OPENBSD_NR_write: - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; - ret = get_errno(write(arg1, p, arg3)); - unlock_user(p, arg2, 0); - break; - case TARGET_OPENBSD_NR_open: - if (!(p = lock_user_string(arg1))) - goto efault; - ret = get_errno(open(path(p), - target_to_host_bitmask(arg2, fcntl_flags_tbl), - arg3)); - unlock_user(p, arg1, 0); - break; - case TARGET_OPENBSD_NR_mmap: - ret = get_errno(target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, - arg6)); - break; - case TARGET_OPENBSD_NR_mprotect: - ret = get_errno(target_mprotect(arg1, arg2, arg3)); - break; - case TARGET_OPENBSD_NR_syscall: - case TARGET_OPENBSD_NR___syscall: - ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); - break; - default: - ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - break; - } - fail: -#ifdef DEBUG - gemu_log(" = %ld\n", ret); -#endif - if (do_strace) - print_openbsd_syscall_ret(num, ret); - - record_syscall_return(cpu, num, ret); - return ret; - efault: - ret = -TARGET_EFAULT; - goto fail; -} - -void syscall_init(void) -{ -} diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index 62b472b990..c3bf14f38f 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -179,4 +179,51 @@ struct target_freebsd__wrusage { struct target_freebsd_rusage wru_children; }; +#define safe_syscall0(type, name) \ +type safe_##name(void) \ +{ \ + return safe_syscall(SYS_##name); \ +} + +#define safe_syscall1(type, name, type1, arg1) \ +type safe_##name(type1 arg1) \ +{ \ + return safe_syscall(SYS_##name, arg1); \ +} + +#define safe_syscall2(type, name, type1, arg1, type2, arg2) \ +type safe_##name(type1 arg1, type2 arg2) \ +{ \ + return safe_syscall(SYS_##name, arg1, arg2); \ +} + +#define safe_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ +type safe_##name(type1 arg1, type2 arg2, type3 arg3) \ +{ \ + return safe_syscall(SYS_##name, arg1, arg2, arg3); \ +} + +#define safe_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ +type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + return safe_syscall(SYS_##name, arg1, arg2, arg3, arg4); \ +} + +#define safe_syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ +type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) \ +{ \ + return safe_syscall(SYS_##name, arg1, arg2, arg3, arg4, arg5); \ +} + +#define safe_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ +type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) \ +{ \ + return safe_syscall(SYS_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ +} + #endif /* ! _SYSCALL_DEFS_H_ */ diff --git a/bsd-user/x86_64/target.h b/bsd-user/x86_64/target.h new file mode 100644 index 0000000000..8956631db1 --- /dev/null +++ b/bsd-user/x86_64/target.h @@ -0,0 +1,21 @@ +/* + * Intel general target stuff that's common to all x86_64 details + * + * Copyright (c) 2022 M. Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_H +#define TARGET_H + +/* + * x86 doesn't 'lump' the registers for 64-bit args, all args are 64 bits. + */ +static inline bool regpairs_aligned(void *cpu_env) +{ + return false; +} + +#endif /* ! TARGET_H */ + diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 0a9c0f0894..5be2f02416 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -124,85 +124,16 @@ static inline void target_cpu_loop(CPUX86State *env) process_queued_cpu_work(cs); switch (trapnr) { - case 0x80: - /* syscall from int $0x80 */ - if (bsd_type == target_freebsd) { - abi_ulong params = (abi_ulong) env->regs[R_ESP] + - sizeof(int32_t); - int32_t syscall_nr = env->regs[R_EAX]; - int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; - - if (syscall_nr == TARGET_FREEBSD_NR_syscall) { - get_user_s32(syscall_nr, params); - params += sizeof(int32_t); - } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { - get_user_s32(syscall_nr, params); - params += sizeof(int64_t); - } - get_user_s32(arg1, params); - params += sizeof(int32_t); - get_user_s32(arg2, params); - params += sizeof(int32_t); - get_user_s32(arg3, params); - params += sizeof(int32_t); - get_user_s32(arg4, params); - params += sizeof(int32_t); - get_user_s32(arg5, params); - params += sizeof(int32_t); - get_user_s32(arg6, params); - params += sizeof(int32_t); - get_user_s32(arg7, params); - params += sizeof(int32_t); - get_user_s32(arg8, params); - env->regs[R_EAX] = do_freebsd_syscall(env, - syscall_nr, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8); - } else { /* if (bsd_type == target_openbsd) */ - env->regs[R_EAX] = do_openbsd_syscall(env, - env->regs[R_EAX], - env->regs[R_EBX], - env->regs[R_ECX], - env->regs[R_EDX], - env->regs[R_ESI], - env->regs[R_EDI], - env->regs[R_EBP]); - } - if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) { - env->regs[R_EAX] = -env->regs[R_EAX]; - env->eflags |= CC_C; - } else { - env->eflags &= ~CC_C; - } - break; - case EXCP_SYSCALL: /* syscall from syscall instruction */ - if (bsd_type == target_freebsd) { - env->regs[R_EAX] = do_freebsd_syscall(env, - env->regs[R_EAX], - env->regs[R_EDI], - env->regs[R_ESI], - env->regs[R_EDX], - env->regs[R_ECX], - env->regs[8], - env->regs[9], 0, 0); - } else { /* if (bsd_type == target_openbsd) */ - env->regs[R_EAX] = do_openbsd_syscall(env, - env->regs[R_EAX], - env->regs[R_EDI], - env->regs[R_ESI], - env->regs[R_EDX], - env->regs[10], - env->regs[8], - env->regs[9]); - } + env->regs[R_EAX] = do_freebsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[R_ECX], + env->regs[8], + env->regs[9], 0, 0); env->eip = env->exception_next_eip; if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) { env->regs[R_EAX] = -env->regs[R_EAX]; diff --git a/bsd-user/x86_64/target_arch_thread.h b/bsd-user/x86_64/target_arch_thread.h index d105e43fd3..b745d7ffeb 100644 --- a/bsd-user/x86_64/target_arch_thread.h +++ b/bsd-user/x86_64/target_arch_thread.h @@ -32,9 +32,7 @@ static inline void target_thread_init(struct target_pt_regs *regs, regs->rax = 0; regs->rsp = infop->start_stack; regs->rip = infop->entry; - if (bsd_type == target_freebsd) { - regs->rdi = infop->start_stack; - } + regs->rdi = infop->start_stack; } #endif /* !_TARGET_ARCH_THREAD_H_ */ diff --git a/chardev/char-fd.c b/chardev/char-fd.c index 93c56913b4..cf78454841 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/module.h" #include "qemu/sockets.h" #include "qapi/error.h" @@ -213,8 +212,8 @@ void qemu_chr_open_fd(Chardev *chr, FDChardev *s = FD_CHARDEV(chr); g_autofree char *name = NULL; - if (fd_out >= 0) { - qemu_set_nonblock(fd_out); + if (fd_out >= 0 && !g_unix_set_fd_nonblocking(fd_out, true, NULL)) { + assert(!"Failed to set FD nonblocking"); } if (fd_out == fd_in && fd_in >= 0) { diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index 7eca5d9a56..66d3b85091 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/chardev/char-pty.c b/chardev/char-pty.c index a2d1e7c985..53f25c6bbd 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "chardev/char.h" #include "io/channel-file.h" @@ -197,6 +196,117 @@ static void char_pty_finalize(Object *obj) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } +#if defined HAVE_PTY_H +# include +#elif defined CONFIG_BSD +# include +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +# include +# else +# include +# endif +#elif defined CONFIG_SOLARIS +# include +# include +#else +# include +#endif + +#ifdef __sun__ + +#if !defined(HAVE_OPENPTY) +/* Once illumos has openpty(), this is going to be removed. */ +static int openpty(int *amaster, int *aslave, char *name, + struct termios *termp, struct winsize *winp) +{ + const char *slave; + int mfd = -1, sfd = -1; + + *amaster = *aslave = -1; + + mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (mfd < 0) { + goto err; + } + + if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) { + goto err; + } + + if ((slave = ptsname(mfd)) == NULL) { + goto err; + } + + if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) { + goto err; + } + + if (ioctl(sfd, I_PUSH, "ptem") == -1 || + (termp != NULL && tcgetattr(sfd, termp) < 0)) { + goto err; + } + + *amaster = mfd; + *aslave = sfd; + + if (winp) { + ioctl(sfd, TIOCSWINSZ, winp); + } + + return 0; + +err: + if (sfd != -1) { + close(sfd); + } + close(mfd); + return -1; +} +#endif + +static void cfmakeraw (struct termios *termios_p) +{ + termios_p->c_iflag &= + ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + termios_p->c_oflag &= ~OPOST; + termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + termios_p->c_cflag &= ~(CSIZE | PARENB); + termios_p->c_cflag |= CS8; + + termios_p->c_cc[VMIN] = 0; + termios_p->c_cc[VTIME] = 0; +} +#endif + +/* like openpty() but also makes it raw; return master fd */ +static int qemu_openpty_raw(int *aslave, char *pty_name) +{ + int amaster; + struct termios tty; +#if defined(__OpenBSD__) || defined(__DragonFly__) + char pty_buf[PATH_MAX]; +#define q_ptsname(x) pty_buf +#else + char *pty_buf = NULL; +#define q_ptsname(x) ptsname(x) +#endif + + if (openpty(&amaster, aslave, pty_buf, NULL, NULL) < 0) { + return -1; + } + + /* Set raw attributes on the pty. */ + tcgetattr(*aslave, &tty); + cfmakeraw(&tty); + tcsetattr(*aslave, TCSAFLUSH, &tty); + + if (pty_name) { + strcpy(pty_name, q_ptsname(amaster)); + } + + return amaster; +} + static void char_pty_open(Chardev *chr, ChardevBackend *backend, bool *be_opened, @@ -214,7 +324,10 @@ static void char_pty_open(Chardev *chr, } close(slave_fd); - qemu_set_nonblock(master_fd); + if (!g_unix_set_fd_nonblocking(master_fd, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return; + } chr->filename = g_strdup_printf("pty:%s", pty_name); qemu_printf("char device redirected to %s (label %s)\n", diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 7c3d84ae24..4b0b83d5b4 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -271,7 +271,10 @@ static void qmp_chardev_open_serial(Chardev *chr, if (fd < 0) { return; } - qemu_set_nonblock(fd); + if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return; + } tty_serial_init(fd, 115200, 'N', 8, 1); qemu_chr_open_fd(chr, fd, fd); diff --git a/chardev/char-socket.c b/chardev/char-socket.c index fab2d791d4..dc4e218eeb 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -311,7 +311,7 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) } /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_set_block(fd); + qemu_socket_set_block(fd); #ifndef MSG_CMSG_CLOEXEC qemu_set_cloexec(fd); diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 403da308c9..3c648678ab 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -103,7 +103,10 @@ static void qemu_chr_open_stdio(Chardev *chr, stdio_in_use = true; old_fd0_flags = fcntl(0, F_GETFL); tcgetattr(0, &oldtty); - qemu_set_nonblock(0); + if (!g_unix_set_fd_nonblocking(0, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return; + } atexit(term_exit); memset(&act, 0, sizeof(act)); diff --git a/chardev/meson.build b/chardev/meson.build index 325ba2bdb9..664f77b887 100644 --- a/chardev/meson.build +++ b/chardev/meson.build @@ -12,11 +12,11 @@ chardev_ss.add(files( 'char-udp.c', 'char.c', )) -chardev_ss.add(when: 'CONFIG_POSIX', if_true: files( +chardev_ss.add(when: 'CONFIG_POSIX', if_true: [files( 'char-fd.c', 'char-parallel.c', 'char-pty.c', -)) +), util]) chardev_ss.add(when: 'CONFIG_WIN32', if_true: files( 'char-console.c', 'char-win-stdio.c', diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index d3ee10c00f..7794424745 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -1,6 +1,6 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index f81e5bf1fe..a249cc2e29 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -1,7 +1,7 @@ TARGET_ARCH=arm TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/hppa-linux-user.mak b/configs/targets/hppa-linux-user.mak index f01e0a7b9e..db873a8796 100644 --- a/configs/targets/hppa-linux-user.mak +++ b/configs/targets/hppa-linux-user.mak @@ -2,4 +2,4 @@ TARGET_ARCH=hppa TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/hppa-softmmu.mak b/configs/targets/hppa-softmmu.mak index e3e71eb21b..44f07b0332 100644 --- a/configs/targets/hppa-softmmu.mak +++ b/configs/targets/hppa-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=hppa TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/m68k-linux-user.mak b/configs/targets/m68k-linux-user.mak index 805d16c6ab..579b5d299c 100644 --- a/configs/targets/m68k-linux-user.mak +++ b/configs/targets/m68k-linux-user.mak @@ -1,6 +1,6 @@ TARGET_ARCH=m68k TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml TARGET_HAS_BFLT=y diff --git a/configs/targets/m68k-softmmu.mak b/configs/targets/m68k-softmmu.mak index 5df1a2b7d7..bbcd0bada6 100644 --- a/configs/targets/m68k-softmmu.mak +++ b/configs/targets/m68k-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=m68k -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml diff --git a/configs/targets/microblaze-linux-user.mak b/configs/targets/microblaze-linux-user.mak index 2a25bf2fa3..4249a37f65 100644 --- a/configs/targets/microblaze-linux-user.mak +++ b/configs/targets/microblaze-linux-user.mak @@ -1,5 +1,5 @@ TARGET_ARCH=microblaze TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index 33f2a00402..8385e2d333 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=microblaze -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y TARGET_NEED_FDT=y diff --git a/configs/targets/mips-linux-user.mak b/configs/targets/mips-linux-user.mak index 19f5779831..71fa77d464 100644 --- a/configs/targets/mips-linux-user.mak +++ b/configs/targets/mips-linux-user.mak @@ -3,4 +3,4 @@ TARGET_ABI_MIPSO32=y TARGET_SYSTBL_ABI=o32 TARGET_SYSTBL=syscall_o32.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/mips-softmmu.mak b/configs/targets/mips-softmmu.mak index 8a49999a47..7787a4d94c 100644 --- a/configs/targets/mips-softmmu.mak +++ b/configs/targets/mips-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=mips TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/mips64-linux-user.mak b/configs/targets/mips64-linux-user.mak index 32fd1acdf2..5a4771f22d 100644 --- a/configs/targets/mips64-linux-user.mak +++ b/configs/targets/mips64-linux-user.mak @@ -4,4 +4,4 @@ TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n64 TARGET_SYSTBL=syscall_n64.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/mips64-softmmu.mak b/configs/targets/mips64-softmmu.mak index ece25b9624..568d66650c 100644 --- a/configs/targets/mips64-softmmu.mak +++ b/configs/targets/mips64-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/mipsn32-linux-user.mak b/configs/targets/mipsn32-linux-user.mak index b8c2441ad0..1e80b302fc 100644 --- a/configs/targets/mipsn32-linux-user.mak +++ b/configs/targets/mipsn32-linux-user.mak @@ -5,4 +5,4 @@ TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n32 TARGET_SYSTBL=syscall_n32.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/nios2-softmmu.mak b/configs/targets/nios2-softmmu.mak index 9a372f0717..1e93b54cd1 100644 --- a/configs/targets/nios2-softmmu.mak +++ b/configs/targets/nios2-softmmu.mak @@ -1 +1,2 @@ TARGET_ARCH=nios2 +TARGET_ALIGNED_ONLY=y diff --git a/configs/targets/or1k-linux-user.mak b/configs/targets/or1k-linux-user.mak index 1dfb93e46d..39558f77ec 100644 --- a/configs/targets/or1k-linux-user.mak +++ b/configs/targets/or1k-linux-user.mak @@ -1,2 +1,2 @@ TARGET_ARCH=openrisc -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index 1dfb93e46d..263e970870 100644 --- a/configs/targets/or1k-softmmu.mak +++ b/configs/targets/or1k-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=openrisc -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y +TARGET_NEED_FDT=y diff --git a/configs/targets/ppc-linux-user.mak b/configs/targets/ppc-linux-user.mak index ca4187e4aa..cc0439a528 100644 --- a/configs/targets/ppc-linux-user.mak +++ b/configs/targets/ppc-linux-user.mak @@ -1,5 +1,5 @@ TARGET_ARCH=ppc TARGET_SYSTBL_ABI=common,nospu,32 TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak index f4eef1819a..774440108f 100644 --- a/configs/targets/ppc-softmmu.mak +++ b/configs/targets/ppc-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=ppc -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml TARGET_NEED_FDT=y diff --git a/configs/targets/ppc64-linux-user.mak b/configs/targets/ppc64-linux-user.mak index 3133346676..4d81969f4a 100644 --- a/configs/targets/ppc64-linux-user.mak +++ b/configs/targets/ppc64-linux-user.mak @@ -3,5 +3,5 @@ TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc TARGET_SYSTBL_ABI=common,nospu,64 TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index 84fbf46be9..ddf0c39617 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -1,6 +1,6 @@ TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml TARGET_NEED_FDT=y diff --git a/configs/targets/s390x-linux-user.mak b/configs/targets/s390x-linux-user.mak index 9e31ce6457..e2978248ed 100644 --- a/configs/targets/s390x-linux-user.mak +++ b/configs/targets/s390x-linux-user.mak @@ -1,5 +1,5 @@ TARGET_ARCH=s390x TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-gs.xml diff --git a/configs/targets/s390x-softmmu.mak b/configs/targets/s390x-softmmu.mak index fd9fbd870d..258b4cf358 100644 --- a/configs/targets/s390x-softmmu.mak +++ b/configs/targets/s390x-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=s390x -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-gs.xml diff --git a/configs/targets/sh4eb-linux-user.mak b/configs/targets/sh4eb-linux-user.mak index 9b6fb4c1bb..6724165efe 100644 --- a/configs/targets/sh4eb-linux-user.mak +++ b/configs/targets/sh4eb-linux-user.mak @@ -2,5 +2,5 @@ TARGET_ARCH=sh4 TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y diff --git a/configs/targets/sh4eb-softmmu.mak b/configs/targets/sh4eb-softmmu.mak index 382e9a80f8..dc8b30bf7a 100644 --- a/configs/targets/sh4eb-softmmu.mak +++ b/configs/targets/sh4eb-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=sh4 TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc-linux-user.mak b/configs/targets/sparc-linux-user.mak index 53dc7aaed5..00e7bc1f07 100644 --- a/configs/targets/sparc-linux-user.mak +++ b/configs/targets/sparc-linux-user.mak @@ -2,4 +2,4 @@ TARGET_ARCH=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak index 9ba3d7b07f..a849190f01 100644 --- a/configs/targets/sparc-softmmu.mak +++ b/configs/targets/sparc-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=sparc TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc32plus-linux-user.mak b/configs/targets/sparc32plus-linux-user.mak index e4c51df3dc..a65c0951a1 100644 --- a/configs/targets/sparc32plus-linux-user.mak +++ b/configs/targets/sparc32plus-linux-user.mak @@ -5,4 +5,4 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak index 9d23ab4a26..20fcb93fa4 100644 --- a/configs/targets/sparc64-linux-user.mak +++ b/configs/targets/sparc64-linux-user.mak @@ -4,4 +4,4 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index 8dd3217800..c626ac3eae 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_ALIGNED_ONLY=y -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y diff --git a/configs/targets/xtensaeb-linux-user.mak b/configs/targets/xtensaeb-linux-user.mak index 1ea0f1ba91..bce2d1d65d 100644 --- a/configs/targets/xtensaeb-linux-user.mak +++ b/configs/targets/xtensaeb-linux-user.mak @@ -1,5 +1,5 @@ TARGET_ARCH=xtensa TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y diff --git a/configs/targets/xtensaeb-softmmu.mak b/configs/targets/xtensaeb-softmmu.mak index 405cf5acbb..b02e11b820 100644 --- a/configs/targets/xtensaeb-softmmu.mak +++ b/configs/targets/xtensaeb-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=xtensa -TARGET_WORDS_BIGENDIAN=y +TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y diff --git a/configure b/configure index 9fe2a6a6ad..9b41cb3b43 100755 --- a/configure +++ b/configure @@ -77,6 +77,7 @@ TMPB="qemu-conf" TMPC="${TMPDIR1}/${TMPB}.c" TMPO="${TMPDIR1}/${TMPB}.o" TMPCXX="${TMPDIR1}/${TMPB}.cxx" +TMPM="${TMPDIR1}/${TMPB}.m" TMPE="${TMPDIR1}/${TMPB}.exe" rm -f config.log @@ -148,6 +149,10 @@ do_cxx() { do_compiler "$cxx" $CPU_CFLAGS "$@" } +do_objc() { + do_compiler "$objcc" $CPU_CFLAGS "$@" +} + # Append $2 to the variable named $1, with space separation add_to() { eval $1=\${$1:+\"\$$1 \"}\$2 @@ -239,7 +244,6 @@ audio_drv_list="default" block_drv_rw_whitelist="" block_drv_ro_whitelist="" host_cc="cc" -debug_info="yes" lto="false" stack_protector="" safe_stack="" @@ -285,6 +289,7 @@ done EXTRA_CFLAGS="" EXTRA_CXXFLAGS="" +EXTRA_OBJCFLAGS="" EXTRA_LDFLAGS="" xen_ctrl_version="$default_feature" @@ -296,17 +301,15 @@ vhost_vsock="$default_feature" vhost_user="no" vhost_user_fs="$default_feature" vhost_vdpa="$default_feature" -rdma="$default_feature" -pvrdma="$default_feature" +debug_info="yes" debug_tcg="no" debug="no" sanitizers="no" tsan="no" -fortify_source="$default_feature" +fortify_source="yes" gcov="no" EXESUF="" modules="no" -module_upgrades="no" prefix="/usr/local" qemu_suffix="qemu" softmmu="yes" @@ -314,17 +317,13 @@ linux_user="" bsd_user="" pkgversion="" pie="" -trace_backends="log" trace_file="trace" -opengl="$default_feature" coroutine="" tls_priority="NORMAL" plugins="$default_feature" -secret_keyring="$default_feature" meson="" meson_args="" ninja="" -gio="$default_feature" skip_meson=no # The following Meson options are handled manually (still they @@ -366,15 +365,14 @@ for opt do --extra-cflags=*) EXTRA_CFLAGS="$EXTRA_CFLAGS $optarg" EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS $optarg" + EXTRA_OBJCFLAGS="$EXTRA_OBJCFLAGS $optarg" ;; --extra-cxxflags=*) EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS $optarg" ;; + --extra-objcflags=*) EXTRA_OBJCFLAGS="$EXTRA_OBJCFLAGS $optarg" + ;; --extra-ldflags=*) EXTRA_LDFLAGS="$EXTRA_LDFLAGS $optarg" ;; - --enable-debug-info) debug_info="yes" - ;; - --disable-debug-info) debug_info="no" - ;; --cross-cc-*[!a-zA-Z0-9_-]*=*) error_exit "Passed bad --cross-cc-FOO option" ;; --cross-cc-cflags-*) cc_arch=${opt#--cross-cc-cflags-}; cc_arch=${cc_arch%%=*} @@ -382,7 +380,6 @@ for opt do cross_cc_vars="$cross_cc_vars cross_cc_cflags_${cc_arch}" ;; --cross-cc-*) cc_arch=${opt#--cross-cc-}; cc_arch=${cc_arch%%=*} - cc_archs="$cc_archs $cc_arch" eval "cross_cc_${cc_arch}=\$optarg" cross_cc_vars="$cross_cc_vars cross_cc_${cc_arch}" ;; @@ -630,10 +627,10 @@ case "$cpu" in ppc) CPU_CFLAGS="-m32" ;; ppc64) - CPU_CFLAGS="-m64 -mbig" ;; + CPU_CFLAGS="-m64 -mbig-endian" ;; ppc64le) cpu="ppc64" - CPU_CFLAGS="-m64 -mlittle" ;; + CPU_CFLAGS="-m64 -mlittle-endian" ;; s390) CPU_CFLAGS="-m31" ;; @@ -750,24 +747,22 @@ for opt do ;; --extra-cxxflags=*) ;; + --extra-objcflags=*) + ;; --extra-ldflags=*) ;; - --enable-debug-info) - ;; - --disable-debug-info) - ;; --cross-cc-*) ;; + --enable-debug-info) debug_info="yes" + ;; + --disable-debug-info) debug_info="no" + ;; --enable-modules) modules="yes" ;; --disable-modules) modules="no" ;; - --disable-module-upgrades) module_upgrades="no" - ;; - --enable-module-upgrades) module_upgrades="yes" - ;; --cpu=*) ;; --target-list=*) target_list="$optarg" @@ -957,10 +952,6 @@ for opt do ;; --enable-vhost-user-fs) vhost_user_fs="yes" ;; - --disable-opengl) opengl="no" - ;; - --enable-opengl) opengl="yes" - ;; --disable-zlib-test) ;; --disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane) @@ -974,14 +965,6 @@ for opt do ;; --tls-priority=*) tls_priority="$optarg" ;; - --enable-rdma) rdma="yes" - ;; - --disable-rdma) rdma="no" - ;; - --enable-pvrdma) pvrdma="yes" - ;; - --disable-pvrdma) pvrdma="no" - ;; --disable-vhost-user) vhost_user="no" ;; --enable-vhost-user) vhost_user="yes" @@ -1021,14 +1004,6 @@ for opt do ;; --gdb=*) gdb_bin="$optarg" ;; - --enable-keyring) secret_keyring="yes" - ;; - --disable-keyring) secret_keyring="no" - ;; - --enable-gio) gio=yes - ;; - --disable-gio) gio=no - ;; # backwards compatibility options --enable-trace-backend=*) meson_option_parse "--enable-trace-backends=$optarg" "$optarg" ;; @@ -1184,6 +1159,7 @@ Advanced options (experts only): --objcc=OBJCC use Objective-C compiler OBJCC [$objcc] --extra-cflags=CFLAGS append extra C compiler flags CFLAGS --extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS + --extra-objcflags=OBJCFLAGS append extra Objective C compiler flags OBJCFLAGS --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS --cross-cc-ARCH=CC use compiler when building ARCH guest test cases --cross-cc-cflags-ARCH= use compiler flags when building ARCH guest tests @@ -1249,14 +1225,11 @@ cat << EOF bsd-user all BSD usermode emulation targets pie Position Independent Executables modules modules support (non-Windows) - module-upgrades try to load modules from alternate paths for upgrades debug-tcg TCG debugging (default is disabled) debug-info debugging information lto Enable Link-Time Optimization. safe-stack SafeStack Stack Smash Protection. Depends on clang/llvm >= 3.7 and requires coroutine backend ucontext. - rdma Enable RDMA-based migration - pvrdma Enable PVRDMA support vhost-net vhost-net kernel acceleration support vhost-vsock virtio sockets device support vhost-scsi vhost-scsi kernel target support @@ -1264,8 +1237,6 @@ cat << EOF vhost-kernel vhost kernel backend support vhost-user vhost-user backend support vhost-vdpa vhost-vdpa kernel backend support - opengl opengl support - gio libgio support NOTE: The object files are built at the place where configure is launched EOF @@ -1450,10 +1421,27 @@ cc_has_warning_flag() { compile_prog "-Werror $optflag" "" } +objcc_has_warning_flag() { + cat > $TMPM < $TMPC < -int main(void) { return 0; } -EOF - rdma_libs="-lrdmacm -libverbs -libumad" - if compile_prog "" "$rdma_libs" ; then - rdma="yes" - else - if test "$rdma" = "yes" ; then - error_exit \ - " OpenFabrics librdmacm/libibverbs/libibumad not present." \ - " Your options:" \ - " (1) Fast: Install infiniband packages (devel) from your distro." \ - " (2) Cleanest: Install libraries from www.openfabrics.org" \ - " (3) Also: Install softiwarp if you don't have RDMA hardware" - fi - rdma="no" - fi -fi - -########################################## -# PVRDMA detection - -cat > $TMPC < - -int -main(void) -{ - char buf = 0; - void *addr = &buf; - addr = mremap(addr, 0, 1, MREMAP_MAYMOVE | MREMAP_FIXED); - - return 0; -} -EOF - -if test "$rdma" = "yes" ; then - case "$pvrdma" in - "") - if compile_prog "" ""; then - pvrdma="yes" - else - pvrdma="no" - fi - ;; - "yes") - if ! compile_prog "" ""; then - error_exit "PVRDMA is not supported since mremap is not implemented" - fi - pvrdma="yes" - ;; - "no") - pvrdma="no" - ;; - esac -else - if test "$pvrdma" = "yes" ; then - error_exit "PVRDMA requires rdma suppport" - fi - pvrdma="no" -fi - -# Let's see if enhanced reg_mr is supported -if test "$pvrdma" = "yes" ; then - -cat > $TMPC < - -int -main(void) -{ - struct ibv_mr *mr; - struct ibv_pd *pd = NULL; - size_t length = 10; - uint64_t iova = 0; - int access = 0; - void *addr = NULL; - - mr = ibv_reg_mr_iova(pd, addr, length, iova, access); - - ibv_dereg_mr(mr); - - return 0; -} -EOF - if ! compile_prog "" "-libverbs"; then - QEMU_CFLAGS="$QEMU_CFLAGS -DLEGACY_RDMA_REG_MR" - fi -fi - ########################################## # glib support probe @@ -2153,50 +2042,6 @@ if test "$static" = yes && test "$mingw32" = yes; then glib_cflags="-DGLIB_STATIC_COMPILATION $glib_cflags" fi -if ! test "$gio" = "no"; then - pass=no - if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then - gio_cflags=$($pkg_config --cflags gio-2.0) - gio_libs=$($pkg_config --libs gio-2.0) - gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0) - if ! has "$gdbus_codegen"; then - gdbus_codegen= - fi - # Check that the libraries actually work -- Ubuntu 18.04 ships - # with pkg-config --static --libs data for gio-2.0 that is missing - # -lblkid and will give a link error. - cat > $TMPC < -int main(void) -{ - g_dbus_proxy_new_sync(0, 0, 0, 0, 0, 0, 0, 0); - return 0; -} -EOF - if compile_prog "$gio_cflags" "$gio_libs" ; then - pass=yes - else - pass=no - fi - - if test "$pass" = "yes" && - $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then - gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)" - gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)" - fi - fi - - if test "$pass" = "no"; then - if test "$gio" = "yes"; then - feature_not_found "gio" "Install libgio >= 2.0" - else - gio=no - fi - else - gio=yes - fi -fi - # Sanity check that the current size_t matches the # size that glib thinks it should be. This catches # problems on multi-arch where people try to build @@ -2231,7 +2076,7 @@ static void foo_free(Foo *f) { g_free(f); } -G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free) int main(void) { return 0; } EOF if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then @@ -2266,59 +2111,6 @@ case "$fdt" in ;; esac -########################################## -# opengl probe (for sdl2, gtk) - -if test "$opengl" != "no" ; then - epoxy=no - if $pkg_config epoxy; then - cat > $TMPC << EOF -#include -int main(void) { return 0; } -EOF - if compile_prog "" "" ; then - epoxy=yes - fi - fi - - if test "$epoxy" = "yes" ; then - opengl_cflags="$($pkg_config --cflags epoxy)" - opengl_libs="$($pkg_config --libs epoxy)" - opengl=yes - else - if test "$opengl" = "yes" ; then - feature_not_found "opengl" "Please install epoxy with EGL" - fi - opengl_cflags="" - opengl_libs="" - opengl=no - fi -fi - -# check for usbfs -have_usbfs=no -if test "$linux_user" = "yes"; then - cat > $TMPC << EOF -#include - -#ifndef USBDEVFS_GET_CAPABILITIES -#error "USBDEVFS_GET_CAPABILITIES undefined" -#endif - -#ifndef USBDEVFS_DISCONNECT_CLAIM -#error "USBDEVFS_DISCONNECT_CLAIM undefined" -#endif - -int main(void) -{ - return 0; -} -EOF - if compile_prog "" ""; then - have_usbfs=yes - fi -fi - ########################################## # capstone @@ -2446,59 +2238,6 @@ else # "$safe_stack" = "" fi fi -######################################## -# check if __[u]int128_t is usable. - -int128=no -cat > $TMPC << EOF -__int128_t a; -__uint128_t b; -int main (void) { - a = a + b; - b = a * b; - a = a * a; - return 0; -} -EOF -if compile_prog "" "" ; then - int128=yes -fi - -######################################### -# See if 128-bit atomic operations are supported. - -atomic128=no -if test "$int128" = "yes"; then - cat > $TMPC << EOF -int main(void) -{ - unsigned __int128 x = 0, y = 0; - y = __atomic_load(&x, 0); - __atomic_store(&x, y, 0); - __atomic_compare_exchange(&x, &y, x, 0, 0, 0); - return 0; -} -EOF - if compile_prog "" "" ; then - atomic128=yes - fi -fi - -cmpxchg128=no -if test "$int128" = yes && test "$atomic128" = no; then - cat > $TMPC << EOF -int main(void) -{ - unsigned __int128 x = 0, y = 0; - __sync_val_compare_and_swap_16(&x, y, x); - return 0; -} -EOF - if compile_prog "" "" ; then - cmpxchg128=yes - fi -fi - ######################################## # check if ccache is interfering with # semantic analysis of macros @@ -2622,50 +2361,13 @@ case "$slirp" in ;; esac -########################################## -# check for usable __NR_keyctl syscall - -if test "$linux" = "yes" ; then - - have_keyring=no - cat > $TMPC << EOF -#include -#include -#include -#include -int main(void) { - return syscall(__NR_keyctl, KEYCTL_READ, 0, NULL, NULL, 0); -} -EOF - if compile_prog "" "" ; then - have_keyring=yes - fi -fi -if test "$secret_keyring" != "no" -then - if test "$have_keyring" = "yes" - then - secret_keyring=yes - else - if test "$secret_keyring" = "yes" - then - error_exit "syscall __NR_keyctl requested, \ -but not implemented on your system" - else - secret_keyring=no - fi - fi -fi - ########################################## # End of CC checks # After here, no more $cc or $ld runs write_c_skeleton -if test "$gcov" = "yes" ; then - : -elif test "$fortify_source" = "yes" ; then +if test "$fortify_source" = "yes" ; then QEMU_CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $QEMU_CFLAGS" debug=no fi @@ -2740,7 +2442,7 @@ if test "$QEMU_GA_VERSION" = ""; then QEMU_GA_VERSION=$(cat $source_path/VERSION) fi -QEMU_GA_MSI_MINGW_DLL_PATH="$($pkg_config --variable=prefix glib-2.0)/bin" +QEMU_GA_MSI_MINGW_BIN_PATH="$($pkg_config --variable=prefix glib-2.0)/bin" # Mac OS X ships with a broken assembler roms= @@ -2828,7 +2530,7 @@ if test "$debug_tcg" = "yes" ; then fi if test "$mingw32" = "yes" ; then echo "CONFIG_WIN32=y" >> $config_host_mak - echo "QEMU_GA_MSI_MINGW_DLL_PATH=${QEMU_GA_MSI_MINGW_DLL_PATH}" >> $config_host_mak + echo "QEMU_GA_MSI_MINGW_BIN_PATH=${QEMU_GA_MSI_MINGW_BIN_PATH}" >> $config_host_mak echo "QEMU_GA_MANUFACTURER=${QEMU_GA_MANUFACTURER}" >> $config_host_mak echo "QEMU_GA_DISTRO=${QEMU_GA_DISTRO}" >> $config_host_mak echo "QEMU_GA_VERSION=${QEMU_GA_VERSION}" >> $config_host_mak @@ -2862,20 +2564,6 @@ if test "$modules" = "yes"; then echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ )" >> $config_host_mak echo "CONFIG_MODULES=y" >> $config_host_mak fi -if test "$module_upgrades" = "yes"; then - echo "CONFIG_MODULE_UPGRADES=y" >> $config_host_mak -fi -if test "$have_usbfs" = "yes" ; then - echo "CONFIG_USBFS=y" >> $config_host_mak -fi -if test "$gio" = "yes" ; then - echo "CONFIG_GIO=y" >> $config_host_mak - echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak - echo "GIO_LIBS=$gio_libs" >> $config_host_mak -fi -if test "$gdbus_codegen" != "" ; then - echo "GDBUS_CODEGEN=$gdbus_codegen" >> $config_host_mak -fi echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak if test "$xen" = "enabled" ; then @@ -2917,15 +2605,6 @@ fi if test "$vhost_user_fs" = "yes" ; then echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak fi -if test "$tcg" = "enabled" -a "$tcg_interpreter" = "true" ; then - echo "CONFIG_TCG_INTERPRETER=y" >> $config_host_mak -fi - -if test "$opengl" = "yes" ; then - echo "CONFIG_OPENGL=y" >> $config_host_mak - echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak - echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak -fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then @@ -2942,27 +2621,6 @@ if test "$have_tsan" = "yes" && test "$have_tsan_iface_fiber" = "yes" ; then echo "CONFIG_TSAN=y" >> $config_host_mak fi -if test "$int128" = "yes" ; then - echo "CONFIG_INT128=y" >> $config_host_mak -fi - -if test "$atomic128" = "yes" ; then - echo "CONFIG_ATOMIC128=y" >> $config_host_mak -fi - -if test "$cmpxchg128" = "yes" ; then - echo "CONFIG_CMPXCHG128=y" >> $config_host_mak -fi - -if test "$rdma" = "yes" ; then - echo "CONFIG_RDMA=y" >> $config_host_mak - echo "RDMA_LIBS=$rdma_libs" >> $config_host_mak -fi - -if test "$pvrdma" = "yes" ; then - echo "CONFIG_PVRDMA=y" >> $config_host_mak -fi - if test "$plugins" = "yes" ; then echo "CONFIG_PLUGIN=y" >> $config_host_mak fi @@ -2974,10 +2632,6 @@ if test -n "$gdb_bin"; then fi fi -if test "$secret_keyring" = "yes" ; then - echo "CONFIG_SECRET_KEYRING=y" >> $config_host_mak -fi - if test "$as_shared_lib" = "yes" ; then echo "AS_SHARED_LIB=y" >> $config_host_mak fi @@ -2992,7 +2646,6 @@ echo "GENISOIMAGE=$genisoimage" >> $config_host_mak echo "MESON=$meson" >> $config_host_mak echo "NINJA=$ninja" >> $config_host_mak echo "CC=$cc" >> $config_host_mak -echo "HOST_CC=$host_cc" >> $config_host_mak echo "AR=$ar" >> $config_host_mak echo "AS=$as" >> $config_host_mak echo "CCAS=$ccas" >> $config_host_mak @@ -3002,6 +2655,7 @@ echo "LD=$ld" >> $config_host_mak echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak +echo "QEMU_OBJCFLAGS=$QEMU_OBJCFLAGS" >> $config_host_mak echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak echo "GLIB_LIBS=$glib_libs" >> $config_host_mak echo "GLIB_VERSION=$(pkg-config --modversion glib-2.0)" >> $config_host_mak @@ -3079,8 +2733,6 @@ LINKS="Makefile" LINKS="$LINKS tests/tcg/Makefile.target" LINKS="$LINKS pc-bios/optionrom/Makefile" LINKS="$LINKS pc-bios/s390-ccw/Makefile" -LINKS="$LINKS roms/seabios/Makefile" -LINKS="$LINKS pc-bios/qemu-icon.bmp" LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit LINKS="$LINKS tests/avocado tests/data" LINKS="$LINKS tests/qemu-iotests/check" @@ -3111,27 +2763,9 @@ done (for i in $cross_cc_vars; do export $i done -export target_list source_path use_containers cpu +export target_list source_path use_containers cpu host_cc $source_path/tests/tcg/configure.sh) -# temporary config to build submodules -if test -f $source_path/roms/seabios/Makefile; then - for rom in seabios; do - config_mak=roms/$rom/config.mak - echo "# Automatically generated by configure - do not modify" > $config_mak - echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak - echo "AS=$as" >> $config_mak - echo "CCAS=$ccas" >> $config_mak - echo "CC=$cc" >> $config_mak - echo "BCC=bcc" >> $config_mak - echo "CPP=$cpp" >> $config_mak - echo "OBJCOPY=objcopy" >> $config_mak - echo "IASL=$iasl" >> $config_mak - echo "LD=$ld" >> $config_mak - echo "RANLIB=$ranlib" >> $config_mak - done -fi - config_mak=pc-bios/optionrom/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "TOPSRC_DIR=$source_path" >> $config_mak @@ -3156,6 +2790,7 @@ if test "$skip_meson" = no; then echo "[built-in options]" >> $cross echo "c_args = [$(meson_quote $CFLAGS $EXTRA_CFLAGS)]" >> $cross echo "cpp_args = [$(meson_quote $CXXFLAGS $EXTRA_CXXFLAGS)]" >> $cross + test -n "$objcc" && echo "objc_args = [$(meson_quote $OBJCFLAGS $EXTRA_OBJCFLAGS)]" >> $cross echo "c_link_args = [$(meson_quote $CFLAGS $LDFLAGS $EXTRA_CFLAGS $EXTRA_LDFLAGS)]" >> $cross echo "cpp_link_args = [$(meson_quote $CXXFLAGS $LDFLAGS $EXTRA_CXXFLAGS $EXTRA_LDFLAGS)]" >> $cross echo "[binaries]" >> $cross diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index 20b477d582..b9fc6d230c 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -141,10 +141,10 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb, return kdbg; } -static void win_context_init_from_qemu_cpu_state(WinContext *ctx, +static void win_context_init_from_qemu_cpu_state(WinContext64 *ctx, QEMUCPUState *s) { - WinContext win_ctx = (WinContext){ + WinContext64 win_ctx = (WinContext64){ .ContextFlags = WIN_CTX_X64 | WIN_CTX_INT | WIN_CTX_SEG | WIN_CTX_CTL, .MxCsr = INITIAL_MXCSR, @@ -302,7 +302,7 @@ static int fill_context(KDDEBUGGER_DATA64 *kdbg, for (i = 0; i < qe->state_nr; i++) { uint64_t Prcb; uint64_t Context; - WinContext ctx; + WinContext64 ctx; QEMUCPUState *s = qe->state[i]; if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i, diff --git a/contrib/gitdm/aliases b/contrib/gitdm/aliases index 4792413ce7..e26b00a71d 100644 --- a/contrib/gitdm/aliases +++ b/contrib/gitdm/aliases @@ -34,8 +34,10 @@ malc@c046a42c-6fe2-441c-8c8c-71466251a162 av1474@comtv.ru # canonical emails liq3ea@163.com liq3ea@gmail.com -# some broken tags +# some broken DCO tags yuval.shaia.ml.gmail.com yuval.shaia.ml@gmail.com +jasowang jasowang@redhat.com +nicta.com.au peter.chubb@nicta.com.au # There is also a: # (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> diff --git a/contrib/ivshmem-server/ivshmem-server.c b/contrib/ivshmem-server/ivshmem-server.c index 39a6ffdb5d..2f3c7320a6 100644 --- a/contrib/ivshmem-server/ivshmem-server.c +++ b/contrib/ivshmem-server/ivshmem-server.c @@ -146,7 +146,7 @@ ivshmem_server_handle_new_conn(IvshmemServer *server) return -1; } - qemu_set_nonblock(newfd); + qemu_socket_set_nonblock(newfd); IVSHMEM_SERVER_DEBUG(server, "accept()=%d\n", newfd); /* allocate new structure for this peer */ diff --git a/contrib/rdmacm-mux/meson.build b/contrib/rdmacm-mux/meson.build index 7674f54cc5..36c9c89630 100644 --- a/contrib/rdmacm-mux/meson.build +++ b/contrib/rdmacm-mux/meson.build @@ -1,7 +1,5 @@ -if 'CONFIG_PVRDMA' in config_host - # if not found, CONFIG_PVRDMA should not be set +if have_pvrdma # FIXME: broken on big endian architectures - libumad = cc.find_library('ibumad', required: true) executable('rdmacm-mux', files('main.c'), genh, dependencies: [glib, libumad], build_by_default: false, diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index d14b2896bf..cd4a5d7335 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -593,7 +593,8 @@ static GOptionEntry entries[] = { {"blk-file", 'b', 0, G_OPTION_ARG_FILENAME, &opt_blk_file, "block device or file path", "PATH"}, { "read-only", 'r', 0, G_OPTION_ARG_NONE, &opt_read_only, - "Enable read-only", NULL } + "Enable read-only", NULL }, + { NULL, }, }; int main(int argc, char **argv) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index 611360e6b4..bfb8d93cf8 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -455,7 +455,7 @@ vg_create_mapping_iov(VuGpu *g, return -1; } - *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries); + *iov = g_new0(struct iovec, ab->nr_entries); for (i = 0; i < ab->nr_entries; i++) { uint64_t len = ents[i].length; (*iov)[i].iov_len = ents[i].length; diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c index fb15d0372c..503d0a4566 100644 --- a/contrib/vhost-user-gpu/vugbm.c +++ b/contrib/vhost-user-gpu/vugbm.c @@ -53,7 +53,7 @@ struct udmabuf_create { static size_t udmabuf_get_size(struct vugbm_buffer *buf) { - return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size); + return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size()); } static bool diff --git a/contrib/vhost-user-scsi/vhost-user-scsi.c b/contrib/vhost-user-scsi/vhost-user-scsi.c index 4f6e3e2a24..b2c0f98253 100644 --- a/contrib/vhost-user-scsi/vhost-user-scsi.c +++ b/contrib/vhost-user-scsi/vhost-user-scsi.c @@ -351,34 +351,59 @@ fail: /** vhost-user-scsi **/ +static int opt_fdnum = -1; +static char *opt_socket_path; +static gboolean opt_print_caps; +static char *iscsi_uri; + +static GOptionEntry entries[] = { + { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, + "Print capabilities", NULL }, + { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, + "Use inherited fd socket", "FDNUM" }, + { "iscsi-uri", 'i', 0, G_OPTION_ARG_FILENAME, &iscsi_uri, + "iSCSI URI to connect to", "FDNUM" }, + { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, + "Use UNIX socket path", "PATH" }, + { NULL, } +}; + int main(int argc, char **argv) { VusDev *vdev_scsi = NULL; - char *unix_fn = NULL; - char *iscsi_uri = NULL; - int lsock = -1, csock = -1, opt, err = EXIT_SUCCESS; + int lsock = -1, csock = -1, err = EXIT_SUCCESS; - while ((opt = getopt(argc, argv, "u:i:")) != -1) { - switch (opt) { - case 'h': - goto help; - case 'u': - unix_fn = g_strdup(optarg); - break; - case 'i': - iscsi_uri = g_strdup(optarg); - break; - default: - goto help; - } + GError *error = NULL; + GOptionContext *context; + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Option parsing failed: %s\n", error->message); + exit(EXIT_FAILURE); } - if (!unix_fn || !iscsi_uri) { + + if (opt_print_caps) { + g_print("{\n"); + g_print(" \"type\": \"scsi\"\n"); + g_print("}\n"); + goto out; + } + + if (!iscsi_uri) { goto help; } - lsock = unix_sock_new(unix_fn); - if (lsock < 0) { - goto err; + if (opt_socket_path) { + lsock = unix_sock_new(opt_socket_path); + if (lsock < 0) { + exit(EXIT_FAILURE); + } + } else if (opt_fdnum < 0) { + g_print("%s\n", g_option_context_get_help(context, true, NULL)); + exit(EXIT_FAILURE); + } else { + lsock = opt_fdnum; } csock = accept(lsock, NULL, NULL); @@ -408,7 +433,7 @@ out: if (vdev_scsi) { g_main_loop_unref(vdev_scsi->loop); g_free(vdev_scsi); - unlink(unix_fn); + unlink(opt_socket_path); } if (csock >= 0) { close(csock); @@ -416,7 +441,7 @@ out: if (lsock >= 0) { close(lsock); } - g_free(unix_fn); + g_free(opt_socket_path); g_free(iscsi_uri); return err; @@ -426,10 +451,12 @@ err: goto out; help: - fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n", + fprintf(stderr, "Usage: %s [ -s socket-path -i iscsi-uri -f fd -p print-capabilities ] | [ -h ]\n", argv[0]); - fprintf(stderr, " -u path to unix socket\n"); - fprintf(stderr, " -i iscsi uri for lun 0\n"); + fprintf(stderr, " -s, --socket-path=SOCKET_PATH path to unix socket\n"); + fprintf(stderr, " -i, --iscsi-uri=ISCSI_URI iscsi uri for lun 0\n"); + fprintf(stderr, " -f, --fd=FILE_DESCRIPTOR file-descriptor\n"); + fprintf(stderr, " -p, --print-capabilities=PRINT_CAPABILITIES denotes print-capabilities\n"); fprintf(stderr, " -h print help and quit\n"); goto err; diff --git a/cpu.c b/cpu.c index a460fe3ee9..8ddc606449 100644 --- a/cpu.c +++ b/cpu.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "exec/target_page.h" @@ -35,10 +34,13 @@ #include "sysemu/tcg.h" #include "sysemu/kvm.h" #include "sysemu/replay.h" +#include "exec/cpu-common.h" +#include "exec/exec-all.h" #include "exec/translate-all.h" #include "exec/log.h" #include "hw/core/accel-cpu.h" #include "trace/trace-root.h" +#include "qemu/accel.h" //// --- Begin LibAFL code --- @@ -454,6 +456,14 @@ const char *parse_cpu_option(const char *cpu_option) return cpu_type; } +void list_cpus(const char *optarg) +{ + /* XXX: implement xxx_cpu_list for targets that still miss it */ +#if defined(cpu_list) + cpu_list(); +#endif +} + #if defined(CONFIG_USER_ONLY) void tb_invalidate_phys_addr(target_ulong addr) { @@ -597,14 +607,14 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) fprintf(stderr, "\n"); cpu_dump_state(cpu, stderr, CPU_DUMP_FPU | CPU_DUMP_CCOP); if (qemu_log_separate()) { - FILE *logfile = qemu_log_lock(); - qemu_log("qemu: fatal: "); - qemu_log_vprintf(fmt, ap2); - qemu_log("\n"); - log_cpu_state(cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP); - qemu_log_flush(); - qemu_log_unlock(logfile); - qemu_log_close(); + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "qemu: fatal: "); + vfprintf(logfile, fmt, ap2); + fprintf(logfile, "\n"); + cpu_dump_state(cpu, logfile, CPU_DUMP_FPU | CPU_DUMP_CCOP); + qemu_log_unlock(logfile); + } } va_end(ap2); va_end(ap); @@ -623,11 +633,11 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) /* physical memory access (slow version, mainly for debug) */ #if defined(CONFIG_USER_ONLY) -int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - void *ptr, target_ulong len, bool is_write) +int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, + void *ptr, size_t len, bool is_write) { int flags; - target_ulong l, page; + vaddr l, page; void * p; uint8_t *buf = ptr; @@ -666,7 +676,7 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, bool target_words_bigendian(void) { -#if defined(TARGET_WORDS_BIGENDIAN) +#if TARGET_BIG_ENDIAN return true; #else return false; @@ -678,7 +688,7 @@ void page_size_init(void) /* NOTE: we can always suppose that qemu_host_page_size >= TARGET_PAGE_SIZE */ if (qemu_host_page_size == 0) { - qemu_host_page_size = qemu_real_host_page_size; + qemu_host_page_size = qemu_real_host_page_size(); } if (qemu_host_page_size < TARGET_PAGE_SIZE) { qemu_host_page_size = TARGET_PAGE_SIZE; diff --git a/cpus-common.c b/cpus-common.c index 6e73d3e58d..db459b41ce 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -160,7 +160,7 @@ void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) { struct qemu_work_item *wi; - wi = g_malloc0(sizeof(struct qemu_work_item)); + wi = g_new0(struct qemu_work_item, 1); wi->func = func; wi->data = data; wi->free = true; @@ -305,7 +305,7 @@ void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, { struct qemu_work_item *wi; - wi = g_malloc0(sizeof(struct qemu_work_item)); + wi = g_new0(struct qemu_work_item, 1); wi->func = func; wi->data = data; wi->free = true; diff --git a/crypto/cipher-afalg.c b/crypto/cipher-afalg.c index 052355a8a9..3df8fc54c0 100644 --- a/crypto/cipher-afalg.c +++ b/crypto/cipher-afalg.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" #include "qemu/sockets.h" -#include "qemu-common.h" #include "qapi/error.h" #include "crypto/cipher.h" #include "cipherpriv.h" @@ -84,8 +83,8 @@ qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgorithm alg, g_free(name); /* setkey */ - if (qemu_setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, key, - nkey) != 0) { + if (setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, key, + nkey) != 0) { error_setg_errno(errp, errno, "Set key failed"); qcrypto_afalg_comm_free(afalg); return NULL; diff --git a/crypto/hash-afalg.c b/crypto/hash-afalg.c index cf34c694af..3ebea39292 100644 --- a/crypto/hash-afalg.c +++ b/crypto/hash-afalg.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "qemu-common.h" #include "qapi/error.h" #include "crypto/hash.h" #include "crypto/hmac.h" @@ -88,8 +87,8 @@ qcrypto_afalg_hash_hmac_ctx_new(QCryptoHashAlgorithm alg, /* HMAC needs setkey */ if (is_hmac) { - if (qemu_setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, - key, nkey) != 0) { + if (setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, + key, nkey) != 0) { error_setg_errno(errp, errno, "Set hmac key failed"); qcrypto_afalg_comm_free(afalg); return NULL; diff --git a/crypto/meson.build b/crypto/meson.build index 19c44bea89..685fb37097 100644 --- a/crypto/meson.build +++ b/crypto/meson.build @@ -34,12 +34,15 @@ else crypto_ss.add(files('hash-glib.c', 'hmac-glib.c', 'pbkdf-stub.c')) endif -crypto_ss.add(when: 'CONFIG_SECRET_KEYRING', if_true: files('secret_keyring.c')) +if have_keyring + crypto_ss.add(files('secret_keyring.c')) +endif if have_afalg crypto_ss.add(if_true: files('afalg.c', 'cipher-afalg.c', 'hash-afalg.c')) endif crypto_ss.add(when: gnutls, if_true: files('tls-cipher-suites.c')) +util_ss.add(files('sm4.c')) util_ss.add(files('aes.c')) util_ss.add(files('init.c')) if gnutls.found() diff --git a/crypto/sm4.c b/crypto/sm4.c new file mode 100644 index 0000000000..9f0cd452c7 --- /dev/null +++ b/crypto/sm4.c @@ -0,0 +1,49 @@ +/* + * QEMU crypto sm4 support + * + * Copyright (C) 2013 - 2018 Linaro Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "crypto/sm4.h" + +uint8_t const sm4_sbox[] = { + 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, + 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, + 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, + 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, + 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, + 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62, + 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, + 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, + 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, + 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, + 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, + 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, + 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, + 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, + 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, + 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, + 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, + 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, + 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, + 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, + 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, + 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, + 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, + 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, + 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, + 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, + 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, + 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, + 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, + 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84, + 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, + 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48, +}; + diff --git a/crypto/tlssession.c b/crypto/tlssession.c index a8db8c76d1..b302d835d2 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -373,6 +373,12 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, session->hostname); goto error; } + } else { + if (session->creds->endpoint == + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, "No hostname for certificate validation"); + goto error; + } } } diff --git a/disas.c b/disas.c index 3dab4482d1..d41f34915d 100644 --- a/disas.c +++ b/disas.c @@ -126,7 +126,7 @@ static void initialize_debug_target(CPUDebug *s, CPUState *cpu) s->cpu = cpu; s->info.read_memory_func = target_read_memory; s->info.print_address_func = print_address; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN s->info.endian = BFD_ENDIAN_BIG; #else s->info.endian = BFD_ENDIAN_LITTLE; @@ -144,7 +144,7 @@ static void initialize_debug_host(CPUDebug *s) s->info.read_memory_func = host_read_memory; s->info.print_address_func = host_print_address; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN s->info.endian = BFD_ENDIAN_BIG; #else s->info.endian = BFD_ENDIAN_LITTLE; @@ -201,7 +201,6 @@ static void initialize_debug_host(CPUDebug *s) #elif defined(__m68k__) s->info.print_insn = print_insn_m68k; #elif defined(__s390__) - s->info.print_insn = print_insn_s390; s->info.cap_arch = CS_ARCH_SYSZ; s->info.cap_insn_unit = 2; s->info.cap_insn_split = 6; diff --git a/disas/meson.build b/disas/meson.build index 449f99e1de..e3e1b67923 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -16,7 +16,6 @@ common_ss.add(when: 'CONFIG_NANOMIPS_DIS', if_true: files('nanomips.cpp')) common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c')) common_ss.add(when: 'CONFIG_PPC_DIS', if_true: files('ppc.c')) common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files('riscv.c')) -common_ss.add(when: 'CONFIG_S390_DIS', if_true: files('s390.c')) common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c')) common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) diff --git a/disas/riscv.c b/disas/riscv.c index 03c8dc9961..7af6afc8fa 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -156,6 +156,8 @@ typedef enum { rv_codec_css_swsp, rv_codec_css_sdsp, rv_codec_css_sqsp, + rv_codec_k_bs, + rv_codec_k_rnum, } rv_codec; typedef enum { @@ -521,6 +523,43 @@ typedef enum { rv_op_bclr = 359, rv_op_binv = 360, rv_op_bext = 361, + rv_op_aes32esmi = 362, + rv_op_aes32esi = 363, + rv_op_aes32dsmi = 364, + rv_op_aes32dsi = 365, + rv_op_aes64ks1i = 366, + rv_op_aes64ks2 = 367, + rv_op_aes64im = 368, + rv_op_aes64esm = 369, + rv_op_aes64es = 370, + rv_op_aes64dsm = 371, + rv_op_aes64ds = 372, + rv_op_sha256sig0 = 373, + rv_op_sha256sig1 = 374, + rv_op_sha256sum0 = 375, + rv_op_sha256sum1 = 376, + rv_op_sha512sig0 = 377, + rv_op_sha512sig1 = 378, + rv_op_sha512sum0 = 379, + rv_op_sha512sum1 = 380, + rv_op_sha512sum0r = 381, + rv_op_sha512sum1r = 382, + rv_op_sha512sig0l = 383, + rv_op_sha512sig0h = 384, + rv_op_sha512sig1l = 385, + rv_op_sha512sig1h = 386, + rv_op_sm3p0 = 387, + rv_op_sm3p1 = 388, + rv_op_sm4ed = 389, + rv_op_sm4ks = 390, + rv_op_brev8 = 391, + rv_op_pack = 392, + rv_op_packh = 393, + rv_op_packw = 394, + rv_op_unzip = 395, + rv_op_zip = 396, + rv_op_xperm4 = 397, + rv_op_xperm8 = 398, } rv_op; /* structures */ @@ -540,6 +579,8 @@ typedef struct { uint8_t succ; uint8_t aq; uint8_t rl; + uint8_t bs; + uint8_t rnum; } rv_decode; typedef struct { @@ -615,6 +656,8 @@ static const char rv_freg_name_sym[32][5] = { #define rv_fmt_rd_rs2 "O\t0,2" #define rv_fmt_rs1_offset "O\t1,o" #define rv_fmt_rs2_offset "O\t2,o" +#define rv_fmt_rs1_rs2_bs "O\t1,2,b" +#define rv_fmt_rd_rs1_rnum "O\t0,1,n" /* pseudo-instruction constraints */ @@ -766,6 +809,7 @@ static const rv_comp_data rvcp_csrrw[] = { { rv_op_illegal, NULL } }; + static const rv_comp_data rvcp_csrrs[] = { { rv_op_rdcycle, rvcc_rdcycle }, { rv_op_rdtime, rvcc_rdtime }, @@ -1203,6 +1247,43 @@ const rv_opcode_data opcode_data[] = { { "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "binv", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "bext", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, + { "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, + { "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, + { "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, + { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum, NULL, 0, 0, 0 }, + { "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "aes64im", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha256sig0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "sha256sig1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "sha256sum0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "sha256sum1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "sm3p0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "sm3p1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, + { "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, + { "brev8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "pack", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "packh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "packw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "unzip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "zip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "xperm8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 } }; /* CSR names */ @@ -1216,6 +1297,7 @@ static const char *csr_name(int csrno) case 0x0003: return "fcsr"; case 0x0004: return "uie"; case 0x0005: return "utvec"; + case 0x0015: return "seed"; case 0x0040: return "uscratch"; case 0x0041: return "uepc"; case 0x0042: return "ucause"; @@ -1594,7 +1676,36 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: switch (((inst >> 27) & 0b11111)) { case 0b00000: op = rv_op_slli; break; + case 0b00001: + switch (((inst >> 20) & 0b1111111)) { + case 0b0001111: op = rv_op_zip; break; + } + break; + case 0b00010: + switch (((inst >> 20) & 0b1111111)) { + case 0b0000000: op = rv_op_sha256sum0; break; + case 0b0000001: op = rv_op_sha256sum1; break; + case 0b0000010: op = rv_op_sha256sig0; break; + case 0b0000011: op = rv_op_sha256sig1; break; + case 0b0000100: op = rv_op_sha512sum0; break; + case 0b0000101: op = rv_op_sha512sum1; break; + case 0b0000110: op = rv_op_sha512sig0; break; + case 0b0000111: op = rv_op_sha512sig1; break; + case 0b0001000: op = rv_op_sm3p0; break; + case 0b0001001: op = rv_op_sm3p1; break; + } + break; case 0b00101: op = rv_op_bseti; break; + case 0b00110: + switch (((inst >> 20) & 0b1111111)) { + case 0b0000000: op = rv_op_aes64im; break; + default: + if (((inst >> 24) & 0b0111) == 0b001) { + op = rv_op_aes64ks1i; + } + break; + } + break; case 0b01001: op = rv_op_bclri; break; case 0b01101: op = rv_op_binvi; break; case 0b01100: @@ -1615,13 +1726,20 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 5: switch (((inst >> 27) & 0b11111)) { case 0b00000: op = rv_op_srli; break; + case 0b00001: + switch (((inst >> 20) & 0b1111111)) { + case 0b0001111: op = rv_op_unzip; break; + } + break; case 0b00101: op = rv_op_orc_b; break; case 0b01000: op = rv_op_srai; break; case 0b01001: op = rv_op_bexti; break; case 0b01100: op = rv_op_rori; break; case 0b01101: switch ((inst >> 20) & 0b1111111) { + case 0b0011000: op = rv_op_rev8; break; case 0b0111000: op = rv_op_rev8; break; + case 0b0000111: op = rv_op_brev8; break; } break; } @@ -1742,8 +1860,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 36: switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_zext_h; break; + default: op = rv_op_pack; break; } break; + case 39: op = rv_op_packh; break; + case 41: op = rv_op_clmul; break; case 42: op = rv_op_clmulr; break; case 43: op = rv_op_clmulh; break; @@ -1755,6 +1876,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 132: op = rv_op_sh2add; break; case 134: op = rv_op_sh3add; break; case 161: op = rv_op_bset; break; + case 162: op = rv_op_xperm4; break; + case 164: op = rv_op_xperm8; break; + case 200: op = rv_op_aes64es; break; + case 216: op = rv_op_aes64esm; break; + case 232: op = rv_op_aes64ds; break; + case 248: op = rv_op_aes64dsm; break; case 256: op = rv_op_sub; break; case 260: op = rv_op_xnor; break; case 261: op = rv_op_sra; break; @@ -1762,9 +1889,24 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 263: op = rv_op_andn; break; case 289: op = rv_op_bclr; break; case 293: op = rv_op_bext; break; + case 320: op = rv_op_sha512sum0r; break; + case 328: op = rv_op_sha512sum1r; break; + case 336: op = rv_op_sha512sig0l; break; + case 344: op = rv_op_sha512sig1l; break; + case 368: op = rv_op_sha512sig0h; break; + case 376: op = rv_op_sha512sig1h; break; case 385: op = rv_op_rol; break; - case 386: op = rv_op_ror; break; + case 389: op = rv_op_ror; break; case 417: op = rv_op_binv; break; + case 504: op = rv_op_aes64ks2; break; + } + switch ((inst >> 25) & 0b0011111) { + case 17: op = rv_op_aes32esi; break; + case 19: op = rv_op_aes32esmi; break; + case 21: op = rv_op_aes32dsi; break; + case 23: op = rv_op_aes32dsmi; break; + case 24: op = rv_op_sm4ed; break; + case 26: op = rv_op_sm4ks; break; } break; case 13: op = rv_op_lui; break; @@ -1782,6 +1924,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 36: switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_zext_h; break; + default: op = rv_op_packw; break; } break; case 130: op = rv_op_sh1add_uw; break; @@ -2374,6 +2517,16 @@ static uint32_t operand_cimmq(rv_inst inst) ((inst << 57) >> 62) << 6; } +static uint32_t operand_bs(rv_inst inst) +{ + return (inst << 32) >> 62; +} + +static uint32_t operand_rnum(rv_inst inst) +{ + return (inst << 40) >> 60; +} + /* decode operands */ static void decode_inst_operands(rv_decode *dec) @@ -2653,6 +2806,16 @@ static void decode_inst_operands(rv_decode *dec) dec->rs2 = operand_crs2(inst); dec->imm = operand_cimmsqsp(inst); break; + case rv_codec_k_bs: + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->bs = operand_bs(inst); + break; + case rv_codec_k_rnum: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rnum = operand_rnum(inst); + break; }; } @@ -2812,6 +2975,14 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) case ')': append(buf, ")", buflen); break; + case 'b': + snprintf(tmp, sizeof(tmp), "%d", dec->bs); + append(buf, tmp, buflen); + break; + case 'n': + snprintf(tmp, sizeof(tmp), "%d", dec->rnum); + append(buf, tmp, buflen); + break; case '0': append(buf, rv_ireg_name_sym[dec->rd], buflen); break; diff --git a/disas/s390.c b/disas/s390.c deleted file mode 100644 index a9ec8fa593..0000000000 --- a/disas/s390.c +++ /dev/null @@ -1,1892 +0,0 @@ -/* opcodes/s390-dis.c revision 1.12 */ -/* s390-dis.c -- Disassemble S390 instructions - Copyright 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. - Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). - - This file is part of GDB, GAS and the GNU binutils. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ - -#include "qemu/osdep.h" -#include "disas/dis-asm.h" - -/* include/opcode/s390.h revision 1.9 */ -/* s390.h -- Header file for S390 opcode table - Copyright 2000, 2001, 2003 Free Software Foundation, Inc. - Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). - - This file is part of BFD, the Binary File Descriptor library. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ - -#ifndef S390_H -#define S390_H - -/* List of instruction sets variations. */ - -enum s390_opcode_mode_val - { - S390_OPCODE_ESA = 0, - S390_OPCODE_ZARCH - }; - -enum s390_opcode_cpu_val - { - S390_OPCODE_G5 = 0, - S390_OPCODE_G6, - S390_OPCODE_Z900, - S390_OPCODE_Z990, - S390_OPCODE_Z9_109, - S390_OPCODE_Z9_EC, - S390_OPCODE_Z10 - }; - -/* The opcode table is an array of struct s390_opcode. */ - -struct s390_opcode - { - /* The opcode name. */ - const char * name; - - /* The opcode itself. Those bits which will be filled in with - operands are zeroes. */ - unsigned char opcode[6]; - - /* The opcode mask. This is used by the disassembler. This is a - mask containing ones indicating those bits which must match the - opcode field, and zeroes indicating those bits which need not - match (and are presumably filled in by operands). */ - unsigned char mask[6]; - - /* The opcode length in bytes. */ - int oplen; - - /* An array of operand codes. Each code is an index into the - operand table. They appear in the order which the operands must - appear in assembly code, and are terminated by a zero. */ - unsigned char operands[6]; - - /* Bitmask of execution modes this opcode is available for. */ - unsigned int modes; - - /* First cpu this opcode is available for. */ - enum s390_opcode_cpu_val min_cpu; - }; - -/* The table itself is sorted by major opcode number, and is otherwise - in the order in which the disassembler should consider - instructions. */ -/* QEMU: Mark these static. */ -static const struct s390_opcode s390_opcodes[]; -static const int s390_num_opcodes; - -/* Values defined for the flags field of a struct powerpc_opcode. */ - -/* The operands table is an array of struct s390_operand. */ - -struct s390_operand - { - /* The number of bits in the operand. */ - int bits; - - /* How far the operand is left shifted in the instruction. */ - int shift; - - /* One bit syntax flags. */ - unsigned long flags; - }; - -/* Elements in the table are retrieved by indexing with values from - the operands field of the powerpc_opcodes table. */ - -static const struct s390_operand s390_operands[]; - -/* Values defined for the flags field of a struct s390_operand. */ - -/* This operand names a register. The disassembler uses this to print - register names with a leading 'r'. */ -#define S390_OPERAND_GPR 0x1 - -/* This operand names a floating point register. The disassembler - prints these with a leading 'f'. */ -#define S390_OPERAND_FPR 0x2 - -/* This operand names an access register. The disassembler - prints these with a leading 'a'. */ -#define S390_OPERAND_AR 0x4 - -/* This operand names a control register. The disassembler - prints these with a leading 'c'. */ -#define S390_OPERAND_CR 0x8 - -/* This operand is a displacement. */ -#define S390_OPERAND_DISP 0x10 - -/* This operand names a base register. */ -#define S390_OPERAND_BASE 0x20 - -/* This operand names an index register, it can be skipped. */ -#define S390_OPERAND_INDEX 0x40 - -/* This operand is a relative branch displacement. The disassembler - prints these symbolically if possible. */ -#define S390_OPERAND_PCREL 0x80 - -/* This operand takes signed values. */ -#define S390_OPERAND_SIGNED 0x100 - -/* This operand is a length. */ -#define S390_OPERAND_LENGTH 0x200 - -/* This operand is optional. Only a single operand at the end of - the instruction may be optional. */ -#define S390_OPERAND_OPTIONAL 0x400 - -/* QEMU-ADD */ -/* ??? Not quite the format the assembler takes, but easy to implement - without recourse to the table generator. */ -#define S390_OPERAND_CCODE 0x800 - -static const char s390_ccode_name[16][4] = { - "n", /* 0000 */ - "o", /* 0001 */ - "h", /* 0010 */ - "nle", /* 0011 */ - "l", /* 0100 */ - "nhe", /* 0101 */ - "lh", /* 0110 */ - "ne", /* 0111 */ - "e", /* 1000 */ - "nlh", /* 1001 */ - "he", /* 1010 */ - "nl", /* 1011 */ - "le", /* 1100 */ - "nh", /* 1101 */ - "no", /* 1110 */ - "a" /* 1111 */ -}; -/* QEMU-END */ - -#endif /* S390_H */ - -static int init_flag = 0; -static int opc_index[256]; - -/* QEMU: We've disabled the architecture check below. */ -/* static int current_arch_mask = 0; */ - -/* Set up index table for first opcode byte. */ - -static void -init_disasm (struct disassemble_info *info) -{ - int i; - - memset (opc_index, 0, sizeof (opc_index)); - - /* Reverse order, such that each opc_index ends up pointing to the - first matching entry instead of the last. */ - for (i = s390_num_opcodes; i--; ) - opc_index[s390_opcodes[i].opcode[0]] = i; - -#ifdef QEMU_DISABLE - switch (info->mach) - { - case bfd_mach_s390_31: - current_arch_mask = 1 << S390_OPCODE_ESA; - break; - case bfd_mach_s390_64: - current_arch_mask = 1 << S390_OPCODE_ZARCH; - break; - default: - abort (); - } -#endif /* QEMU_DISABLE */ - - init_flag = 1; -} - -/* Extracts an operand value from an instruction. */ - -static inline unsigned int -s390_extract_operand (unsigned char *insn, const struct s390_operand *operand) -{ - unsigned int val; - int bits; - - /* Extract fragments of the operand byte for byte. */ - insn += operand->shift / 8; - bits = (operand->shift & 7) + operand->bits; - val = 0; - do - { - val <<= 8; - val |= (unsigned int) *insn++; - bits -= 8; - } - while (bits > 0); - val >>= -bits; - val &= ((1U << (operand->bits - 1)) << 1) - 1; - - /* Check for special long displacement case. */ - if (operand->bits == 20 && operand->shift == 20) - val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; - - /* Sign extend value if the operand is signed or pc relative. */ - if ((operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL)) - && (val & (1U << (operand->bits - 1)))) - val |= (-1U << (operand->bits - 1)) << 1; - - /* Double value if the operand is pc relative. */ - if (operand->flags & S390_OPERAND_PCREL) - val <<= 1; - - /* Length x in an instructions has real length x + 1. */ - if (operand->flags & S390_OPERAND_LENGTH) - val++; - return val; -} - -/* Print a S390 instruction. */ - -int -print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info) -{ - bfd_byte buffer[6]; - const struct s390_opcode *opcode; - const struct s390_opcode *opcode_end; - unsigned int value; - int status, opsize, bufsize; - char separator; - - if (init_flag == 0) - init_disasm (info); - - /* The output looks better if we put 6 bytes on a line. */ - info->bytes_per_line = 6; - - /* Every S390 instruction is max 6 bytes long. */ - memset (buffer, 0, 6); - status = (*info->read_memory_func) (memaddr, buffer, 6, info); - if (status != 0) - { - for (bufsize = 0; bufsize < 6; bufsize++) - if ((*info->read_memory_func) (memaddr, buffer, bufsize + 1, info) != 0) - break; - if (bufsize <= 0) - { - (*info->memory_error_func) (status, memaddr, info); - return -1; - } - /* Opsize calculation looks strange but it works - 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes, - 11xxxxxx -> 6 bytes. */ - opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; - status = opsize > bufsize; - } - else - { - bufsize = 6; - opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; - } - - if (status == 0) - { - /* Find the first match in the opcode table. */ - opcode_end = s390_opcodes + s390_num_opcodes; - for (opcode = s390_opcodes + opc_index[(int) buffer[0]]; - (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]); - opcode++) - { - const struct s390_operand *operand; - const unsigned char *opindex; - -#ifdef QEMU_DISABLE - /* Check architecture. */ - if (!(opcode->modes & current_arch_mask)) - continue; -#endif /* QEMU_DISABLE */ - - /* Check signature of the opcode. */ - if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1] - || (buffer[2] & opcode->mask[2]) != opcode->opcode[2] - || (buffer[3] & opcode->mask[3]) != opcode->opcode[3] - || (buffer[4] & opcode->mask[4]) != opcode->opcode[4] - || (buffer[5] & opcode->mask[5]) != opcode->opcode[5]) - continue; - - /* The instruction is valid. */ -/* QEMU-MOD */ - (*info->fprintf_func) (info->stream, "%s", opcode->name); - - if (s390_operands[opcode->operands[0]].flags & S390_OPERAND_CCODE) - separator = 0; - else - separator = '\t'; -/* QEMU-END */ - - /* Extract the operands. */ - for (opindex = opcode->operands; *opindex != 0; opindex++) - { - unsigned int value; - - operand = s390_operands + *opindex; - value = s390_extract_operand (buffer, operand); - - if ((operand->flags & S390_OPERAND_INDEX) && value == 0) - continue; - if ((operand->flags & S390_OPERAND_BASE) && - value == 0 && separator == '(') - { - separator = ','; - continue; - } - - if (separator) - (*info->fprintf_func) (info->stream, "%c", separator); - - if (operand->flags & S390_OPERAND_GPR) - (*info->fprintf_func) (info->stream, "%%r%i", value); - else if (operand->flags & S390_OPERAND_FPR) - (*info->fprintf_func) (info->stream, "%%f%i", value); - else if (operand->flags & S390_OPERAND_AR) - (*info->fprintf_func) (info->stream, "%%a%i", value); - else if (operand->flags & S390_OPERAND_CR) - (*info->fprintf_func) (info->stream, "%%c%i", value); - else if (operand->flags & S390_OPERAND_PCREL) - (*info->print_address_func) (memaddr + (int) value, info); - else if (operand->flags & S390_OPERAND_SIGNED) - (*info->fprintf_func) (info->stream, "%i", (int) value); -/* QEMU-ADD */ - else if (operand->flags & S390_OPERAND_CCODE) - { - (*info->fprintf_func) (info->stream, "%s", - s390_ccode_name[(int) value]); - separator = '\t'; - continue; - } -/* QEMU-END */ - else - (*info->fprintf_func) (info->stream, "%u", value); - - if (operand->flags & S390_OPERAND_DISP) - { - separator = '('; - } - else if (operand->flags & S390_OPERAND_BASE) - { - (*info->fprintf_func) (info->stream, ")"); - separator = ','; - } - else - separator = ','; - } - - /* Found instruction, printed it, return its size. */ - return opsize; - } - /* No matching instruction found, fall through to hex print. */ - } - - if (bufsize >= 4) - { - value = (unsigned int) buffer[0]; - value = (value << 8) + (unsigned int) buffer[1]; - value = (value << 8) + (unsigned int) buffer[2]; - value = (value << 8) + (unsigned int) buffer[3]; - (*info->fprintf_func) (info->stream, ".long\t0x%08x", value); - return 4; - } - else if (bufsize >= 2) - { - value = (unsigned int) buffer[0]; - value = (value << 8) + (unsigned int) buffer[1]; - (*info->fprintf_func) (info->stream, ".short\t0x%04x", value); - return 2; - } - else - { - value = (unsigned int) buffer[0]; - (*info->fprintf_func) (info->stream, ".byte\t0x%02x", value); - return 1; - } -} - -/* opcodes/s390-opc.c revision 1.16 */ -/* s390-opc.c -- S390 opcode list - Copyright 2000, 2001, 2003 Free Software Foundation, Inc. - Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). - - This file is part of GDB, GAS, and the GNU binutils. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ - -/* This file holds the S390 opcode table. The opcode table - includes almost all of the extended instruction mnemonics. This - permits the disassembler to use them, and simplifies the assembler - logic, at the cost of increasing the table size. The table is - strictly constant data, so the compiler should be able to put it in - the .text section. - - This file also holds the operand table. All knowledge about - inserting operands into instructions and vice-versa is kept in this - file. */ - -/* The operands table. - The fields are bits, shift, insert, extract, flags. */ - -static const struct s390_operand s390_operands[] = -{ -#define UNUSED 0 - { 0, 0, 0 }, /* Indicates the end of the operand list */ - -#define R_8 1 /* GPR starting at position 8 */ - { 4, 8, S390_OPERAND_GPR }, -#define R_12 2 /* GPR starting at position 12 */ - { 4, 12, S390_OPERAND_GPR }, -#define R_16 3 /* GPR starting at position 16 */ - { 4, 16, S390_OPERAND_GPR }, -#define R_20 4 /* GPR starting at position 20 */ - { 4, 20, S390_OPERAND_GPR }, -#define R_24 5 /* GPR starting at position 24 */ - { 4, 24, S390_OPERAND_GPR }, -#define R_28 6 /* GPR starting at position 28 */ - { 4, 28, S390_OPERAND_GPR }, -#define R_32 7 /* GPR starting at position 32 */ - { 4, 32, S390_OPERAND_GPR }, - -#define F_8 8 /* FPR starting at position 8 */ - { 4, 8, S390_OPERAND_FPR }, -#define F_12 9 /* FPR starting at position 12 */ - { 4, 12, S390_OPERAND_FPR }, -#define F_16 10 /* FPR starting at position 16 */ - { 4, 16, S390_OPERAND_FPR }, -#define F_20 11 /* FPR starting at position 16 */ - { 4, 16, S390_OPERAND_FPR }, -#define F_24 12 /* FPR starting at position 24 */ - { 4, 24, S390_OPERAND_FPR }, -#define F_28 13 /* FPR starting at position 28 */ - { 4, 28, S390_OPERAND_FPR }, -#define F_32 14 /* FPR starting at position 32 */ - { 4, 32, S390_OPERAND_FPR }, - -#define A_8 15 /* Access reg. starting at position 8 */ - { 4, 8, S390_OPERAND_AR }, -#define A_12 16 /* Access reg. starting at position 12 */ - { 4, 12, S390_OPERAND_AR }, -#define A_24 17 /* Access reg. starting at position 24 */ - { 4, 24, S390_OPERAND_AR }, -#define A_28 18 /* Access reg. starting at position 28 */ - { 4, 28, S390_OPERAND_AR }, - -#define C_8 19 /* Control reg. starting at position 8 */ - { 4, 8, S390_OPERAND_CR }, -#define C_12 20 /* Control reg. starting at position 12 */ - { 4, 12, S390_OPERAND_CR }, - -#define B_16 21 /* Base register starting at position 16 */ - { 4, 16, S390_OPERAND_BASE|S390_OPERAND_GPR }, -#define B_32 22 /* Base register starting at position 32 */ - { 4, 32, S390_OPERAND_BASE|S390_OPERAND_GPR }, - -#define X_12 23 /* Index register starting at position 12 */ - { 4, 12, S390_OPERAND_INDEX|S390_OPERAND_GPR }, - -#define D_20 24 /* Displacement starting at position 20 */ - { 12, 20, S390_OPERAND_DISP }, -#define D_36 25 /* Displacement starting at position 36 */ - { 12, 36, S390_OPERAND_DISP }, -#define D20_20 26 /* 20 bit displacement starting at 20 */ - { 20, 20, S390_OPERAND_DISP|S390_OPERAND_SIGNED }, - -#define L4_8 27 /* 4 bit length starting at position 8 */ - { 4, 8, S390_OPERAND_LENGTH }, -#define L4_12 28 /* 4 bit length starting at position 12 */ - { 4, 12, S390_OPERAND_LENGTH }, -#define L8_8 29 /* 8 bit length starting at position 8 */ - { 8, 8, S390_OPERAND_LENGTH }, - -#define U4_8 30 /* 4 bit unsigned value starting at 8 */ - { 4, 8, 0 }, -#define U4_12 31 /* 4 bit unsigned value starting at 12 */ - { 4, 12, 0 }, -#define U4_16 32 /* 4 bit unsigned value starting at 16 */ - { 4, 16, 0 }, -#define U4_20 33 /* 4 bit unsigned value starting at 20 */ - { 4, 20, 0 }, -#define U8_8 34 /* 8 bit unsigned value starting at 8 */ - { 8, 8, 0 }, -#define U8_16 35 /* 8 bit unsigned value starting at 16 */ - { 8, 16, 0 }, -#define I16_16 36 /* 16 bit signed value starting at 16 */ - { 16, 16, S390_OPERAND_SIGNED }, -#define U16_16 37 /* 16 bit unsigned value starting at 16 */ - { 16, 16, 0 }, -#define J16_16 38 /* PC relative jump offset at 16 */ - { 16, 16, S390_OPERAND_PCREL }, -#define J32_16 39 /* PC relative long offset at 16 */ - { 32, 16, S390_OPERAND_PCREL }, -#define I32_16 40 /* 32 bit signed value starting at 16 */ - { 32, 16, S390_OPERAND_SIGNED }, -#define U32_16 41 /* 32 bit unsigned value starting at 16 */ - { 32, 16, 0 }, -#define M_16 42 /* 4 bit optional mask starting at 16 */ - { 4, 16, S390_OPERAND_OPTIONAL }, -#define RO_28 43 /* optional GPR starting at position 28 */ - { 4, 28, (S390_OPERAND_GPR | S390_OPERAND_OPTIONAL) }, - -/* QEMU-ADD: */ -#define M4_12 44 /* 4-bit condition-code starting at 12 */ - { 4, 12, S390_OPERAND_CCODE }, -#define M4_32 45 /* 4-bit condition-code starting at 32 */ - { 4, 32, S390_OPERAND_CCODE }, -#define I8_32 46 /* 8 bit signed value starting at 32 */ - { 8, 32, S390_OPERAND_SIGNED }, -#define U8_24 47 /* 8 bit unsigned value starting at 24 */ - { 8, 24, 0 }, -#define U8_32 48 /* 8 bit unsigned value starting at 32 */ - { 8, 32, 0 }, -#define I16_32 49 - { 16, 32, S390_OPERAND_SIGNED }, -#define M4_16 50 /* 4-bit condition-code starting at 12 */ - { 4, 16, S390_OPERAND_CCODE }, -#define I8_16 51 - { 8, 16, S390_OPERAND_SIGNED }, -/* QEMU-END */ -}; - - -/* Macros used to form opcodes. */ - -/* 8/16/48 bit opcodes. */ -#define OP8(x) { x, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define OP16(x) { x >> 8, x & 255, 0x00, 0x00, 0x00, 0x00 } -#define OP48(x) { x >> 40, (x >> 32) & 255, (x >> 24) & 255, \ - (x >> 16) & 255, (x >> 8) & 255, x & 255} - -/* The new format of the INSTR_x_y and MASK_x_y defines is based - on the following rules: - 1) the middle part of the definition (x in INSTR_x_y) is the official - names of the instruction format that you can find in the principals - of operation. - 2) the last part of the definition (y in INSTR_x_y) gives you an idea - which operands the binary representation of the instruction has. - The meanings of the letters in y are: - a - access register - c - control register - d - displacement, 12 bit - f - floating pointer register - i - signed integer, 4, 8, 16 or 32 bit - l - length, 4 or 8 bit - p - pc relative - r - general purpose register - u - unsigned integer, 4, 8, 16 or 32 bit - m - mode field, 4 bit - 0 - operand skipped. - The order of the letters reflects the layout of the format in - storage and not the order of the parameters of the instructions. - The use of the letters is not a 100% match with the PoP but it is - quite close. - - For example the instruction "mvo" is defined in the PoP as follows: - - MVO D1(L1,B1),D2(L2,B2) [SS] - - -------------------------------------- - | 'F1' | L1 | L2 | B1 | D1 | B2 | D2 | - -------------------------------------- - 0 8 12 16 20 32 36 - - The instruction format is: INSTR_SS_LLRDRD / MASK_SS_LLRDRD. */ - -#define INSTR_E 2, { 0,0,0,0,0,0 } /* e.g. pr */ -#define INSTR_RIE_RRP 6, { R_8,R_12,J16_16,0,0,0 } /* e.g. brxhg */ -#define INSTR_RIL_0P 6, { J32_16,0,0,0,0 } /* e.g. jg */ -#define INSTR_RIL_RP 6, { R_8,J32_16,0,0,0,0 } /* e.g. brasl */ -#define INSTR_RIL_UP 6, { U4_8,J32_16,0,0,0,0 } /* e.g. brcl */ -#define INSTR_RIL_RI 6, { R_8,I32_16,0,0,0,0 } /* e.g. afi */ -#define INSTR_RIL_RU 6, { R_8,U32_16,0,0,0,0 } /* e.g. alfi */ -#define INSTR_RI_0P 4, { J16_16,0,0,0,0,0 } /* e.g. j */ -#define INSTR_RI_RI 4, { R_8,I16_16,0,0,0,0 } /* e.g. ahi */ -#define INSTR_RI_RP 4, { R_8,J16_16,0,0,0,0 } /* e.g. brct */ -#define INSTR_RI_RU 4, { R_8,U16_16,0,0,0,0 } /* e.g. tml */ -#define INSTR_RI_UP 4, { U4_8,J16_16,0,0,0,0 } /* e.g. brc */ -#define INSTR_RRE_00 4, { 0,0,0,0,0,0 } /* e.g. palb */ -#define INSTR_RRE_0R 4, { R_28,0,0,0,0,0 } /* e.g. tb */ -#define INSTR_RRE_AA 4, { A_24,A_28,0,0,0,0 } /* e.g. cpya */ -#define INSTR_RRE_AR 4, { A_24,R_28,0,0,0,0 } /* e.g. sar */ -#define INSTR_RRE_F0 4, { F_24,0,0,0,0,0 } /* e.g. sqer */ -#define INSTR_RRE_FF 4, { F_24,F_28,0,0,0,0 } /* e.g. debr */ -#define INSTR_RRE_R0 4, { R_24,0,0,0,0,0 } /* e.g. ipm */ -#define INSTR_RRE_RA 4, { R_24,A_28,0,0,0,0 } /* e.g. ear */ -#define INSTR_RRE_RF 4, { R_24,F_28,0,0,0,0 } /* e.g. cefbr */ -#define INSTR_RRE_RR 4, { R_24,R_28,0,0,0,0 } /* e.g. lura */ -#define INSTR_RRE_FR 4, { F_24,R_28,0,0,0,0 } /* e.g. ldgr */ -/* Actually efpc and sfpc do not take an optional operand. - This is just a workaround for existing code e.g. glibc. */ -#define INSTR_RRE_RR_OPT 4, { R_24,RO_28,0,0,0,0 } /* efpc, sfpc */ -#define INSTR_RRF_F0FF 4, { F_16,F_24,F_28,0,0,0 } /* e.g. madbr */ -/* QEMU-MOD */ -#define INSTR_RRF_F0FF2 4, { F_24,F_28,F_16,0,0,0 } /* e.g. cpsdr */ -/* QEMU-END */ -#define INSTR_RRF_F0FR 4, { F_24,F_16,R_28,0,0,0 } /* e.g. iedtr */ -#define INSTR_RRF_FUFF 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. didbr */ -#define INSTR_RRF_RURR 4, { R_24,R_28,R_16,U4_20,0,0 } /* e.g. .insn */ -#define INSTR_RRF_R0RR 4, { R_24,R_28,R_16,0,0,0 } /* e.g. idte */ -#define INSTR_RRF_U0FF 4, { F_24,U4_16,F_28,0,0,0 } /* e.g. fixr */ -#define INSTR_RRF_U0RF 4, { R_24,U4_16,F_28,0,0,0 } /* e.g. cfebr */ -#define INSTR_RRF_UUFF 4, { F_24,U4_16,F_28,U4_20,0,0 } /* e.g. fidtr */ -#define INSTR_RRF_0UFF 4, { F_24,F_28,U4_20,0,0,0 } /* e.g. ldetr */ -#define INSTR_RRF_FFFU 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. qadtr */ -#define INSTR_RRF_M0RR 4, { R_24,R_28,M_16,0,0,0 } /* e.g. sske */ -#define INSTR_RR_0R 2, { R_12, 0,0,0,0,0 } /* e.g. br */ -#define INSTR_RR_FF 2, { F_8,F_12,0,0,0,0 } /* e.g. adr */ -#define INSTR_RR_R0 2, { R_8, 0,0,0,0,0 } /* e.g. spm */ -#define INSTR_RR_RR 2, { R_8,R_12,0,0,0,0 } /* e.g. lr */ -#define INSTR_RR_U0 2, { U8_8, 0,0,0,0,0 } /* e.g. svc */ -#define INSTR_RR_UR 2, { U4_8,R_12,0,0,0,0 } /* e.g. bcr */ -#define INSTR_RRR_F0FF 4, { F_24,F_28,F_16,0,0,0 } /* e.g. ddtr */ -#define INSTR_RSE_RRRD 6, { R_8,R_12,D_20,B_16,0,0 } /* e.g. lmh */ -#define INSTR_RSE_CCRD 6, { C_8,C_12,D_20,B_16,0,0 } /* e.g. lmh */ -#define INSTR_RSE_RURD 6, { R_8,U4_12,D_20,B_16,0,0 } /* e.g. icmh */ -#define INSTR_RSL_R0RD 6, { R_8,D_20,B_16,0,0,0 } /* e.g. tp */ -#define INSTR_RSI_RRP 4, { R_8,R_12,J16_16,0,0,0 } /* e.g. brxh */ -#define INSTR_RSY_RRRD 6, { R_8,R_12,D20_20,B_16,0,0 } /* e.g. stmy */ -#define INSTR_RSY_RURD 6, { R_8,U4_12,D20_20,B_16,0,0 } /* e.g. icmh */ -#define INSTR_RSY_AARD 6, { A_8,A_12,D20_20,B_16,0,0 } /* e.g. lamy */ -#define INSTR_RSY_CCRD 6, { C_8,C_12,D20_20,B_16,0,0 } /* e.g. lamy */ -#define INSTR_RS_AARD 4, { A_8,A_12,D_20,B_16,0,0 } /* e.g. lam */ -#define INSTR_RS_CCRD 4, { C_8,C_12,D_20,B_16,0,0 } /* e.g. lctl */ -#define INSTR_RS_R0RD 4, { R_8,D_20,B_16,0,0,0 } /* e.g. sll */ -#define INSTR_RS_RRRD 4, { R_8,R_12,D_20,B_16,0,0 } /* e.g. cs */ -#define INSTR_RS_RURD 4, { R_8,U4_12,D_20,B_16,0,0 } /* e.g. icm */ -#define INSTR_RXE_FRRD 6, { F_8,D_20,X_12,B_16,0,0 } /* e.g. axbr */ -#define INSTR_RXE_RRRD 6, { R_8,D_20,X_12,B_16,0,0 } /* e.g. lg */ -#define INSTR_RXF_FRRDF 6, { F_32,F_8,D_20,X_12,B_16,0 } /* e.g. madb */ -#define INSTR_RXF_RRRDR 6, { R_32,R_8,D_20,X_12,B_16,0 } /* e.g. .insn */ -#define INSTR_RXY_RRRD 6, { R_8,D20_20,X_12,B_16,0,0 } /* e.g. ly */ -#define INSTR_RXY_FRRD 6, { F_8,D20_20,X_12,B_16,0,0 } /* e.g. ley */ -#define INSTR_RX_0RRD 4, { D_20,X_12,B_16,0,0,0 } /* e.g. be */ -#define INSTR_RX_FRRD 4, { F_8,D_20,X_12,B_16,0,0 } /* e.g. ae */ -#define INSTR_RX_RRRD 4, { R_8,D_20,X_12,B_16,0,0 } /* e.g. l */ -#define INSTR_RX_URRD 4, { U4_8,D_20,X_12,B_16,0,0 } /* e.g. bc */ -#define INSTR_SI_URD 4, { D_20,B_16,U8_8,0,0,0 } /* e.g. cli */ -#define INSTR_SIY_URD 6, { D20_20,B_16,U8_8,0,0,0 } /* e.g. tmy */ -#define INSTR_SSE_RDRD 6, { D_20,B_16,D_36,B_32,0,0 } /* e.g. mvsdk */ -#define INSTR_SS_L0RDRD 6, { D_20,L8_8,B_16,D_36,B_32,0 } /* e.g. mvc */ -#define INSTR_SS_L2RDRD 6, { D_20,B_16,D_36,L8_8,B_32,0 } /* e.g. pka */ -#define INSTR_SS_LIRDRD 6, { D_20,L4_8,B_16,D_36,B_32,U4_12 } /* e.g. srp */ -#define INSTR_SS_LLRDRD 6, { D_20,L4_8,B_16,D_36,L4_12,B_32 } /* e.g. pack */ -#define INSTR_SS_RRRDRD 6, { D_20,R_8,B_16,D_36,B_32,R_12 } /* e.g. mvck */ -#define INSTR_SS_RRRDRD2 6, { R_8,D_20,B_16,R_12,D_36,B_32 } /* e.g. plo */ -#define INSTR_SS_RRRDRD3 6, { R_8,R_12,D_20,B_16,D_36,B_32 } /* e.g. lmd */ -#define INSTR_S_00 4, { 0,0,0,0,0,0 } /* e.g. hsch */ -#define INSTR_S_RD 4, { D_20,B_16,0,0,0,0 } /* e.g. lpsw */ -#define INSTR_SSF_RRDRD 6, { D_20,B_16,D_36,B_32,R_8,0 } /* e.g. mvcos */ - -#define MASK_E { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RIE_RRP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RIL_0P { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RIL_RP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RIL_UP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RIL_RI { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RIL_RU { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RI_0P { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RI_RI { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RI_RP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RI_RU { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RI_UP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RRE_00 { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } -#define MASK_RRE_0R { 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 } -#define MASK_RRE_AA { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRE_AR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRE_F0 { 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00 } -#define MASK_RRE_FF { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRE_R0 { 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00 } -#define MASK_RRE_RA { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRE_RF { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRE_RR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRE_FR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRE_RR_OPT { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } -#define MASK_RRF_F0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } -#define MASK_RRF_F0FF2 { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } -#define MASK_RRF_F0FR { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } -#define MASK_RRF_FUFF { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RRF_RURR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RRF_R0RR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RRF_U0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } -#define MASK_RRF_U0RF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } -#define MASK_RRF_UUFF { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RRF_0UFF { 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 } -#define MASK_RRF_FFFU { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RRF_M0RR { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } -#define MASK_RR_0R { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RR_FF { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RR_R0 { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RR_RR { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RR_U0 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RR_UR { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RRR_F0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 } -#define MASK_RSE_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RSE_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RSE_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RSL_R0RD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RSI_RRP { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RS_AARD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RS_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RS_R0RD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RS_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RS_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RSY_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RSY_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RSY_AARD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RSY_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RXE_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RXE_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RXF_FRRDF { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RXF_RRRDR { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RXY_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RXY_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_RX_0RRD { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RX_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RX_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_RX_URRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SI_URD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SIY_URD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define MASK_SSE_RDRD { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SS_L0RDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SS_L2RDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SS_LIRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SS_LLRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SS_RRRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SS_RRRDRD2 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SS_RRRDRD3 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } -#define MASK_S_00 { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } -#define MASK_S_RD { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } -#define MASK_SSF_RRDRD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } - -/* QEMU-ADD: */ -#define INSTR_RIE_MRRP 6, { M4_32, R_8, R_12, J16_16, 0, 0 } /* e.g. crj */ -#define MASK_RIE_MRRP { 0xff, 0x00, 0x00, 0x00, 0x0f, 0xff } - -#define INSTR_RIE_MRIP 6, { M4_12, R_8, I8_32, J16_16, 0, 0 } /* e.g. cij */ -#define MASK_RIE_MRIP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } - -#define INSTR_RIE_RRIII 6, { R_8, R_12, U8_16, U8_24, U8_32, 0 } /* risbg */ -#define MASK_RIE_RRIII { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define INSTR_RIE_MRI 6, { M4_32, R_8, I16_16, 0, 0, 0 } /* e.g. cit */ -#define MASK_RIE_MRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define INSTR_RIE_MRU 6, { M4_32, R_8, U16_16, 0, 0, 0 } /* e.g. clfit */ -#define MASK_RIE_MRU { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -#define INSTR_RIE_RRI 6, { R_8, R_12, I16_16, 0, 0, 0 } -#define MASK_RIE_RRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } - -#define INSTR_RXY_URRD 6, { U8_8, D20_20, X_12, B_16, 0, 0 } -#define MASK_RXY_URRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } - -#define INSTR_SIL_DRI 6, { D_20, B_16, I16_32, 0, 0, 0 } -#define MASK_SIL_DRI { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } - -#define INSTR_RSY_MRRD 6, { M4_12, R_8, D20_20, B_16, 0, 0 } -#define MASK_SRY_MRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } - -#define INSTR_RRF_MRR 6, { M4_16, R_24, R_28, 0, 0, 0 } -#define MASK_RRF_MRR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } - -#define INSTR_SIY_DRI 6, { D20_20, B_16, I8_16, 0, 0, 0 } -#define MASK_SIY_DRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } -/* QEMU-END */ - -/* include "s390-opc.tab" generated from opcodes/s390-opc.txt rev 1.17 */ -/* The opcode table. This file was generated by s390-mkopc. - - The format of the opcode table is: - - NAME OPCODE MASK OPERANDS - - Name is the name of the instruction. - OPCODE is the instruction opcode. - MASK is the opcode mask; this is used to tell the disassembler - which bits in the actual opcode must match OPCODE. - OPERANDS is the list of operands. - - The disassembler reads the table in order and prints the first - instruction which matches. */ - -static const struct s390_opcode s390_opcodes[] = - { - { "dp", OP8(0xfdLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "mp", OP8(0xfcLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "sp", OP8(0xfbLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "ap", OP8(0xfaLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "cp", OP8(0xf9LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "zap", OP8(0xf8LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "unpk", OP8(0xf3LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "pack", OP8(0xf2LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "mvo", OP8(0xf1LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, - { "srp", OP8(0xf0LL), MASK_SS_LIRDRD, INSTR_SS_LIRDRD, 3, 0}, - { "lmd", OP8(0xefLL), MASK_SS_RRRDRD3, INSTR_SS_RRRDRD3, 2, 2}, - { "plo", OP8(0xeeLL), MASK_SS_RRRDRD2, INSTR_SS_RRRDRD2, 3, 0}, - { "stdy", OP48(0xed0000000067LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, - { "stey", OP48(0xed0000000066LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, - { "ldy", OP48(0xed0000000065LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, - { "ley", OP48(0xed0000000064LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3}, - { "tgxt", OP48(0xed0000000059LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, - { "tcxt", OP48(0xed0000000058LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, - { "tgdt", OP48(0xed0000000055LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, - { "tcdt", OP48(0xed0000000054LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, - { "tget", OP48(0xed0000000051LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, - { "tcet", OP48(0xed0000000050LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5}, - { "srxt", OP48(0xed0000000049LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, - { "slxt", OP48(0xed0000000048LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, - { "srdt", OP48(0xed0000000041LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, - { "sldt", OP48(0xed0000000040LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5}, - { "msd", OP48(0xed000000003fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, - { "mad", OP48(0xed000000003eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, - { "myh", OP48(0xed000000003dLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, - { "mayh", OP48(0xed000000003cLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, - { "my", OP48(0xed000000003bLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, - { "may", OP48(0xed000000003aLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, - { "myl", OP48(0xed0000000039LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, - { "mayl", OP48(0xed0000000038LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4}, - { "mee", OP48(0xed0000000037LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "sqe", OP48(0xed0000000034LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "mse", OP48(0xed000000002fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, - { "mae", OP48(0xed000000002eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3}, - { "lxe", OP48(0xed0000000026LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "lxd", OP48(0xed0000000025LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "lde", OP48(0xed0000000024LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "msdb", OP48(0xed000000001fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, - { "madb", OP48(0xed000000001eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, - { "ddb", OP48(0xed000000001dLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "mdb", OP48(0xed000000001cLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "sdb", OP48(0xed000000001bLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "adb", OP48(0xed000000001aLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "cdb", OP48(0xed0000000019LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "kdb", OP48(0xed0000000018LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "meeb", OP48(0xed0000000017LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "sqdb", OP48(0xed0000000015LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "sqeb", OP48(0xed0000000014LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "tcxb", OP48(0xed0000000012LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "tcdb", OP48(0xed0000000011LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "tceb", OP48(0xed0000000010LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "mseb", OP48(0xed000000000fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, - { "maeb", OP48(0xed000000000eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0}, - { "deb", OP48(0xed000000000dLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "mdeb", OP48(0xed000000000cLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "seb", OP48(0xed000000000bLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "aeb", OP48(0xed000000000aLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "ceb", OP48(0xed0000000009LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "keb", OP48(0xed0000000008LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "mxdb", OP48(0xed0000000007LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "lxeb", OP48(0xed0000000006LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "lxdb", OP48(0xed0000000005LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "ldeb", OP48(0xed0000000004LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, - { "brxlg", OP48(0xec0000000045LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2}, - { "brxhg", OP48(0xec0000000044LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2}, -/* QEMU-ADD: */ - { "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - { "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - { "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - { "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - { "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - { "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - { "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - { "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - { "risbg", OP48(0xec0000000055LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, - { "risbhg", OP48(0xec000000005dLL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, - { "risblg", OP48(0xec0000000051LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, - { "rnsbg", OP48(0xec0000000054LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, - { "rosbg", OP48(0xec0000000056LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, - { "rxsbg", OP48(0xec0000000057LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, - { "cit", OP48(0xec0000000072LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6}, - { "cgit", OP48(0xec0000000070LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6}, - { "clfit", OP48(0xec0000000073LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6}, - { "clgit", OP48(0xec0000000071LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6}, - { "ahik", OP48(0xec00000000d8LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, - { "aghik", OP48(0xec00000000d9LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, - { "alhsik", OP48(0xec00000000daLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, - { "alghsik", OP48(0xec00000000dbLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, -/* QEMU-END */ - { "tp", OP48(0xeb00000000c0LL), MASK_RSL_R0RD, INSTR_RSL_R0RD, 3, 0}, - { "stamy", OP48(0xeb000000009bLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3}, - { "lamy", OP48(0xeb000000009aLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3}, - { "lmy", OP48(0xeb0000000098LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "lmh", OP48(0xeb0000000096LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "lmh", OP48(0xeb0000000096LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "stmy", OP48(0xeb0000000090LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "clclu", OP48(0xeb000000008fLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "mvclu", OP48(0xeb000000008eLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3}, - { "mvclu", OP48(0xeb000000008eLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 0}, - { "icmy", OP48(0xeb0000000081LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, - { "icmh", OP48(0xeb0000000080LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, - { "icmh", OP48(0xeb0000000080LL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2}, - { "xiy", OP48(0xeb0000000057LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, - { "oiy", OP48(0xeb0000000056LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, - { "cliy", OP48(0xeb0000000055LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, - { "niy", OP48(0xeb0000000054LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, - { "mviy", OP48(0xeb0000000052LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, - { "tmy", OP48(0xeb0000000051LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3}, - { "bxleg", OP48(0xeb0000000045LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "bxleg", OP48(0xeb0000000045LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "bxhg", OP48(0xeb0000000044LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "bxhg", OP48(0xeb0000000044LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "cdsg", OP48(0xeb000000003eLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "cdsg", OP48(0xeb000000003eLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "cdsy", OP48(0xeb0000000031LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "csg", OP48(0xeb0000000030LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "csg", OP48(0xeb0000000030LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "lctlg", OP48(0xeb000000002fLL), MASK_RSY_CCRD, INSTR_RSY_CCRD, 2, 3}, - { "lctlg", OP48(0xeb000000002fLL), MASK_RSE_CCRD, INSTR_RSE_CCRD, 2, 2}, - { "stcmy", OP48(0xeb000000002dLL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, - { "stcmh", OP48(0xeb000000002cLL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, - { "stcmh", OP48(0xeb000000002cLL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2}, - { "stmh", OP48(0xeb0000000026LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "stmh", OP48(0xeb0000000026LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "stctg", OP48(0xeb0000000025LL), MASK_RSY_CCRD, INSTR_RSY_CCRD, 2, 3}, - { "stctg", OP48(0xeb0000000025LL), MASK_RSE_CCRD, INSTR_RSE_CCRD, 2, 2}, - { "stmg", OP48(0xeb0000000024LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "stmg", OP48(0xeb0000000024LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "clmy", OP48(0xeb0000000021LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, - { "clmh", OP48(0xeb0000000020LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3}, - { "clmh", OP48(0xeb0000000020LL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2}, - { "rll", OP48(0xeb000000001dLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3}, - { "rll", OP48(0xeb000000001dLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 2}, - { "rllg", OP48(0xeb000000001cLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "rllg", OP48(0xeb000000001cLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "csy", OP48(0xeb0000000014LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "tracg", OP48(0xeb000000000fLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "tracg", OP48(0xeb000000000fLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "sllg", OP48(0xeb000000000dLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "sllg", OP48(0xeb000000000dLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "srlg", OP48(0xeb000000000cLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "srlg", OP48(0xeb000000000cLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "slag", OP48(0xeb000000000bLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "slag", OP48(0xeb000000000bLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "srag", OP48(0xeb000000000aLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "srag", OP48(0xeb000000000aLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, - { "lmg", OP48(0xeb0000000004LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, - { "lmg", OP48(0xeb0000000004LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, -/* QEMU-ADD: */ - { "loc", OP48(0xeb00000000f2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, - { "locg", OP48(0xeb00000000e2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, - { "stoc", OP48(0xeb00000000f3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, - { "stocg", OP48(0xeb00000000e3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, - { "srak", OP48(0xeb00000000dcLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, - { "slak", OP48(0xeb00000000ddLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, - { "srlk", OP48(0xeb00000000deLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, - { "sllk", OP48(0xeb00000000dfLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, - { "asi", OP48(0xeb000000006aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, - { "alsi", OP48(0xeb000000006eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, - { "agsi", OP48(0xeb000000007aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, - { "algsi", OP48(0xeb000000007eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, -/* QEMU-END */ - { "unpka", OP8(0xeaLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "pka", OP8(0xe9LL), MASK_SS_L2RDRD, INSTR_SS_L2RDRD, 3, 0}, - { "mvcin", OP8(0xe8LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "mvcdk", OP16(0xe50fLL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, - { "mvcsk", OP16(0xe50eLL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, - { "tprot", OP16(0xe501LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, - { "strag", OP48(0xe50000000002LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 2, 2}, - { "lasp", OP16(0xe500LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, -/* QEMU-ADD: */ - { "mvhhi", OP16(0xe544LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "mvghi", OP16(0xe548LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "mvhi", OP16(0xe54cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "chhsi", OP16(0xe554LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "clhhsi", OP16(0xe555LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "cghsi", OP16(0xe558LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "clghsi", OP16(0xe559LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "chsi", OP16(0xe55cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, - { "clfhsi", OP16(0xe55dLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, -/* QEMU-END */ - { "slb", OP48(0xe30000000099LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, - { "slb", OP48(0xe30000000099LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "alc", OP48(0xe30000000098LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, - { "alc", OP48(0xe30000000098LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "dl", OP48(0xe30000000097LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, - { "dl", OP48(0xe30000000097LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "ml", OP48(0xe30000000096LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, - { "ml", OP48(0xe30000000096LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "llh", OP48(0xe30000000095LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, - { "llc", OP48(0xe30000000094LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, - { "llgh", OP48(0xe30000000091LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "llgh", OP48(0xe30000000091LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "llgc", OP48(0xe30000000090LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "llgc", OP48(0xe30000000090LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "lpq", OP48(0xe3000000008fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lpq", OP48(0xe3000000008fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "stpq", OP48(0xe3000000008eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "stpq", OP48(0xe3000000008eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "slbg", OP48(0xe30000000089LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "slbg", OP48(0xe30000000089LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "alcg", OP48(0xe30000000088LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "alcg", OP48(0xe30000000088LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "dlg", OP48(0xe30000000087LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "dlg", OP48(0xe30000000087LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "mlg", OP48(0xe30000000086LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "mlg", OP48(0xe30000000086LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "xg", OP48(0xe30000000082LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "xg", OP48(0xe30000000082LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "og", OP48(0xe30000000081LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "og", OP48(0xe30000000081LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "ng", OP48(0xe30000000080LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "ng", OP48(0xe30000000080LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "shy", OP48(0xe3000000007bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "ahy", OP48(0xe3000000007aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "chy", OP48(0xe30000000079LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lhy", OP48(0xe30000000078LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lgb", OP48(0xe30000000077LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lb", OP48(0xe30000000076LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "icy", OP48(0xe30000000073LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "stcy", OP48(0xe30000000072LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lay", OP48(0xe30000000071LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "sthy", OP48(0xe30000000070LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "sly", OP48(0xe3000000005fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "aly", OP48(0xe3000000005eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "sy", OP48(0xe3000000005bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "ay", OP48(0xe3000000005aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "cy", OP48(0xe30000000059LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "ly", OP48(0xe30000000058LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "xy", OP48(0xe30000000057LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "oy", OP48(0xe30000000056LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "cly", OP48(0xe30000000055LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "ny", OP48(0xe30000000054LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "msy", OP48(0xe30000000051LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "sty", OP48(0xe30000000050LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "bctg", OP48(0xe30000000046LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "bctg", OP48(0xe30000000046LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "strvh", OP48(0xe3000000003fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "strvh", OP48(0xe3000000003fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "strv", OP48(0xe3000000003eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, - { "strv", OP48(0xe3000000003eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "clgf", OP48(0xe30000000031LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "clgf", OP48(0xe30000000031LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "cgf", OP48(0xe30000000030LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "cgf", OP48(0xe30000000030LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "strvg", OP48(0xe3000000002fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "strvg", OP48(0xe3000000002fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "cvdg", OP48(0xe3000000002eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "cvdg", OP48(0xe3000000002eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "cvdy", OP48(0xe30000000026LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "stg", OP48(0xe30000000024LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "stg", OP48(0xe30000000024LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "clg", OP48(0xe30000000021LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "clg", OP48(0xe30000000021LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "cg", OP48(0xe30000000020LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "cg", OP48(0xe30000000020LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "lrvh", OP48(0xe3000000001fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, - { "lrvh", OP48(0xe3000000001fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "lrv", OP48(0xe3000000001eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, - { "lrv", OP48(0xe3000000001eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, - { "dsgf", OP48(0xe3000000001dLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "dsgf", OP48(0xe3000000001dLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "msgf", OP48(0xe3000000001cLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "msgf", OP48(0xe3000000001cLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "slgf", OP48(0xe3000000001bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "slgf", OP48(0xe3000000001bLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "algf", OP48(0xe3000000001aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "algf", OP48(0xe3000000001aLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "sgf", OP48(0xe30000000019LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "sgf", OP48(0xe30000000019LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "agf", OP48(0xe30000000018LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "agf", OP48(0xe30000000018LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "llgt", OP48(0xe30000000017LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "llgt", OP48(0xe30000000017LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "llgf", OP48(0xe30000000016LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "llgf", OP48(0xe30000000016LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "lgh", OP48(0xe30000000015LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lgh", OP48(0xe30000000015LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "lgf", OP48(0xe30000000014LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lgf", OP48(0xe30000000014LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "lray", OP48(0xe30000000013LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lt", OP48(0xe30000000012LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, - { "lrvg", OP48(0xe3000000000fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lrvg", OP48(0xe3000000000fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "cvbg", OP48(0xe3000000000eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "cvbg", OP48(0xe3000000000eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "dsg", OP48(0xe3000000000dLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "dsg", OP48(0xe3000000000dLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "msg", OP48(0xe3000000000cLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "msg", OP48(0xe3000000000cLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "slg", OP48(0xe3000000000bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "slg", OP48(0xe3000000000bLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "alg", OP48(0xe3000000000aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "alg", OP48(0xe3000000000aLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "sg", OP48(0xe30000000009LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "sg", OP48(0xe30000000009LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "ag", OP48(0xe30000000008LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "ag", OP48(0xe30000000008LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "cvby", OP48(0xe30000000006LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lg", OP48(0xe30000000004LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lg", OP48(0xe30000000004LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "lrag", OP48(0xe30000000003LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, - { "lrag", OP48(0xe30000000003LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, - { "ltg", OP48(0xe30000000002LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, -/* QEMU-ADD: */ - { "pfd", OP48(0xe30000000036LL), MASK_RXY_URRD, INSTR_RXY_URRD, 3, 6}, -/* QEMU-END */ - { "unpku", OP8(0xe2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "pku", OP8(0xe1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "edmk", OP8(0xdfLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "ed", OP8(0xdeLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "trt", OP8(0xddLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "tr", OP8(0xdcLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "mvcs", OP8(0xdbLL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0}, - { "mvcp", OP8(0xdaLL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0}, - { "mvck", OP8(0xd9LL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0}, - { "xc", OP8(0xd7LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "oc", OP8(0xd6LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "clc", OP8(0xd5LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "nc", OP8(0xd4LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "mvz", OP8(0xd3LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "mvc", OP8(0xd2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "mvn", OP8(0xd1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, - { "csst", OP16(0xc802LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5}, - { "ectg", OP16(0xc801LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5}, - { "mvcos", OP16(0xc800LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 4}, -/* QEMU-ADD: */ - { "exrl", OP16(0xc600ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "pfdrl", OP16(0xc602ll), MASK_RIL_UP, INSTR_RIL_UP, 3, 6}, - { "cghrl", OP16(0xc604ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "chrl", OP16(0xc605ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "clghrl", OP16(0xc606ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "clhrl", OP16(0xc607ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "cgrl", OP16(0xc608ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "clgrl", OP16(0xc60all), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "cgfrl", OP16(0xc60cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "crl", OP16(0xc60dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "clgfrl", OP16(0xc60ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "clrl", OP16(0xc60fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - - { "llhrl", OP16(0xc400ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "lghrl", OP16(0xc404ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "lhrl", OP16(0xc405ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "llghrl", OP16(0xc406ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "sthrl", OP16(0xc407ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "stgrl", OP16(0xc40bll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "llgfrl", OP16(0xc40ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "strl", OP16(0xc40fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, -/* QEMU-END */ - { "clfi", OP16(0xc20fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "clgfi", OP16(0xc20eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "cfi", OP16(0xc20dLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, - { "cgfi", OP16(0xc20cLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, - { "alfi", OP16(0xc20bLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "algfi", OP16(0xc20aLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "afi", OP16(0xc209LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, - { "agfi", OP16(0xc208LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, - { "slfi", OP16(0xc205LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "slgfi", OP16(0xc204LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, -/* QEMU-ADD: */ - { "msfi", OP16(0xc201ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6}, - { "msgfi", OP16(0xc200ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6}, -/* QEMU-END */ - { "jg", OP16(0xc0f4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgno", OP16(0xc0e4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnh", OP16(0xc0d4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnp", OP16(0xc0d4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgle", OP16(0xc0c4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnl", OP16(0xc0b4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnm", OP16(0xc0b4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jghe", OP16(0xc0a4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnlh", OP16(0xc094LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jge", OP16(0xc084LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgz", OP16(0xc084LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgne", OP16(0xc074LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnz", OP16(0xc074LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jglh", OP16(0xc064LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnhe", OP16(0xc054LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgl", OP16(0xc044LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgm", OP16(0xc044LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgnle", OP16(0xc034LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgh", OP16(0xc024LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgp", OP16(0xc024LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "jgo", OP16(0xc014LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, - { "llilf", OP16(0xc00fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "llihf", OP16(0xc00eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "oilf", OP16(0xc00dLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "oihf", OP16(0xc00cLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "nilf", OP16(0xc00bLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "nihf", OP16(0xc00aLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "iilf", OP16(0xc009LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "iihf", OP16(0xc008LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "xilf", OP16(0xc007LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "xihf", OP16(0xc006LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, - { "brasl", OP16(0xc005LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 2}, - { "brcl", OP16(0xc004LL), MASK_RIL_UP, INSTR_RIL_UP, 3, 2}, - { "lgfi", OP16(0xc001LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, - { "larl", OP16(0xc000LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 2}, - { "icm", OP8(0xbfLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0}, - { "stcm", OP8(0xbeLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0}, - { "clm", OP8(0xbdLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0}, - { "cds", OP8(0xbbLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "cs", OP8(0xbaLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "cu42", OP16(0xb9b3LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "cu41", OP16(0xb9b2LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "cu24", OP16(0xb9b1LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "cu14", OP16(0xb9b0LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "lptea", OP16(0xb9aaLL), MASK_RRF_RURR, INSTR_RRF_RURR, 2, 4}, - { "esea", OP16(0xb99dLL), MASK_RRE_R0, INSTR_RRE_R0, 2, 2}, - { "slbr", OP16(0xb999LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, - { "alcr", OP16(0xb998LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, - { "dlr", OP16(0xb997LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, - { "mlr", OP16(0xb996LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, - { "llhr", OP16(0xb995LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "llcr", OP16(0xb994LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "troo", OP16(0xb993LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, - { "troo", OP16(0xb993LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "trot", OP16(0xb992LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, - { "trot", OP16(0xb992LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "trto", OP16(0xb991LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, - { "trto", OP16(0xb991LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "trtt", OP16(0xb990LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4}, - { "trtt", OP16(0xb990LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "idte", OP16(0xb98eLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 2, 3}, - { "epsw", OP16(0xb98dLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, - { "cspg", OP16(0xb98aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 3}, - { "slbgr", OP16(0xb989LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "alcgr", OP16(0xb988LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "dlgr", OP16(0xb987LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "mlgr", OP16(0xb986LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "llghr", OP16(0xb985LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "llgcr", OP16(0xb984LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "flogr", OP16(0xb983LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "xgr", OP16(0xb982LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "ogr", OP16(0xb981LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "ngr", OP16(0xb980LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "bctgr", OP16(0xb946LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "klmd", OP16(0xb93fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, - { "kimd", OP16(0xb93eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, - { "clgfr", OP16(0xb931LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "cgfr", OP16(0xb930LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "kmc", OP16(0xb92fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, - { "km", OP16(0xb92eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, - { "lhr", OP16(0xb927LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "lbr", OP16(0xb926LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "sturg", OP16(0xb925LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "clgr", OP16(0xb921LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "cgr", OP16(0xb920LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lrvr", OP16(0xb91fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2}, - { "kmac", OP16(0xb91eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3}, - { "dsgfr", OP16(0xb91dLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "msgfr", OP16(0xb91cLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "slgfr", OP16(0xb91bLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "algfr", OP16(0xb91aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "sgfr", OP16(0xb919LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "agfr", OP16(0xb918LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "llgtr", OP16(0xb917LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "llgfr", OP16(0xb916LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lgfr", OP16(0xb914LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lcgfr", OP16(0xb913LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "ltgfr", OP16(0xb912LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lngfr", OP16(0xb911LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lpgfr", OP16(0xb910LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lrvgr", OP16(0xb90fLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "eregg", OP16(0xb90eLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "dsgr", OP16(0xb90dLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "msgr", OP16(0xb90cLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "slgr", OP16(0xb90bLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "algr", OP16(0xb90aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "sgr", OP16(0xb909LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "agr", OP16(0xb908LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lghr", OP16(0xb907LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "lgbr", OP16(0xb906LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4}, - { "lurag", OP16(0xb905LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lgr", OP16(0xb904LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lcgr", OP16(0xb903LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "ltgr", OP16(0xb902LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lngr", OP16(0xb901LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "lpgr", OP16(0xb900LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, -/* QEMU-ADD: */ - { "crt", OP16(0xb972LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, - { "cgrt", OP16(0xb960LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, - { "clrt", OP16(0xb973LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, - { "clgrt", OP16(0xb961LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, - { "locr", OP16(0xb9f2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6}, - { "locgr", OP16(0xb9e2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6}, - { "popcnt", OP16(0xb9e1LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 6}, - { "ngrk", OP16(0xb9e4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "ogrk", OP16(0xb9e6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "xgrk", OP16(0xb9e7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "agrk", OP16(0xb9e8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "sgrk", OP16(0xb9e9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "algrk", OP16(0xb9eaLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "slgrk", OP16(0xb9ebLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "nrk", OP16(0xb9f4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "ork", OP16(0xb9f6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "xrk", OP16(0xb9f7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "ark", OP16(0xb9f8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "srk", OP16(0xb9f9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "alrk", OP16(0xb9faLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, - { "slrk", OP16(0xb9fbLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, -/* QEMU-END */ - { "lctl", OP8(0xb7LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0}, - { "stctl", OP8(0xb6LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0}, - { "rrxtr", OP16(0xb3ffLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, - { "iextr", OP16(0xb3feLL), MASK_RRF_F0FR, INSTR_RRF_F0FR, 2, 5}, - { "qaxtr", OP16(0xb3fdLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, - { "cextr", OP16(0xb3fcLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "cxstr", OP16(0xb3fbLL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, - { "cxutr", OP16(0xb3faLL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, - { "cxgtr", OP16(0xb3f9LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, - { "rrdtr", OP16(0xb3f7LL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, - { "iedtr", OP16(0xb3f6LL), MASK_RRF_F0FR, INSTR_RRF_F0FR, 2, 5}, - { "qadtr", OP16(0xb3f5LL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, - { "cedtr", OP16(0xb3f4LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "cdstr", OP16(0xb3f3LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, - { "cdutr", OP16(0xb3f2LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, - { "cdgtr", OP16(0xb3f1LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, - { "esxtr", OP16(0xb3efLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "eextr", OP16(0xb3edLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "cxtr", OP16(0xb3ecLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "csxtr", OP16(0xb3ebLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "cuxtr", OP16(0xb3eaLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "cgxtr", OP16(0xb3e9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 5}, - { "kxtr", OP16(0xb3e8LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "esdtr", OP16(0xb3e7LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "eedtr", OP16(0xb3e5LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "cdtr", OP16(0xb3e4LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "csdtr", OP16(0xb3e3LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "cudtr", OP16(0xb3e2LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "cgdtr", OP16(0xb3e1LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 5}, - { "kdtr", OP16(0xb3e0LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "fixtr", OP16(0xb3dfLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, - { "ltxtr", OP16(0xb3deLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "ldxtr", OP16(0xb3ddLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, - { "lxdtr", OP16(0xb3dcLL), MASK_RRF_0UFF, INSTR_RRF_0UFF, 2, 5}, - { "sxtr", OP16(0xb3dbLL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "axtr", OP16(0xb3daLL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "dxtr", OP16(0xb3d9LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "mxtr", OP16(0xb3d8LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "fidtr", OP16(0xb3d7LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, - { "ltdtr", OP16(0xb3d6LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "ledtr", OP16(0xb3d5LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5}, - { "ldetr", OP16(0xb3d4LL), MASK_RRF_0UFF, INSTR_RRF_0UFF, 2, 5}, - { "sdtr", OP16(0xb3d3LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "adtr", OP16(0xb3d2LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "ddtr", OP16(0xb3d1LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "mdtr", OP16(0xb3d0LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5}, - { "lgdr", OP16(0xb3cdLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5}, - { "cgxr", OP16(0xb3caLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cgdr", OP16(0xb3c9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cger", OP16(0xb3c8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cxgr", OP16(0xb3c6LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "cdgr", OP16(0xb3c5LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "cegr", OP16(0xb3c4LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "ldgr", OP16(0xb3c1LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5}, - { "cfxr", OP16(0xb3baLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cfdr", OP16(0xb3b9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cfer", OP16(0xb3b8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cxfr", OP16(0xb3b6LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, - { "cdfr", OP16(0xb3b5LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, - { "cefr", OP16(0xb3b4LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, - { "cgxbr", OP16(0xb3aaLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cgdbr", OP16(0xb3a9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cgebr", OP16(0xb3a8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2}, - { "cxgbr", OP16(0xb3a6LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "cdgbr", OP16(0xb3a5LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "cegbr", OP16(0xb3a4LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, - { "cfxbr", OP16(0xb39aLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0}, - { "cfdbr", OP16(0xb399LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0}, - { "cfebr", OP16(0xb398LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0}, - { "cxfbr", OP16(0xb396LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, - { "cdfbr", OP16(0xb395LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, - { "cefbr", OP16(0xb394LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0}, - { "efpc", OP16(0xb38cLL), MASK_RRE_RR_OPT, INSTR_RRE_RR_OPT, 3, 0}, - { "sfasr", OP16(0xb385LL), MASK_RRE_R0, INSTR_RRE_R0, 2, 5}, - { "sfpc", OP16(0xb384LL), MASK_RRE_RR_OPT, INSTR_RRE_RR_OPT, 3, 0}, - { "fidr", OP16(0xb37fLL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "fier", OP16(0xb377LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "lzxr", OP16(0xb376LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "lzdr", OP16(0xb375LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "lzer", OP16(0xb374LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "lcdfr", OP16(0xb373LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "cpsdr", OP16(0xb372LL), MASK_RRF_F0FF2, INSTR_RRF_F0FF2, 2, 5}, - { "lndfr", OP16(0xb371LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "lpdfr", OP16(0xb370LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5}, - { "cxr", OP16(0xb369LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "fixr", OP16(0xb367LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "lexr", OP16(0xb366LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lxr", OP16(0xb365LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "lcxr", OP16(0xb363LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "ltxr", OP16(0xb362LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lnxr", OP16(0xb361LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lpxr", OP16(0xb360LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "fidbr", OP16(0xb35fLL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "didbr", OP16(0xb35bLL), MASK_RRF_FUFF, INSTR_RRF_FUFF, 3, 0}, - { "thdr", OP16(0xb359LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "thder", OP16(0xb358LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "fiebr", OP16(0xb357LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "diebr", OP16(0xb353LL), MASK_RRF_FUFF, INSTR_RRF_FUFF, 3, 0}, - { "tbdr", OP16(0xb351LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "tbedr", OP16(0xb350LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "dxbr", OP16(0xb34dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "mxbr", OP16(0xb34cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "sxbr", OP16(0xb34bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "axbr", OP16(0xb34aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "cxbr", OP16(0xb349LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "kxbr", OP16(0xb348LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "fixbr", OP16(0xb347LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0}, - { "lexbr", OP16(0xb346LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "ldxbr", OP16(0xb345LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "ledbr", OP16(0xb344LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lcxbr", OP16(0xb343LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "ltxbr", OP16(0xb342LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lnxbr", OP16(0xb341LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lpxbr", OP16(0xb340LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "msdr", OP16(0xb33fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, - { "madr", OP16(0xb33eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, - { "myhr", OP16(0xb33dLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, - { "mayhr", OP16(0xb33cLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, - { "myr", OP16(0xb33bLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, - { "mayr", OP16(0xb33aLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, - { "mylr", OP16(0xb339LL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, - { "maylr", OP16(0xb338LL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4}, - { "meer", OP16(0xb337LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "sqxr", OP16(0xb336LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "mser", OP16(0xb32fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, - { "maer", OP16(0xb32eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3}, - { "lxer", OP16(0xb326LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lxdr", OP16(0xb325LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lder", OP16(0xb324LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "msdbr", OP16(0xb31fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, - { "madbr", OP16(0xb31eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, - { "ddbr", OP16(0xb31dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "mdbr", OP16(0xb31cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "sdbr", OP16(0xb31bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "adbr", OP16(0xb31aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "cdbr", OP16(0xb319LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "kdbr", OP16(0xb318LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "meebr", OP16(0xb317LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "sqxbr", OP16(0xb316LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "sqdbr", OP16(0xb315LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "sqebr", OP16(0xb314LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lcdbr", OP16(0xb313LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "ltdbr", OP16(0xb312LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lndbr", OP16(0xb311LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lpdbr", OP16(0xb310LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "msebr", OP16(0xb30fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, - { "maebr", OP16(0xb30eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0}, - { "debr", OP16(0xb30dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "mdebr", OP16(0xb30cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "sebr", OP16(0xb30bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "aebr", OP16(0xb30aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "cebr", OP16(0xb309LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "kebr", OP16(0xb308LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "mxdbr", OP16(0xb307LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lxebr", OP16(0xb306LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lxdbr", OP16(0xb305LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "ldebr", OP16(0xb304LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lcebr", OP16(0xb303LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "ltebr", OP16(0xb302LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lnebr", OP16(0xb301LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, - { "lpebr", OP16(0xb300LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, -/* QEMU-ADD: */ - { "clfebr", OP16(0xb39cLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "clfdbr", OP16(0xb39dLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "clfxbr", OP16(0xb39eLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "clgebr", OP16(0xb3acLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "clgdbr", OP16(0xb3adLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "clgxbr", OP16(0xb3aeLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "celfbr", OP16(0xb390LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "cdlfbr", OP16(0xb391LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "cxlfbr", OP16(0xb392LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "celgbr", OP16(0xb3a0LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "cdlgbr", OP16(0xb3a1LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, - { "cxlgbr", OP16(0xb3a2LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, -/* QEMU-END */ - { "trap4", OP16(0xb2ffLL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "lfas", OP16(0xb2bdLL), MASK_S_RD, INSTR_S_RD, 2, 5}, - { "srnmt", OP16(0xb2b9LL), MASK_S_RD, INSTR_S_RD, 2, 5}, - { "lpswe", OP16(0xb2b2LL), MASK_S_RD, INSTR_S_RD, 2, 2}, - { "stfl", OP16(0xb2b1LL), MASK_S_RD, INSTR_S_RD, 3, 2}, - { "stfle", OP16(0xb2b0LL), MASK_S_RD, INSTR_S_RD, 2, 4}, - { "cu12", OP16(0xb2a7LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "cutfu", OP16(0xb2a7LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "cutfu", OP16(0xb2a7LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "cu21", OP16(0xb2a6LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "cuutf", OP16(0xb2a6LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "cuutf", OP16(0xb2a6LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "tre", OP16(0xb2a5LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "lfpc", OP16(0xb29dLL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stfpc", OP16(0xb29cLL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "srnm", OP16(0xb299LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stsi", OP16(0xb27dLL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stckf", OP16(0xb27cLL), MASK_S_RD, INSTR_S_RD, 2, 4}, - { "sacf", OP16(0xb279LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stcke", OP16(0xb278LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "rp", OP16(0xb277LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "xsch", OP16(0xb276LL), MASK_S_00, INSTR_S_00, 3, 0}, - { "siga", OP16(0xb274LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "cmpsc", OP16(0xb263LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "cmpsc", OP16(0xb263LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "srst", OP16(0xb25eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "clst", OP16(0xb25dLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "bsa", OP16(0xb25aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "bsg", OP16(0xb258LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "cuse", OP16(0xb257LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "mvst", OP16(0xb255LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "mvpg", OP16(0xb254LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "msr", OP16(0xb252LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "csp", OP16(0xb250LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "ear", OP16(0xb24fLL), MASK_RRE_RA, INSTR_RRE_RA, 3, 0}, - { "sar", OP16(0xb24eLL), MASK_RRE_AR, INSTR_RRE_AR, 3, 0}, - { "cpya", OP16(0xb24dLL), MASK_RRE_AA, INSTR_RRE_AA, 3, 0}, - { "tar", OP16(0xb24cLL), MASK_RRE_AR, INSTR_RRE_AR, 3, 0}, - { "lura", OP16(0xb24bLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "esta", OP16(0xb24aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "ereg", OP16(0xb249LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "palb", OP16(0xb248LL), MASK_RRE_00, INSTR_RRE_00, 3, 0}, - { "msta", OP16(0xb247LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "stura", OP16(0xb246LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "sqer", OP16(0xb245LL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0}, - { "sqdr", OP16(0xb244LL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0}, - { "cksm", OP16(0xb241LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "bakr", OP16(0xb240LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "schm", OP16(0xb23cLL), MASK_S_00, INSTR_S_00, 3, 0}, - { "rchp", OP16(0xb23bLL), MASK_S_00, INSTR_S_00, 3, 0}, - { "stcps", OP16(0xb23aLL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stcrw", OP16(0xb239LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "rsch", OP16(0xb238LL), MASK_S_00, INSTR_S_00, 3, 0}, - { "sal", OP16(0xb237LL), MASK_S_00, INSTR_S_00, 3, 0}, - { "tpi", OP16(0xb236LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "tsch", OP16(0xb235LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stsch", OP16(0xb234LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "ssch", OP16(0xb233LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "msch", OP16(0xb232LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "hsch", OP16(0xb231LL), MASK_S_00, INSTR_S_00, 3, 0}, - { "csch", OP16(0xb230LL), MASK_S_00, INSTR_S_00, 3, 0}, - { "pgout", OP16(0xb22fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "pgin", OP16(0xb22eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "dxr", OP16(0xb22dLL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0}, - { "tb", OP16(0xb22cLL), MASK_RRE_0R, INSTR_RRE_0R, 3, 0}, - { "sske", OP16(0xb22bLL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4}, - { "sske", OP16(0xb22bLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "rrbe", OP16(0xb22aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "iske", OP16(0xb229LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "pt", OP16(0xb228LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "esar", OP16(0xb227LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "epar", OP16(0xb226LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "ssar", OP16(0xb225LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "iac", OP16(0xb224LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "ivsk", OP16(0xb223LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "ipm", OP16(0xb222LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0}, - { "ipte", OP16(0xb221LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0}, - { "cfc", OP16(0xb21aLL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "sac", OP16(0xb219LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "pc", OP16(0xb218LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "sie", OP16(0xb214LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stap", OP16(0xb212LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stpx", OP16(0xb211LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "spx", OP16(0xb210LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "ptlb", OP16(0xb20dLL), MASK_S_00, INSTR_S_00, 3, 0}, - { "ipk", OP16(0xb20bLL), MASK_S_00, INSTR_S_00, 3, 0}, - { "spka", OP16(0xb20aLL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stpt", OP16(0xb209LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "spt", OP16(0xb208LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stckc", OP16(0xb207LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "sckc", OP16(0xb206LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stck", OP16(0xb205LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "sck", OP16(0xb204LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "stidp", OP16(0xb202LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "lra", OP8(0xb1LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "mc", OP8(0xafLL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "sigp", OP8(0xaeLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "stosm", OP8(0xadLL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "stnsm", OP8(0xacLL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "clcle", OP8(0xa9LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "mvcle", OP8(0xa8LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "j", OP16(0xa7f4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jno", OP16(0xa7e4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnh", OP16(0xa7d4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnp", OP16(0xa7d4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jle", OP16(0xa7c4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnl", OP16(0xa7b4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnm", OP16(0xa7b4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jhe", OP16(0xa7a4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnlh", OP16(0xa794LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "je", OP16(0xa784LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jz", OP16(0xa784LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jne", OP16(0xa774LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnz", OP16(0xa774LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jlh", OP16(0xa764LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnhe", OP16(0xa754LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jl", OP16(0xa744LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jm", OP16(0xa744LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jnle", OP16(0xa734LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jh", OP16(0xa724LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jp", OP16(0xa724LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "jo", OP16(0xa714LL), MASK_RI_0P, INSTR_RI_0P, 3, 0}, - { "cghi", OP16(0xa70fLL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, - { "chi", OP16(0xa70eLL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, - { "mghi", OP16(0xa70dLL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, - { "mhi", OP16(0xa70cLL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, - { "aghi", OP16(0xa70bLL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, - { "ahi", OP16(0xa70aLL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, - { "lghi", OP16(0xa709LL), MASK_RI_RI, INSTR_RI_RI, 2, 2}, - { "lhi", OP16(0xa708LL), MASK_RI_RI, INSTR_RI_RI, 3, 0}, - { "brctg", OP16(0xa707LL), MASK_RI_RP, INSTR_RI_RP, 2, 2}, - { "brct", OP16(0xa706LL), MASK_RI_RP, INSTR_RI_RP, 3, 0}, - { "bras", OP16(0xa705LL), MASK_RI_RP, INSTR_RI_RP, 3, 0}, - { "brc", OP16(0xa704LL), MASK_RI_UP, INSTR_RI_UP, 3, 0}, - { "tmhl", OP16(0xa703LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "tmhh", OP16(0xa702LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "tml", OP16(0xa701LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, - { "tmll", OP16(0xa701LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, - { "tmh", OP16(0xa700LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, - { "tmlh", OP16(0xa700LL), MASK_RI_RU, INSTR_RI_RU, 3, 0}, - { "llill", OP16(0xa50fLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "llilh", OP16(0xa50eLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "llihl", OP16(0xa50dLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "llihh", OP16(0xa50cLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "oill", OP16(0xa50bLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "oilh", OP16(0xa50aLL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "oihl", OP16(0xa509LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "oihh", OP16(0xa508LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "nill", OP16(0xa507LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "nilh", OP16(0xa506LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "nihl", OP16(0xa505LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "nihh", OP16(0xa504LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "iill", OP16(0xa503LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "iilh", OP16(0xa502LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "iihl", OP16(0xa501LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "iihh", OP16(0xa500LL), MASK_RI_RU, INSTR_RI_RU, 2, 2}, - { "stam", OP8(0x9bLL), MASK_RS_AARD, INSTR_RS_AARD, 3, 0}, - { "lam", OP8(0x9aLL), MASK_RS_AARD, INSTR_RS_AARD, 3, 0}, - { "trace", OP8(0x99LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "lm", OP8(0x98LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "xi", OP8(0x97LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "oi", OP8(0x96LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "cli", OP8(0x95LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "ni", OP8(0x94LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "ts", OP8(0x93LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "mvi", OP8(0x92LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "tm", OP8(0x91LL), MASK_SI_URD, INSTR_SI_URD, 3, 0}, - { "stm", OP8(0x90LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "slda", OP8(0x8fLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "srda", OP8(0x8eLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "sldl", OP8(0x8dLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "srdl", OP8(0x8cLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "sla", OP8(0x8bLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "sra", OP8(0x8aLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "sll", OP8(0x89LL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "srl", OP8(0x88LL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0}, - { "bxle", OP8(0x87LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "bxh", OP8(0x86LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "brxle", OP8(0x85LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0}, - { "brxh", OP8(0x84LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0}, - { "diag", OP8(0x83LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0}, - { "lpsw", OP8(0x82LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "ssm", OP8(0x80LL), MASK_S_RD, INSTR_S_RD, 3, 0}, - { "su", OP8(0x7fLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "au", OP8(0x7eLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "de", OP8(0x7dLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "me", OP8(0x7cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "mde", OP8(0x7cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "se", OP8(0x7bLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "ae", OP8(0x7aLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "ce", OP8(0x79LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "le", OP8(0x78LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "ms", OP8(0x71LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "ste", OP8(0x70LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "sw", OP8(0x6fLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "aw", OP8(0x6eLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "dd", OP8(0x6dLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "md", OP8(0x6cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "sd", OP8(0x6bLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "ad", OP8(0x6aLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "cd", OP8(0x69LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "ld", OP8(0x68LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "mxd", OP8(0x67LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "std", OP8(0x60LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0}, - { "sl", OP8(0x5fLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "al", OP8(0x5eLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "d", OP8(0x5dLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "m", OP8(0x5cLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "s", OP8(0x5bLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "a", OP8(0x5aLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "c", OP8(0x59LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "l", OP8(0x58LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "x", OP8(0x57LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "o", OP8(0x56LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "cl", OP8(0x55LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "n", OP8(0x54LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "lae", OP8(0x51LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "st", OP8(0x50LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "cvb", OP8(0x4fLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "cvd", OP8(0x4eLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "bas", OP8(0x4dLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "mh", OP8(0x4cLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "sh", OP8(0x4bLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "ah", OP8(0x4aLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "ch", OP8(0x49LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "lh", OP8(0x48LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "b", OP16(0x47f0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bno", OP16(0x47e0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnh", OP16(0x47d0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnp", OP16(0x47d0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "ble", OP16(0x47c0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnl", OP16(0x47b0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnm", OP16(0x47b0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bhe", OP16(0x47a0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnlh", OP16(0x4790LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "be", OP16(0x4780LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bz", OP16(0x4780LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bne", OP16(0x4770LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnz", OP16(0x4770LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "blh", OP16(0x4760LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnhe", OP16(0x4750LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bl", OP16(0x4740LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bm", OP16(0x4740LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bnle", OP16(0x4730LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bh", OP16(0x4720LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bp", OP16(0x4720LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bo", OP16(0x4710LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bc", OP8(0x47LL), MASK_RX_URRD, INSTR_RX_URRD, 3, 0}, - { "nop", OP16(0x4700LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0}, - { "bct", OP8(0x46LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "bal", OP8(0x45LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "ex", OP8(0x44LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "ic", OP8(0x43LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "stc", OP8(0x42LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "la", OP8(0x41LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "sth", OP8(0x40LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0}, - { "sur", OP8(0x3fLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "aur", OP8(0x3eLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "der", OP8(0x3dLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "mer", OP8(0x3cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "mder", OP8(0x3cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "ser", OP8(0x3bLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "aer", OP8(0x3aLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "cer", OP8(0x39LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "ler", OP8(0x38LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "sxr", OP8(0x37LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "axr", OP8(0x36LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lrer", OP8(0x35LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "ledr", OP8(0x35LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "her", OP8(0x34LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lcer", OP8(0x33LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lter", OP8(0x32LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lner", OP8(0x31LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lper", OP8(0x30LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "swr", OP8(0x2fLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "awr", OP8(0x2eLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "ddr", OP8(0x2dLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "mdr", OP8(0x2cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "sdr", OP8(0x2bLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "adr", OP8(0x2aLL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "cdr", OP8(0x29LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "ldr", OP8(0x28LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "mxdr", OP8(0x27LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "mxr", OP8(0x26LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lrdr", OP8(0x25LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "ldxr", OP8(0x25LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "hdr", OP8(0x24LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lcdr", OP8(0x23LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "ltdr", OP8(0x22LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lndr", OP8(0x21LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "lpdr", OP8(0x20LL), MASK_RR_FF, INSTR_RR_FF, 3, 0}, - { "slr", OP8(0x1fLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "alr", OP8(0x1eLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "dr", OP8(0x1dLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "mr", OP8(0x1cLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "sr", OP8(0x1bLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "ar", OP8(0x1aLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "cr", OP8(0x19LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "lr", OP8(0x18LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "xr", OP8(0x17LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "or", OP8(0x16LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "clr", OP8(0x15LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "nr", OP8(0x14LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "lcr", OP8(0x13LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "ltr", OP8(0x12LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "lnr", OP8(0x11LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "lpr", OP8(0x10LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "clcl", OP8(0x0fLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "mvcl", OP8(0x0eLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "basr", OP8(0x0dLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "bassm", OP8(0x0cLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "bsm", OP8(0x0bLL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "svc", OP8(0x0aLL), MASK_RR_U0, INSTR_RR_U0, 3, 0}, - { "br", OP16(0x07f0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnor", OP16(0x07e0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnhr", OP16(0x07d0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnpr", OP16(0x07d0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bler", OP16(0x07c0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnlr", OP16(0x07b0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnmr", OP16(0x07b0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bher", OP16(0x07a0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnlhr", OP16(0x0790LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "ber", OP16(0x0780LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bzr", OP16(0x0780LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bner", OP16(0x0770LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnzr", OP16(0x0770LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "blhr", OP16(0x0760LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnher", OP16(0x0750LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "blr", OP16(0x0740LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bmr", OP16(0x0740LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bnler", OP16(0x0730LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bhr", OP16(0x0720LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bpr", OP16(0x0720LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bor", OP16(0x0710LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bcr", OP8(0x07LL), MASK_RR_UR, INSTR_RR_UR, 3, 0}, - { "nopr", OP16(0x0700LL), MASK_RR_0R, INSTR_RR_0R, 3, 0}, - { "bctr", OP8(0x06LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "balr", OP8(0x05LL), MASK_RR_RR, INSTR_RR_RR, 3, 0}, - { "spm", OP8(0x04LL), MASK_RR_R0, INSTR_RR_R0, 3, 0}, - { "trap2", OP16(0x01ffLL), MASK_E, INSTR_E, 3, 0}, - { "sam64", OP16(0x010eLL), MASK_E, INSTR_E, 2, 2}, - { "sam31", OP16(0x010dLL), MASK_E, INSTR_E, 3, 2}, - { "sam24", OP16(0x010cLL), MASK_E, INSTR_E, 3, 2}, - { "tam", OP16(0x010bLL), MASK_E, INSTR_E, 3, 2}, - { "pfpo", OP16(0x010aLL), MASK_E, INSTR_E, 2, 5}, - { "sckpf", OP16(0x0107LL), MASK_E, INSTR_E, 3, 0}, - { "upt", OP16(0x0102LL), MASK_E, INSTR_E, 3, 0}, - { "pr", OP16(0x0101LL), MASK_E, INSTR_E, 3, 0}, -}; - -static const int s390_num_opcodes = - sizeof (s390_opcodes) / sizeof (s390_opcodes[0]); diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index c29a4b8fe6..e9163ba556 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -80,7 +80,7 @@ Ubuntu LTS. Other distros will be assumed to ship similar software versions. For FreeBSD and OpenBSD, decisions will be made based on the contents of the respective ports repository, while NetBSD will use the pkgsrc repository. -For macOS, `HomeBrew`_ will be used, although `MacPorts`_ is expected to carry +For macOS, `Homebrew`_ will be used, although `MacPorts`_ is expected to carry similar versions. Windows @@ -92,6 +92,6 @@ hosted on Linux (Debian/Fedora). The version of the Windows API that's currently targeted is Vista / Server 2008. -.. _HomeBrew: https://brew.sh/ +.. _Homebrew: https://brew.sh/ .. _MacPorts: https://www.macports.org/ .. _Repology: https://repology.org/ diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 26d00812ba..896e5a97ab 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -67,25 +67,6 @@ and will cause a warning. The replacement for the ``nodelay`` short-form boolean option is ``nodelay=on`` rather than ``delay=off``. -``--enable-fips`` (since 6.0) -''''''''''''''''''''''''''''' - -This option restricts usage of certain cryptographic algorithms when -the host is operating in FIPS mode. - -If FIPS compliance is required, QEMU should be built with the ``libgcrypt`` -library enabled as a cryptography provider. - -Neither the ``nettle`` library, or the built-in cryptography provider are -supported on FIPS enabled hosts. - -``-writeconfig`` (since 6.0) -''''''''''''''''''''''''''''' - -The ``-writeconfig`` option is not able to serialize the entire contents -of the QEMU command line. It is thus considered a failed experiment -and deprecated, with no current replacement. - Userspace local APIC with KVM (x86, since 6.0) '''''''''''''''''''''''''''''''''''''''''''''' @@ -315,13 +296,6 @@ Use the more generic event ``DEVICE_UNPLUG_GUEST_ERROR`` instead. System emulator machines ------------------------ -Aspeed ``swift-bmc`` machine (since 6.1) -'''''''''''''''''''''''''''''''''''''''' - -This machine is deprecated because we have enough AST2500 based OpenPOWER -machines. It can be easily replaced by the ``witherspoon-bmc`` or the -``romulus-bmc`` machines. - PPC 405 ``taihu`` machine (since 7.0) ''''''''''''''''''''''''''''''''''''' @@ -331,6 +305,14 @@ machine is hardly emulated at all (e.g. neither the LCD nor the USB part had been implemented), so there is not much value added by this board. Use the ``ref405ep`` machine instead. +``pc-i440fx-1.4`` up to ``pc-i440fx-1.7`` (since 7.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''' + +These old machine types are quite neglected nowadays and thus might have +various pitfalls with regards to live migration. Use a newer machine type +instead. + + Backend options --------------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index cb0575fd49..4a0b270296 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -336,6 +336,24 @@ for the RISC-V ``virt`` machine and ``sifive_u`` machine. The ``-no-quit`` was a synonym for ``-display ...,window-close=off`` which should be used instead. +``--enable-fips`` (removed in 7.1) +'''''''''''''''''''''''''''''''''' + +This option restricted usage of certain cryptographic algorithms when +the host is operating in FIPS mode. + +If FIPS compliance is required, QEMU should be built with the ``libgcrypt`` +or ``gnutls`` library enabled as a cryptography provider. + +Neither the ``nettle`` library, or the built-in cryptography provider are +supported on FIPS enabled hosts. + +``-writeconfig`` (removed in 7.1) +''''''''''''''''''''''''''''''''' + +The ``-writeconfig`` option was not able to serialize the entire contents +of the QEMU command line. It is thus considered a failed experiment +and removed without a replacement. QEMU Machine Protocol (QMP) commands ------------------------------------ @@ -355,7 +373,8 @@ documentation of ``query-hotpluggable-cpus`` for additional details. ``change`` (removed in 6.0) ''''''''''''''''''''''''''' -Use ``blockdev-change-medium`` or ``change-vnc-password`` instead. +Use ``blockdev-change-medium`` or ``change-vnc-password`` or +``display-update`` instead. ``query-events`` (removed in 6.0) ''''''''''''''''''''''''''''''''' @@ -588,6 +607,11 @@ The Raspberry Pi machines come in various models (A, A+, B, B+). To be able to distinguish which model QEMU is implementing, the ``raspi2`` and ``raspi3`` machines have been renamed ``raspi2b`` and ``raspi3b``. +Aspeed ``swift-bmc`` machine (removed in 7.0) +''''''''''''''''''''''''''''''''''''''''''''' + +This machine was removed because it was unused. Alternative AST2500 based +OpenPOWER machines are ``witherspoon-bmc`` and ``romulus-bmc``. linux-user mode CPUs -------------------- diff --git a/docs/ccid.txt b/docs/ccid.txt deleted file mode 100644 index 2b85b1bd42..0000000000 --- a/docs/ccid.txt +++ /dev/null @@ -1,182 +0,0 @@ -QEMU CCID Device Documentation. - -Contents -1. USB CCID device -2. Building -3. Using ccid-card-emulated with hardware -4. Using ccid-card-emulated with certificates -5. Using ccid-card-passthru with client side hardware -6. Using ccid-card-passthru with client side certificates -7. Passthrough protocol scenario -8. libcacard - -1. USB CCID device - -The USB CCID device is a USB device implementing the CCID specification, which -lets one connect smart card readers that implement the same spec. For more -information see the specification: - - Universal Serial Bus - Device Class: Smart Card - CCID - Specification for - Integrated Circuit(s) Cards Interface Devices - Revision 1.1 - April 22rd, 2005 - -Smartcards are used for authentication, single sign on, decryption in -public/private schemes and digital signatures. A smartcard reader on the client -cannot be used on a guest with simple usb passthrough since it will then not be -available on the client, possibly locking the computer when it is "removed". On -the other hand this device can let you use the smartcard on both the client and -the guest machine. It is also possible to have a completely virtual smart card -reader and smart card (i.e. not backed by a physical device) using this device. - -2. Building - -The cryptographic functions and access to the physical card is done via the -libcacard library, whose development package must be installed prior to -building QEMU: - -In redhat/fedora: - yum install libcacard-devel -In ubuntu: - apt-get install libcacard-dev - -Configuring and building: - ./configure --enable-smartcard && make - - -3. Using ccid-card-emulated with hardware - -Assuming you have a working smartcard on the host with the current -user, using libcacard, QEMU acts as another client using ccid-card-emulated: - - qemu -usb -device usb-ccid -device ccid-card-emulated - - -4. Using ccid-card-emulated with certificates stored in files - -You must create the CA and card certificates. This is a one time process. -We use NSS certificates: - - mkdir fake-smartcard - cd fake-smartcard - certutil -N -d sql:$PWD - certutil -S -d sql:$PWD -s "CN=Fake Smart Card CA" -x -t TC,TC,TC -n fake-smartcard-ca - certutil -S -d sql:$PWD -t ,, -s "CN=John Doe" -n id-cert -c fake-smartcard-ca - certutil -S -d sql:$PWD -t ,, -s "CN=John Doe (signing)" --nsCertType smime -n signing-cert -c fake-smartcard-ca - certutil -S -d sql:$PWD -t ,, -s "CN=John Doe (encryption)" --nsCertType sslClient -n encryption-cert -c fake-smartcard-ca - -Note: you must have exactly three certificates. - -You can use the emulated card type with the certificates backend: - - qemu -usb -device usb-ccid -device ccid-card-emulated,backend=certificates,db=sql:$PWD,cert1=id-cert,cert2=signing-cert,cert3=encryption-cert - -To use the certificates in the guest, export the CA certificate: - - certutil -L -r -d sql:$PWD -o fake-smartcard-ca.cer -n fake-smartcard-ca - -and import it in the guest: - - certutil -A -d /etc/pki/nssdb -i fake-smartcard-ca.cer -t TC,TC,TC -n fake-smartcard-ca - -In a Linux guest you can then use the CoolKey PKCS #11 module to access -the card: - - certutil -d /etc/pki/nssdb -L -h all - -It will prompt you for the PIN (which is the password you assigned to the -certificate database early on), and then show you all three certificates -together with the manually imported CA cert: - - Certificate Nickname Trust Attributes - fake-smartcard-ca CT,C,C - John Doe:CAC ID Certificate u,u,u - John Doe:CAC Email Signature Certificate u,u,u - John Doe:CAC Email Encryption Certificate u,u,u - -If this does not happen, CoolKey is not installed or not registered with -NSS. Registration can be done from Firefox or the command line: - - modutil -dbdir /etc/pki/nssdb -add "CAC Module" -libfile /usr/lib64/pkcs11/libcoolkeypk11.so - modutil -dbdir /etc/pki/nssdb -list - - -5. Using ccid-card-passthru with client side hardware - -on the host specify the ccid-card-passthru device with a suitable chardev: - - qemu -chardev socket,server=on,host=0.0.0.0,port=2001,id=ccid,wait=off \ - -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid - -on the client run vscclient, built when you built QEMU: - - vscclient 2001 - - -6. Using ccid-card-passthru with client side certificates - -This case is not particularly useful, but you can use it to debug -your setup if #4 works but #5 does not. - -Follow instructions as per #4, except run QEMU and vscclient as follows: -Run qemu as per #5, and run vscclient from the "fake-smartcard" -directory as follows: - - qemu -chardev socket,server=on,host=0.0.0.0,port=2001,id=ccid,wait=off \ - -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid - vscclient -e "db=\"sql:$PWD\" use_hw=no soft=(,Test,CAC,,id-cert,signing-cert,encryption-cert)" 2001 - - -7. Passthrough protocol scenario - -This is a typical interchange of messages when using the passthru card device. -usb-ccid is a usb device. It defaults to an unattached usb device on startup. -usb-ccid expects a chardev and expects the protocol defined in -cac_card/vscard_common.h to be passed over that. -The usb-ccid device can be in one of three modes: - * detached - * attached with no card - * attached with card - -A typical interchange is: (the arrow shows who started each exchange, it can be client -originated or guest originated) - -client event | vscclient | passthru | usb-ccid | guest event ----------------------------------------------------------------------------------------------- - | VSC_Init | | | - | VSC_ReaderAdd | | attach | - | | | | sees new usb device. -card inserted -> | | | | - | VSC_ATR | insert | insert | see new card - | | | | - | VSC_APDU | VSC_APDU | | <- guest sends APDU -client<->physical | | | | -card APDU exchange| | | | -client response ->| VSC_APDU | VSC_APDU | | receive APDU response - ... - [APDU<->APDU repeats several times] - ... -card removed -> | | | | - | VSC_CardRemove | remove | remove | card removed - ... - [(card insert, apdu's, card remove) repeat] - ... -kill/quit | | | | - vscclient | | | | - | VSC_ReaderRemove | | detach | - | | | | usb device removed. - - -8. libcacard - -Both ccid-card-emulated and vscclient use libcacard as the card emulator. -libcacard implements a completely virtual CAC (DoD standard for smart -cards) compliant card and uses NSS to retrieve certificates and do -any encryption. The backend can then be a real reader and card, or -certificates stored in files. - -For documentation of the library see docs/libcacard.txt. - diff --git a/docs/devel/ci-jobs.rst.inc b/docs/devel/ci-jobs.rst.inc index db3f571d5f..92e25872aa 100644 --- a/docs/devel/ci-jobs.rst.inc +++ b/docs/devel/ci-jobs.rst.inc @@ -44,6 +44,13 @@ If you've got access to an aarch64 host that can be used as a gitlab-CI runner, you can set this variable to enable the tests that require this kind of host. The runner should be tagged with "aarch64". +AARCH32_RUNNER_AVAILABLE +~~~~~~~~~~~~~~~~~~~~~~~~ +If you've got access to an armhf host or an arch64 host that can run +aarch32 EL0 code to be used as a gitlab-CI runner, you can set this +variable to enable the tests that require this kind of host. The +runner should be tagged with "aarch32". + S390X_RUNNER_AVAILABLE ~~~~~~~~~~~~~~~~~~~~~~ If you've got access to an IBM Z host that can be used as a gitlab-CI diff --git a/docs/devel/index-api.rst b/docs/devel/index-api.rst new file mode 100644 index 0000000000..60c0d7459d --- /dev/null +++ b/docs/devel/index-api.rst @@ -0,0 +1,14 @@ +Internal QEMU APIs +------------------ + +Details about how QEMU's various internal APIs. Most of these are +generated from in-code annotations to function prototypes. + +.. toctree:: + :maxdepth: 2 + + bitops + loads-stores + memory + modules + ui diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst new file mode 100644 index 0000000000..1002a533a6 --- /dev/null +++ b/docs/devel/index-build.rst @@ -0,0 +1,18 @@ +QEMU Build and Test 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. + +.. toctree:: + :maxdepth: 3 + + build-system + kconfig + testing + qtest + ci + qapi-code-gen + fuzzing + control-flow-integrity diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst new file mode 100644 index 0000000000..a50889c556 --- /dev/null +++ b/docs/devel/index-internals.rst @@ -0,0 +1,20 @@ +Internal Subsystem Information +------------------------------ + +Details about QEMU's various subsystems including how to add features to them. + +.. toctree:: + :maxdepth: 2 + + qom + atomics + block-coroutine-wrapper + clocks + ebpf_rss + migration + multi-process + reset + s390-dasd-ipl + tracing + vfio-migration + writing-monitor-commands diff --git a/docs/devel/index-process.rst b/docs/devel/index-process.rst new file mode 100644 index 0000000000..d0d7a200fd --- /dev/null +++ b/docs/devel/index-process.rst @@ -0,0 +1,16 @@ +QEMU Community Processes +------------------------ + +Notes about how to interact with the community and how and where to submit patches. + +.. toctree:: + :maxdepth: 2 + + code-of-conduct + conflict-resolution + style + submitting-a-patch + trivial-patches + stable-process + submitting-a-pull-request + secure-coding-practices diff --git a/docs/devel/index-tcg.rst b/docs/devel/index-tcg.rst new file mode 100644 index 0000000000..0b0ad12c22 --- /dev/null +++ b/docs/devel/index-tcg.rst @@ -0,0 +1,15 @@ +TCG Emulation +------------- + +Details about QEMU's Tiny Code Generator and the infrastructure +associated with emulation. You do not need to worry about this if you +are only implementing things for HW accelerated hypervisors. + +.. toctree:: + :maxdepth: 2 + + tcg + decodetree + multi-thread-tcg + tcg-icount + tcg-plugins diff --git a/docs/devel/index.rst b/docs/devel/index.rst index afd937535e..09cfb322be 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -7,44 +7,10 @@ You only need to read it if you are interested in reading or modifying QEMU's source code. .. toctree:: - :maxdepth: 2 - :includehidden: + :maxdepth: 1 - code-of-conduct - conflict-resolution - build-system - style - kconfig - testing - fuzzing - control-flow-integrity - loads-stores - memory - migration - atomics - stable-process - ci - qtest - decodetree - secure-coding-practices - tcg - tcg-icount - tracing - multi-thread-tcg - tcg-plugins - bitops - ui - reset - s390-dasd-ipl - clocks - qom - modules - block-coroutine-wrapper - multi-process - ebpf_rss - vfio-migration - qapi-code-gen - writing-monitor-commands - trivial-patches - submitting-a-patch - submitting-a-pull-request + index-process + index-build + index-api + index-internals + index-tcg diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 8f0035c821..ad5dfe133e 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -275,7 +275,7 @@ called during the translator callback ``translate_insn``. There is a set of functions ending in ``_swap`` which, if the parameter is true, returns the value in the endianness that is the reverse of -the guest native endianness, as determined by ``TARGET_WORDS_BIGENDIAN``. +the guest native endianness, as determined by ``TARGET_BIG_ENDIAN``. Function names follow the pattern: diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 2401253482..3e9656d8e0 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -389,19 +389,13 @@ Each version is associated with a series of fields saved. The ``save_state`` al the state as the newer version. But ``load_state`` sometimes is able to load state from an older version. -You can see that there are several version fields: +You can see that there are two version fields: - ``version_id``: the maximum version_id supported by VMState for that device. - ``minimum_version_id``: the minimum version_id that VMState is able to understand for that device. -- ``minimum_version_id_old``: For devices that were not able to port to vmstate, we can - assign a function that knows how to read this old state. This field is - ignored if there is no ``load_state_old`` handler. -VMState is able to read versions from minimum_version_id to -version_id. And the function ``load_state_old()`` (if present) is able to -load state from minimum_version_id_old to minimum_version_id. This -function is deprecated and will be removed when no more users are left. +VMState is able to read versions from minimum_version_id to version_id. There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields, e.g. diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 246709ede8..7b968433a6 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -41,8 +41,8 @@ used internally. There are several kinds of types: simple types (a number of built-in types, such as ``int`` and ``str``; as well as enumerations), arrays, -complex types (structs and two flavors of unions), and alternate types -(a choice between other types). +complex types (structs and unions), and alternate types (a choice +between other types). Schema syntax diff --git a/docs/devel/qtest.rst b/docs/devel/qtest.rst index c3dceb6c8a..0455aa06ab 100644 --- a/docs/devel/qtest.rst +++ b/docs/devel/qtest.rst @@ -3,7 +3,6 @@ QTest Device Emulation Testing Framework ======================================== .. toctree:: - :hidden: qgraph @@ -89,4 +88,4 @@ QTest Protocol libqtest API reference ---------------------- -.. kernel-doc:: tests/qtest/libqos/libqtest.h +.. kernel-doc:: tests/qtest/libqtest.h diff --git a/docs/devel/style.rst b/docs/devel/style.rst index 9e66d133e1..7ddd42b6c2 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -522,7 +522,7 @@ documented in the GNU Compiler Collection manual starting at version 4.0. Automatic memory deallocation ============================= -QEMU has a mandatory dependency either the GCC or CLang compiler. As +QEMU has a mandatory dependency on either the GCC or the Clang compiler. As such it has the freedom to make use of a C language extension for automatically running a cleanup function when a stack variable goes out of scope. This can be used to simplify function cleanup paths, diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index f93ef4fe52..a7cc44aa20 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -27,13 +27,18 @@ Once built a program can be run with multiple plugins loaded each with their own arguments:: $QEMU $OTHER_QEMU_ARGS \ - -plugin tests/plugin/libhowvec.so,inline=on,count=hint \ - -plugin tests/plugin/libhotblocks.so + -plugin contrib/plugin/libhowvec.so,inline=on,count=hint \ + -plugin contrib/plugin/libhotblocks.so Arguments are plugin specific and can be used to modify their behaviour. In this case the howvec plugin is being asked to use inline ops to count and break down the hint instructions by type. +Linux user-mode emulation also evaluates the environment variable +``QEMU_PLUGIN``:: + + QEMU_PLUGIN="file=contrib/plugins/libhowvec.so,inline=on,count=hint" $QEMU + Writing plugins --------------- diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 92d40cdd19..5b60a31807 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -637,6 +637,44 @@ The full set of annotations can be found here: https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp +docker-binfmt-image-debian-% targets +------------------------------------ + +It is possible to combine Debian's bootstrap scripts with a configured +``binfmt_misc`` to bootstrap a number of Debian's distros including +experimental ports not yet supported by a released OS. This can +simplify setting up a rootfs by using docker to contain the foreign +rootfs rather than manually invoking chroot. + +Setting up ``binfmt_misc`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the script ``qemu-binfmt-conf.sh`` to configure a QEMU +user binary to automatically run binaries for the foreign +architecture. While the scripts will try their best to work with +dynamically linked QEMU's a statically linked one will present less +potential complications when copying into the docker image. Modern +kernels support the ``F`` (fix binary) flag which will open the QEMU +executable on setup and avoids the need to find and re-open in the +chroot environment. This is triggered with the ``--persistent`` flag. + +Example invocation +~~~~~~~~~~~~~~~~~~ + +For example to setup the HPPA ports builds of Debian:: + + make docker-binfmt-image-debian-sid-hppa \ + DEB_TYPE=sid DEB_ARCH=hppa \ + DEB_URL=http://ftp.ports.debian.org/debian-ports/ \ + DEB_KEYRING=/usr/share/keyrings/debian-ports-archive-keyring.gpg \ + EXECUTABLE=(pwd)/qemu-hppa V=1 + +The ``DEB_`` variables are substitutions used by +``debian-boostrap.pre`` which is called to do the initial debootstrap +of the rootfs before it is copied into the container. The second stage +is run as part of the build. The final image will be tagged as +``qemu/debian-sid-hppa``. + VM testing ---------- diff --git a/docs/devel/tracing.rst b/docs/devel/tracing.rst index ec9a687cfd..d288480db1 100644 --- a/docs/devel/tracing.rst +++ b/docs/devel/tracing.rst @@ -48,7 +48,7 @@ file. During build, the "trace-events" file in each listed subdirectory will be processed by the "tracetool" script to generate code for the trace events. The individual "trace-events" files are merged into a "trace-events-all" file, -which is also installed into "/usr/share/qemu" with the name "trace-events". +which is also installed into "/usr/share/qemu". This merged file is to be used by the "simpletrace.py" script to later analyse traces in the simpletrace data format. diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 0417c183a3..33588a0396 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -225,6 +225,21 @@ default (WS2016). Note: hv-version-id-* are not enlightenments and thus don't enable Hyper-V identification when specified without any other enlightenments. +3.21. hv-syndbg +=============== +Enables Hyper-V synthetic debugger interface, this is a special interface used +by Windows Kernel debugger to send the packets through, rather than sending +them via serial/network . +When enabled, this enlightenment provides additional communication facilities +to the guest: SynDbg messages. +This new communication is used by Windows Kernel debugger rather than sending +packets via serial/network, adding significant performance boost over the other +comm channels. +This enlightenment requires a VMBus device (-device vmbus-bridge,irq=15) +and the follow enlightenments to work: +hv-relaxed,hv_time,hv-vapic,hv-vpindex,hv-synic,hv-runtime,hv-stimer + + 4. Supplementary features ========================= diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index edc3ad84a3..4dbc84fd00 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -38,6 +38,26 @@ conventions `. *Master* and *slave* can be either a client (i.e. connecting) or server (listening) in the socket communication. +Support for platforms other than Linux +-------------------------------------- + +While vhost-user was initially developed targeting Linux, nowadays it +is supported on any platform that provides the following features: + +- A way for requesting shared memory represented by a file descriptor + so it can be passed over a UNIX domain socket and then mapped by the + other process. + +- AF_UNIX sockets with SCM_RIGHTS, so QEMU and the other process can + exchange messages through it, including ancillary data when needed. + +- Either eventfd or pipe/pipe2. On platforms where eventfd is not + available, QEMU will automatically fall back to pipe2 or, as a last + resort, pipe. Each file descriptor will be used for receiving or + sending events by reading or writing (respectively) an 8-byte value + to the corresponding it. The 8-value itself has no meaning and + should not be interpreted. + Message Specification ===================== diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt new file mode 100644 index 0000000000..11158dbf88 --- /dev/null +++ b/docs/pcie_sriov.txt @@ -0,0 +1,115 @@ +PCI SR/IOV EMULATION SUPPORT +============================ + +Description +=========== +SR/IOV (Single Root I/O Virtualization) is an optional extended capability +of a PCI Express device. It allows a single physical function (PF) to appear as multiple +virtual functions (VFs) for the main purpose of eliminating software +overhead in I/O from virtual machines. + +QEMU now implements the basic common functionality to enable an emulated device +to support SR/IOV. Yet no fully implemented devices exists in QEMU, but a +proof-of-concept hack of the Intel igb can be found here: + +git://github.com/knuto/qemu.git sriov_patches_v5 + +Implementation +============== +Implementing emulation of an SR/IOV capable device typically consists of +implementing support for two types of device classes; the "normal" physical device +(PF) and the virtual device (VF). From QEMU's perspective, the VFs are just +like other devices, except that some of their properties are derived from +the PF. + +A virtual function is different from a physical function in that the BAR +space for all VFs are defined by the BAR registers in the PFs SR/IOV +capability. All VFs have the same BARs and BAR sizes. + +Accesses to these virtual BARs then is computed as + + + * + + +From our emulation perspective this means that there is a separate call for +setting up a BAR for a VF. + +1) To enable SR/IOV support in the PF, it must be a PCI Express device so + you would need to add a PCI Express capability in the normal PCI + capability list. You might also want to add an ARI (Alternative + Routing-ID Interpretation) capability to indicate that your device + supports functions beyond it's "own" function space (0-7), + which is necessary to support more than 7 functions, or + if functions extends beyond offset 7 because they are placed at an + offset > 1 or have stride > 1. + + ... + #include "hw/pci/pcie.h" + #include "hw/pci/pcie_sriov.h" + + pci_your_pf_dev_realize( ... ) + { + ... + int ret = pcie_endpoint_cap_init(d, 0x70); + ... + pcie_ari_init(d, 0x100, 1); + ... + + /* Add and initialize the SR/IOV capability */ + pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", + vf_devid, initial_vfs, total_vfs, + fun_offset, stride); + + /* Set up individual VF BARs (parameters as for normal BARs) */ + pcie_sriov_pf_init_vf_bar( ... ) + ... + } + + For cleanup, you simply call: + + pcie_sriov_pf_exit(device); + + which will delete all the virtual functions and associated resources. + +2) Similarly in the implementation of the virtual function, you need to + make it a PCI Express device and add a similar set of capabilities + except for the SR/IOV capability. Then you need to set up the VF BARs as + subregions of the PFs SR/IOV VF BARs by calling + pcie_sriov_vf_register_bar() instead of the normal pci_register_bar() call: + + pci_your_vf_dev_realize( ... ) + { + ... + int ret = pcie_endpoint_cap_init(d, 0x60); + ... + pcie_ari_init(d, 0x100, 1); + ... + memory_region_init(mr, ... ) + pcie_sriov_vf_register_bar(d, bar_nr, mr); + ... + } + +Testing on Linux guest +====================== +The easiest is if your device driver supports sysfs based SR/IOV +enabling. Support for this was added in kernel v.3.8, so not all drivers +support it yet. + +To enable 4 VFs for a device at 01:00.0: + + modprobe yourdriver + echo 4 > /sys/bus/pci/devices/0000:01:00.0/sriov_numvfs + +You should now see 4 VFs with lspci. +To turn SR/IOV off again - the standard requires you to turn it off before you can enable +another VF count, and the emulation enforces this: + + echo 0 > /sys/bus/pci/devices/0000:01:00.0/sriov_numvfs + +Older drivers typically provide a max_vfs module parameter +to enable it at load time: + + modprobe yourdriver max_vfs=4 + +To disable the VFs again then, you simply have to unload the driver: + + rmmod yourdriver diff --git a/docs/qemu_logo.pdf b/docs/qemu_logo.pdf deleted file mode 100644 index 294cb7dec5..0000000000 Binary files a/docs/qemu_logo.pdf and /dev/null differ diff --git a/docs/specs/acpi_erst.rst b/docs/specs/acpi_erst.rst new file mode 100644 index 0000000000..a8a9d22d25 --- /dev/null +++ b/docs/specs/acpi_erst.rst @@ -0,0 +1,200 @@ +ACPI ERST DEVICE +================ + +The ACPI ERST device is utilized to support the ACPI Error Record +Serialization Table, ERST, functionality. This feature is designed for +storing error records in persistent storage for future reference +and/or debugging. + +The ACPI specification[1], in Chapter "ACPI Platform Error Interfaces +(APEI)", and specifically subsection "Error Serialization", outlines a +method for storing error records into persistent storage. + +The format of error records is described in the UEFI specification[2], +in Appendix N "Common Platform Error Record". + +While the ACPI specification allows for an NVRAM "mode" (see +GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES) where non-volatile RAM is +directly exposed for direct access by the OS/guest, this device +implements the non-NVRAM "mode". This non-NVRAM "mode" is what is +implemented by most BIOS (since flash memory requires programming +operations in order to update its contents). Furthermore, as of the +time of this writing, Linux only supports the non-NVRAM "mode". + + +Background/Motivation +--------------------- + +Linux uses the persistent storage filesystem, pstore, to record +information (eg. dmesg tail) upon panics and shutdowns. Pstore is +independent of, and runs before, kdump. In certain scenarios (ie. +hosts/guests with root filesystems on NFS/iSCSI where networking +software and/or hardware fails, and thus kdump fails), pstore may +contain information available for post-mortem debugging. + +Two common storage backends for the pstore filesystem are ACPI ERST +and UEFI. Most BIOS implement ACPI ERST. UEFI is not utilized in all +guests. With QEMU supporting ACPI ERST, it becomes a viable pstore +storage backend for virtual machines (as it is now for bare metal +machines). + +Enabling support for ACPI ERST facilitates a consistent method to +capture kernel panic information in a wide range of guests: from +resource-constrained microvms to very large guests, and in particular, +in direct-boot environments (which would lack UEFI run-time services). + +Note that Microsoft Windows also utilizes the ACPI ERST for certain +crash information, if available[3]. + + +Configuration|Usage +------------------- + +To use ACPI ERST, a memory-backend-file object and acpi-erst device +can be created, for example: + + qemu ... + -object memory-backend-file,id=erstnvram,mem-path=acpi-erst.backing,size=0x10000,share=on \ + -device acpi-erst,memdev=erstnvram + +For proper operation, the ACPI ERST device needs a memory-backend-file +object with the following parameters: + + - id: The id of the memory-backend-file object is used to associate + this memory with the acpi-erst device. + - size: The size of the ACPI ERST backing storage. This parameter is + required. + - mem-path: The location of the ACPI ERST backing storage file. This + parameter is also required. + - share: The share=on parameter is required so that updates to the + ERST backing store are written to the file. + +and ERST device: + + - memdev: Is the object id of the memory-backend-file. + - record_size: Specifies the size of the records (or slots) in the + backend storage. Must be a power of two value greater than or + equal to 4096 (PAGE_SIZE). + + +PCI Interface +------------- + +The ERST device is a PCI device with two BARs, one for accessing the +programming registers, and the other for accessing the record exchange +buffer. + +BAR0 contains the programming interface consisting of ACTION and VALUE +64-bit registers. All ERST actions/operations/side effects happen on +the write to the ACTION, by design. Any data needed by the action must +be placed into VALUE prior to writing ACTION. Reading the VALUE +simply returns the register contents, which can be updated by a +previous ACTION. + +BAR1 contains the 8KiB record exchange buffer, which is the +implemented maximum record size. + + +Backend Storage Format +---------------------- + +The backend storage is divided into fixed size "slots", 8KiB in +length, with each slot storing a single record. Not all slots need to +be occupied, and they need not be occupied in a contiguous fashion. +The ability to clear/erase specific records allows for the formation +of unoccupied slots. + +Slot 0 contains a backend storage header that identifies the contents +as ERST and also facilitates efficient access to the records. +Depending upon the size of the backend storage, additional slots will +be designated to be a part of the slot 0 header. For example, at 8KiB, +the slot 0 header can accomodate 1021 records. Thus a storage size +of 8MiB (8KiB * 1024) requires an additional slot for use by the +header. In this scenario, slot 0 and slot 1 form the backend storage +header, and records can be stored starting at slot 2. + +Below is an example layout of the backend storage format (for storage +size less than 8MiB). The size of the storage is a multiple of 8KiB, +and contains N number of slots to store records. The example below +shows two records (in CPER format) in the backend storage, while the +remaining slots are empty/available. + +:: + + Slot Record + <------------------ 8KiB --------------------> + +--------------------------------------------+ + 0 | storage header | + +--------------------------------------------+ + 1 | empty/available | + +--------------------------------------------+ + 2 | CPER | + +--------------------------------------------+ + 3 | CPER | + +--------------------------------------------+ + ... | | + +--------------------------------------------+ + N | empty/available | + +--------------------------------------------+ + +The storage header consists of some basic information and an array +of CPER record_id's to efficiently access records in the backend +storage. + +All fields in the header are stored in little endian format. + +:: + + +--------------------------------------------+ + | magic | 0x0000 + +--------------------------------------------+ + | record_offset | record_size | 0x0008 + +--------------------------------------------+ + | record_count | reserved | version | 0x0010 + +--------------------------------------------+ + | record_id[0] | 0x0018 + +--------------------------------------------+ + | record_id[1] | 0x0020 + +--------------------------------------------+ + | record_id[...] | + +--------------------------------------------+ + | record_id[N] | 0x1FF8 + +--------------------------------------------+ + +The 'magic' field contains the value 0x524F545354535245. + +The 'record_size' field contains the value 0x2000, 8KiB. + +The 'record_offset' field points to the first record_id in the array, +0x0018. + +The 'version' field contains 0x0100, the first version. + +The 'record_count' field contains the number of valid records in the +backend storage. + +The 'record_id' array fields are the 64-bit record identifiers of the +CPER record in the corresponding slot. Stated differently, the +location of a CPER record_id in the record_id[] array provides the +slot index for the corresponding record in the backend storage. + +Note that, for example, with a backend storage less than 8MiB, slot 0 +contains the header, so the record_id[0] will never contain a valid +CPER record_id. Instead slot 1 is the first available slot and thus +record_id_[1] may contain a CPER. + +A 'record_id' of all 0s or all 1s indicates an invalid record (ie. the +slot is available). + + +References +---------- + +[1] "Advanced Configuration and Power Interface Specification", + version 4.0, June 2009. + +[2] "Unified Extensible Firmware Interface Specification", + version 2.1, October 2008. + +[3] "Windows Hardware Error Architecture", specfically + "Error Record Persistence Mechanism". diff --git a/docs/specs/index.rst b/docs/specs/index.rst index ecc43896bb..e10684bf53 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -18,3 +18,5 @@ guest hardware that is specific to QEMU. acpi_mem_hotplug acpi_pci_hotplug acpi_nvdimm + acpi_erst + sev-guest-firmware diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt index 5e407a6f32..dd6859d039 100644 --- a/docs/specs/pci-ids.txt +++ b/docs/specs/pci-ids.txt @@ -65,6 +65,7 @@ PCI devices (other than virtio): 1b36:000f mdpy (mdev sample device), linux/samples/vfio-mdev/mdpy.c 1b36:0010 PCIe NVMe device (-device nvme) 1b36:0011 PCI PVPanic device (-device pvpanic-pci) +1b36:0012 PCI ACPI ERST device (-device acpi-erst) All these devices are documented in docs/specs. diff --git a/docs/specs/sev-guest-firmware.rst b/docs/specs/sev-guest-firmware.rst new file mode 100644 index 0000000000..3f7f082df5 --- /dev/null +++ b/docs/specs/sev-guest-firmware.rst @@ -0,0 +1,125 @@ +==================================================== +QEMU/Guest Firmware Interface for AMD SEV and SEV-ES +==================================================== + +Overview +======== + +The guest firmware image (OVMF) may contain some configuration entries +which are used by QEMU before the guest launches. These are listed in a +GUIDed table at a known location in the firmware image. QEMU parses +this table when it loads the firmware image into memory, and then QEMU +reads individual entries when their values are needed. + +Though nothing in the table structure is SEV-specific, currently all the +entries in the table are related to SEV and SEV-ES features. + + +Table parsing in QEMU +--------------------- + +The table is parsed from the footer: first the presence of the table +footer GUID (96b582de-1fb2-45f7-baea-a366c55a082d) at 0xffffffd0 is +verified. If that is found, two bytes at 0xffffffce are the entire +table length. + +Then the table is scanned backwards looking for the specific entry GUID. + +QEMU files related to parsing and scanning the OVMF table: + - ``hw/i386/pc_sysfw_ovmf.c`` + +The edk2 firmware code that constructs this structure is in the +`OVMF Reset Vector file`_. + + +Table memory layout +------------------- + ++------------+--------+-----------------------------------------+ +| GPA | Length | Description | ++============+========+=========================================+ +| 0xffffff80 | 4 | Zero padding | ++------------+--------+-----------------------------------------+ +| 0xffffff84 | 4 | SEV hashes table base address | ++------------+--------+-----------------------------------------+ +| 0xffffff88 | 4 | SEV hashes table size (=0x400) | ++------------+--------+-----------------------------------------+ +| 0xffffff8c | 2 | SEV hashes table entry length (=0x1a) | ++------------+--------+-----------------------------------------+ +| 0xffffff8e | 16 | SEV hashes table GUID: | +| | | 7255371f-3a3b-4b04-927b-1da6efa8d454 | ++------------+--------+-----------------------------------------+ +| 0xffffff9e | 4 | SEV secret block base address | ++------------+--------+-----------------------------------------+ +| 0xffffffa2 | 4 | SEV secret block size (=0xc00) | ++------------+--------+-----------------------------------------+ +| 0xffffffa6 | 2 | SEV secret block entry length (=0x1a) | ++------------+--------+-----------------------------------------+ +| 0xffffffa8 | 16 | SEV secret block GUID: | +| | | 4c2eb361-7d9b-4cc3-8081-127c90d3d294 | ++------------+--------+-----------------------------------------+ +| 0xffffffb8 | 4 | SEV-ES AP reset RIP | ++------------+--------+-----------------------------------------+ +| 0xffffffbc | 2 | SEV-ES reset block entry length (=0x16) | ++------------+--------+-----------------------------------------+ +| 0xffffffbe | 16 | SEV-ES reset block entry GUID: | +| | | 00f771de-1a7e-4fcb-890e-68c77e2fb44e | ++------------+--------+-----------------------------------------+ +| 0xffffffce | 2 | Length of entire table including table | +| | | footer GUID and length (=0x72) | ++------------+--------+-----------------------------------------+ +| 0xffffffd0 | 16 | OVMF GUIDed table footer GUID: | +| | | 96b582de-1fb2-45f7-baea-a366c55a082d | ++------------+--------+-----------------------------------------+ +| 0xffffffe0 | 8 | Application processor entry point code | ++------------+--------+-----------------------------------------+ +| 0xffffffe8 | 8 | "\0\0\0\0VTF\0" | ++------------+--------+-----------------------------------------+ +| 0xfffffff0 | 16 | Reset vector code | ++------------+--------+-----------------------------------------+ + + +Table entries description +========================= + +SEV-ES reset block +------------------ + +Entry GUID: 00f771de-1a7e-4fcb-890e-68c77e2fb44e + +For the initial boot of an AP under SEV-ES, the "reset" RIP must be +programmed to the RAM area defined by this entry. The entry's format +is: + +* IP value [0:15] +* CS segment base [31:16] + +A hypervisor reads the CS segment base and IP value. The CS segment +base value represents the high order 16-bits of the CS segment base, so +the hypervisor must left shift the value of the CS segment base by 16 +bits to form the full CS segment base for the CS segment register. It +would then program the EIP register with the IP value as read. + + +SEV secret block +---------------- + +Entry GUID: 4c2eb361-7d9b-4cc3-8081-127c90d3d294 + +This describes the guest RAM area where the hypervisor should inject the +Guest Owner secret (using SEV_LAUNCH_SECRET). + + +SEV hashes table +---------------- + +Entry GUID: 7255371f-3a3b-4b04-927b-1da6efa8d454 + +This describes the guest RAM area where the hypervisor should install a +table describing the hashes of certain firmware configuration device +files that would otherwise be passed in unchecked. The current use is +for the kernel, initrd and command line values, but others may be added. + + +.. _OVMF Reset Vector file: + https://github.com/tianocore/edk2/blob/master/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index d8b102fa0a..60ed94f187 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -22,7 +22,6 @@ AST2500 SoC based machines : - ``romulus-bmc`` OpenPOWER Romulus POWER9 BMC - ``witherspoon-bmc`` OpenPOWER Witherspoon POWER9 BMC - ``sonorapass-bmc`` OCP SonoraPass BMC -- ``swift-bmc`` OpenPOWER Swift BMC POWER9 (to be removed in v7.0) - ``fp5280g2-bmc`` Inspur FP5280G2 BMC - ``g220a-bmc`` Bytedance G220A BMC diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 144dc491d9..c3bd0676a8 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -9,6 +9,7 @@ the following architecture extensions: - FEAT_AA32HPD (AArch32 hierarchical permission disables) - FEAT_AA32I8MM (AArch32 Int8 matrix multiplication instructions) - FEAT_AES (AESD and AESE instructions) +- FEAT_BBM at level 2 (Translation table break-before-make levels) - FEAT_BF16 (AArch64 BFloat16 instructions) - FEAT_BTI (Branch Target Identification) - FEAT_DIT (Data Independent Timing instructions) @@ -24,9 +25,12 @@ the following architecture extensions: - FEAT_I8MM (AArch64 Int8 matrix multiplication instructions) - FEAT_JSCVT (JavaScript conversion instructions) - FEAT_LOR (Limited ordering regions) +- FEAT_LPA (Large Physical Address space) +- FEAT_LPA2 (Large Physical and virtual Address space v2) - FEAT_LRCPC (Load-acquire RCpc instructions) - FEAT_LRCPC2 (Load-acquire RCpc instructions v2) - FEAT_LSE (Large System Extensions) +- FEAT_LVA (Large Virtual Address space) - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) @@ -51,6 +55,7 @@ the following architecture extensions: - FEAT_TLBIOS (TLB invalidate instructions in Outer Shareable domain) - FEAT_TLBIRANGE (TLB invalidate range instructions) - FEAT_TTCNP (Translation table Common not private translations) +- FEAT_TTL (Translation Table Level) - FEAT_TTST (Small translation tables) - FEAT_UAO (Unprivileged Access Override control) - FEAT_VHE (Virtualization Host Extensions) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 1544632b67..5fe045cbf0 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -96,14 +96,17 @@ gic-version Valid values are: ``2`` - GICv2 + GICv2. Note that this limits the number of CPUs to 8. ``3`` - GICv3 + GICv3. This allows up to 512 CPUs. + ``4`` + GICv4. Requires ``virtualization`` to be ``on``; allows up to 317 CPUs. ``host`` Use the same GIC version the host provides, when using KVM ``max`` Use the best GIC version possible (same as host when using KVM; - currently same as ``3``` for TCG, but this may change in future) + with TCG this is currently ``3`` if ``virtualization`` is ``off`` and + ``4`` if ``virtualization`` is ``on``, but this may change in future) its Set ``on``/``off`` to enable/disable ITS instantiation. The default is ``on`` diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index 0b3a3d73ad..ae8dd233e8 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -83,6 +83,7 @@ Emulated Devices :maxdepth: 1 devices/can.rst + devices/ccid.rst devices/ivshmem.rst devices/net.rst devices/nvme.rst diff --git a/docs/system/devices/can.rst b/docs/system/devices/can.rst index 16d72c3ac3..fe37af8223 100644 --- a/docs/system/devices/can.rst +++ b/docs/system/devices/can.rst @@ -182,7 +182,7 @@ Links to other resources (5) `GNU/Linux, CAN and CANopen in Real-time Control Applications Slides from LinuxDays 2017 (include updated RTLWS 2015 content) `_ (6) `Linux SocketCAN utilities `_ (7) `CTU CAN FD project including core VHDL design, Linux driver, test utilities etc. `_ - (8) `CTU CAN FD Core Datasheet Documentation `_ - (9) `CTU CAN FD Core System Architecture Documentation `_ - (10) `CTU CAN FD Driver Documentation `_ + (8) `CTU CAN FD Core Datasheet Documentation `_ + (9) `CTU CAN FD Core System Architecture Documentation `_ + (10) `CTU CAN FD Driver Documentation `_ (11) `Integration with PCIe interfacing for Intel/Altera Cyclone IV based board `_ diff --git a/docs/system/devices/ccid.rst b/docs/system/devices/ccid.rst new file mode 100644 index 0000000000..3b8c2ab46a --- /dev/null +++ b/docs/system/devices/ccid.rst @@ -0,0 +1,171 @@ +Chip Card Interface Device (CCID) +================================= + +USB CCID device +--------------- +The USB CCID device is a USB device implementing the CCID specification, which +lets one connect smart card readers that implement the same spec. For more +information see the specification:: + + Universal Serial Bus + Device Class: Smart Card + CCID + Specification for + Integrated Circuit(s) Cards Interface Devices + Revision 1.1 + April 22rd, 2005 + +Smartcards are used for authentication, single sign on, decryption in +public/private schemes and digital signatures. A smartcard reader on the client +cannot be used on a guest with simple usb passthrough since it will then not be +available on the client, possibly locking the computer when it is "removed". On +the other hand this device can let you use the smartcard on both the client and +the guest machine. It is also possible to have a completely virtual smart card +reader and smart card (i.e. not backed by a physical device) using this device. + +Building +-------- +The cryptographic functions and access to the physical card is done via the +libcacard library, whose development package must be installed prior to +building QEMU: + +In redhat/fedora:: + + yum install libcacard-devel + +In ubuntu:: + + apt-get install libcacard-dev + +Configuring and building:: + + ./configure --enable-smartcard && make + +Using ccid-card-emulated with hardware +-------------------------------------- +Assuming you have a working smartcard on the host with the current +user, using libcacard, QEMU acts as another client using ccid-card-emulated:: + + qemu -usb -device usb-ccid -device ccid-card-emulated + +Using ccid-card-emulated with certificates stored in files +---------------------------------------------------------- +You must create the CA and card certificates. This is a one time process. +We use NSS certificates:: + + mkdir fake-smartcard + cd fake-smartcard + certutil -N -d sql:$PWD + certutil -S -d sql:$PWD -s "CN=Fake Smart Card CA" -x -t TC,TC,TC -n fake-smartcard-ca + certutil -S -d sql:$PWD -t ,, -s "CN=John Doe" -n id-cert -c fake-smartcard-ca + certutil -S -d sql:$PWD -t ,, -s "CN=John Doe (signing)" --nsCertType smime -n signing-cert -c fake-smartcard-ca + certutil -S -d sql:$PWD -t ,, -s "CN=John Doe (encryption)" --nsCertType sslClient -n encryption-cert -c fake-smartcard-ca + +Note: you must have exactly three certificates. + +You can use the emulated card type with the certificates backend:: + + qemu -usb -device usb-ccid -device ccid-card-emulated,backend=certificates,db=sql:$PWD,cert1=id-cert,cert2=signing-cert,cert3=encryption-cert + +To use the certificates in the guest, export the CA certificate:: + + certutil -L -r -d sql:$PWD -o fake-smartcard-ca.cer -n fake-smartcard-ca + +and import it in the guest:: + + certutil -A -d /etc/pki/nssdb -i fake-smartcard-ca.cer -t TC,TC,TC -n fake-smartcard-ca + +In a Linux guest you can then use the CoolKey PKCS #11 module to access +the card:: + + certutil -d /etc/pki/nssdb -L -h all + +It will prompt you for the PIN (which is the password you assigned to the +certificate database early on), and then show you all three certificates +together with the manually imported CA cert:: + + Certificate Nickname Trust Attributes + fake-smartcard-ca CT,C,C + John Doe:CAC ID Certificate u,u,u + John Doe:CAC Email Signature Certificate u,u,u + John Doe:CAC Email Encryption Certificate u,u,u + +If this does not happen, CoolKey is not installed or not registered with +NSS. Registration can be done from Firefox or the command line:: + + modutil -dbdir /etc/pki/nssdb -add "CAC Module" -libfile /usr/lib64/pkcs11/libcoolkeypk11.so + modutil -dbdir /etc/pki/nssdb -list + +Using ccid-card-passthru with client side hardware +-------------------------------------------------- +On the host specify the ccid-card-passthru device with a suitable chardev:: + + qemu -chardev socket,server=on,host=0.0.0.0,port=2001,id=ccid,wait=off \ + -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid + +On the client run vscclient, built when you built QEMU:: + + vscclient 2001 + +Using ccid-card-passthru with client side certificates +------------------------------------------------------ +This case is not particularly useful, but you can use it to debug +your setup. + +Follow instructions above, except run QEMU and vscclient as follows. + +Run qemu as per above, and run vscclient from the "fake-smartcard" +directory as follows:: + + qemu -chardev socket,server=on,host=0.0.0.0,port=2001,id=ccid,wait=off \ + -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid + vscclient -e "db=\"sql:$PWD\" use_hw=no soft=(,Test,CAC,,id-cert,signing-cert,encryption-cert)" 2001 + + +Passthrough protocol scenario +----------------------------- +This is a typical interchange of messages when using the passthru card device. +usb-ccid is a usb device. It defaults to an unattached usb device on startup. +usb-ccid expects a chardev and expects the protocol defined in +cac_card/vscard_common.h to be passed over that. +The usb-ccid device can be in one of three modes: + +* detached +* attached with no card +* attached with card + +A typical interchange is (the arrow shows who started each exchange, it can be client +originated or guest originated):: + + client event | vscclient | passthru | usb-ccid | guest event + ------------------------------------------------------------------------------------------------ + | VSC_Init | | | + | VSC_ReaderAdd | | attach | + | | | | sees new usb device. + card inserted -> | | | | + | VSC_ATR | insert | insert | see new card + | | | | + | VSC_APDU | VSC_APDU | | <- guest sends APDU + client <-> physical | | | | + card APDU exchange | | | | + client response -> | VSC_APDU | VSC_APDU | | receive APDU response + ... + [APDU<->APDU repeats several times] + ... + card removed -> | | | | + | VSC_CardRemove | remove | remove | card removed + ... + [(card insert, apdu's, card remove) repeat] + ... + kill/quit | | | | + vscclient | | | | + | VSC_ReaderRemove | | detach | + | | | | usb device removed. + +libcacard +--------- +Both ccid-card-emulated and vscclient use libcacard as the card emulator. +libcacard implements a completely virtual CAC (DoD standard for smart +cards) compliant card and uses NSS to retrieve certificates and do +any encryption. The backend can then be a real reader and card, or +certificates stored in files. diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst index 215946f813..dcf4add0e7 100644 --- a/docs/system/i386/amd-memory-encryption.rst +++ b/docs/system/i386/amd-memory-encryption.rst @@ -47,7 +47,7 @@ The guest policy is passed as plaintext. A hypervisor may choose to read it, but should not modify it (any modification of the policy bits will result in bad measurement). The guest policy is a 4-byte data structure containing several flags that restricts what can be done on a running SEV guest. -See KM Spec section 3 and 6.2 for more details. +See SEV API Spec ([SEVAPI]_) section 3 and 6.2 for more details. The guest policy can be provided via the ``policy`` property:: @@ -92,7 +92,7 @@ expects. ``LAUNCH_FINISH`` finalizes the guest launch and destroys the cryptographic context. -See SEV KM API Spec ([SEVKM]_) 'Launching a guest' usage flow (Appendix A) for the +See SEV API Spec ([SEVAPI]_) 'Launching a guest' usage flow (Appendix A) for the complete flow chart. To launch a SEV guest:: @@ -118,6 +118,49 @@ a SEV-ES guest: - Requires in-kernel irqchip - the burden is placed on the hypervisor to manage booting APs. +Calculating expected guest launch measurement +--------------------------------------------- + +In order to verify the guest launch measurement, The Guest Owner must compute +it in the exact same way as it is calculated by the AMD-SP. SEV API Spec +([SEVAPI]_) section 6.5.1 describes the AMD-SP operations: + + GCTX.LD is finalized, producing the hash digest of all plaintext data + imported into the guest. + + The launch measurement is calculated as: + + HMAC(0x04 || API_MAJOR || API_MINOR || BUILD || GCTX.POLICY || GCTX.LD || MNONCE; GCTX.TIK) + + where "||" represents concatenation. + +The values of API_MAJOR, API_MINOR, BUILD, and GCTX.POLICY can be obtained +from the ``query-sev`` qmp command. + +The value of MNONCE is part of the response of ``query-sev-launch-measure``: it +is the last 16 bytes of the base64-decoded data field (see SEV API Spec +([SEVAPI]_) section 6.5.2 Table 52: LAUNCH_MEASURE Measurement Buffer). + +The value of GCTX.LD is +``SHA256(firmware_blob || kernel_hashes_blob || vmsas_blob)``, where: + +* ``firmware_blob`` is the content of the entire firmware flash file (for + example, ``OVMF.fd``). Note that you must build a stateless firmware file + which doesn't use an NVRAM store, because the NVRAM area is not measured, and + therefore it is not secure to use a firmware which uses state from an NVRAM + store. +* if kernel is used, and ``kernel-hashes=on``, then ``kernel_hashes_blob`` is + the content of PaddedSevHashTable (including the zero padding), which itself + includes the hashes of kernel, initrd, and cmdline that are passed to the + guest. The PaddedSevHashTable struct is defined in ``target/i386/sev.c``. +* if SEV-ES is enabled (``policy & 0x4 != 0``), ``vmsas_blob`` is the + concatenation of all VMSAs of the guest vcpus. Each VMSA is 4096 bytes long; + its content is defined inside Linux kernel code as ``struct vmcb_save_area``, + or in AMD APM Volume 2 ([APMVOL2]_) Table B-2: VMCB Layout, State Save Area. + +If kernel hashes are not used, or SEV-ES is disabled, use empty blobs for +``kernel_hashes_blob`` and ``vmsas_blob`` as needed. + Debugging --------- @@ -142,8 +185,11 @@ References `AMD Memory Encryption whitepaper `_ -.. [SEVKM] `Secure Encrypted Virtualization Key Management - `_ +.. [SEVAPI] `Secure Encrypted Virtualization API + `_ + +.. [APMVOL2] `AMD64 Architecture Programmer's Manual Volume 2: System Programming + `_ KVM Forum slides: diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index 08ce3c4177..f8ecec95f3 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -63,6 +63,22 @@ The following machine-specific options are supported: When this option is "on", ACLINT devices will be emulated instead of SiFive CLINT. When not specified, this option is assumed to be "off". +- aia=[none|aplic|aplic-imsic] + + This option allows selecting interrupt controller defined by the AIA + (advanced interrupt architecture) specification. The "aia=aplic" selects + APLIC (advanced platform level interrupt controller) to handle wired + interrupts whereas the "aia=aplic-imsic" selects APLIC and IMSIC (incoming + message signaled interrupt controller) to handle both wired interrupts and + MSIs. When not specified, this option is assumed to be "none" which selects + SiFive PLIC to handle wired interrupts. + +- aia-guests=nnn + + The number of per-HART VS-level AIA IMSIC pages to be emulated for a guest + having AIA IMSIC (i.e. "aia=aplic-imsic" selected). When not specified, + the default number of per-HART VS-level AIA IMSIC pages is 0. + Running Linux kernel -------------------- @@ -146,3 +162,23 @@ The minimal QEMU commands to run U-Boot SPL are: To test 32-bit U-Boot images, switch to use qemu-riscv32_smode_defconfig and riscv32_spl_defconfig builds, and replace ``qemu-system-riscv64`` with ``qemu-system-riscv32`` in the command lines above to boot the 32-bit U-Boot. + +Enabling TPM +------------ + +A TPM device can be connected to the virt board by following the steps below. + +First launch the TPM emulator + + swtpm socket --tpm2 -t -d --tpmstate dir=/tmp/tpm \ + --ctrl type=unixio,path=swtpm-sock + +Then launch QEMU with: + + ... + -chardev socket,id=chrtpm,path=swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis-device,tpmdev=tpm0 + +The TPM device can be seen in the memory tree and the generated device +tree and should be accessible from the guest software. diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index 6031f96893..4c950f6199 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -27,7 +27,7 @@ Options .. program:: qemu-nbd *filename* is a disk image filename, or a set of block -driver options if ``--image-opts`` is specified. +driver options if :option:`--image-opts` is specified. *dev* is an NBD device. @@ -165,9 +165,22 @@ driver options if ``--image-opts`` is specified. .. option:: --tls-creds=ID Enable mandatory TLS encryption for the server by setting the ID - of the TLS credentials object previously created with the --object - option; or provide the credentials needed for connecting as a client - in list mode. + of the TLS credentials object previously created with the + :option:`--object` option; or provide the credentials needed for + connecting as a client in list mode. + +.. option:: --tls-hostname=hostname + + When validating an x509 certificate received over a TLS connection, + the hostname that the NBD client used to connect will be checked + against information in the server provided certificate. Sometimes + it might be required to override the hostname used to perform this + check. For example, if the NBD client is using a tunnel from localhost + to connect to the remote server, the :option:`--tls-hostname` option should + be used to set the officially expected hostname of the remote NBD + server. This can also be used if accessing NBD over a UNIX socket + where there is no inherent hostname available. This is only permitted + when acting as a NBD client with the :option:`--list` option. .. option:: --fork diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst index 878e6a5c5c..8b97592663 100644 --- a/docs/tools/qemu-storage-daemon.rst +++ b/docs/tools/qemu-storage-daemon.rst @@ -154,6 +154,13 @@ Standard options: created but before accepting connections. The daemon has started successfully when the pid file is written and clients may begin connecting. +.. option:: --daemonize + + Daemonize the process. The parent process will exit once startup is complete + (i.e., after the pid file has been or would have been written) or failure + occurs. Its exit code reflects whether the child has started up successfully + or failed to do so. + Examples -------- Launch the daemon with QMP monitor socket ``qmp.sock`` so clients can execute diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst index 0c0560203c..e457b13d56 100644 --- a/docs/tools/virtiofsd.rst +++ b/docs/tools/virtiofsd.rst @@ -111,6 +111,11 @@ Options label. Server will try to set that label on newly created file atomically wherever possible. + * killpriv_v2|no_killpriv_v2 - + Enable/disable ``FUSE_HANDLE_KILLPRIV_V2`` support. KILLPRIV_V2 is enabled + by default as long as the client supports it. Enabling this option helps + with performance in write path. + .. option:: --socket-path=PATH Listen on vhost-user UNIX domain socket at PATH. diff --git a/dump/dump.c b/dump/dump.c index a84d8b1598..4d9658ffa2 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "elf.h" #include "exec/hwaddr.h" @@ -55,6 +54,11 @@ static Error *dump_migration_blocker; DIV_ROUND_UP((name_size), 4) + \ DIV_ROUND_UP((desc_size), 4)) * 4) +static inline bool dump_is_64bit(DumpState *s) +{ + return s->dump_info.d_class == ELFCLASS64; +} + uint16_t cpu_to_dump16(DumpState *s, uint16_t val) { if (s->dump_info.d_endian == ELFDATA2LSB) { @@ -124,6 +128,12 @@ static int fd_write_vmcore(const void *buf, size_t size, void *opaque) static void write_elf64_header(DumpState *s, Error **errp) { + /* + * phnum in the elf header is 16 bit, if we have more segments we + * set phnum to PN_XNUM and write the real number of segments to a + * special section. + */ + uint16_t phnum = MIN(s->phdr_num, PN_XNUM); Elf64_Ehdr elf_header; int ret; @@ -136,15 +146,13 @@ static void write_elf64_header(DumpState *s, Error **errp) elf_header.e_machine = cpu_to_dump16(s, s->dump_info.d_machine); elf_header.e_version = cpu_to_dump32(s, EV_CURRENT); elf_header.e_ehsize = cpu_to_dump16(s, sizeof(elf_header)); - elf_header.e_phoff = cpu_to_dump64(s, sizeof(Elf64_Ehdr)); + elf_header.e_phoff = cpu_to_dump64(s, s->phdr_offset); elf_header.e_phentsize = cpu_to_dump16(s, sizeof(Elf64_Phdr)); - elf_header.e_phnum = cpu_to_dump16(s, s->phdr_num); - if (s->have_section) { - uint64_t shoff = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) * s->sh_info; - - elf_header.e_shoff = cpu_to_dump64(s, shoff); + elf_header.e_phnum = cpu_to_dump16(s, phnum); + if (s->shdr_num) { + elf_header.e_shoff = cpu_to_dump64(s, s->shdr_offset); elf_header.e_shentsize = cpu_to_dump16(s, sizeof(Elf64_Shdr)); - elf_header.e_shnum = cpu_to_dump16(s, 1); + elf_header.e_shnum = cpu_to_dump16(s, s->shdr_num); } ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); @@ -155,6 +163,12 @@ static void write_elf64_header(DumpState *s, Error **errp) static void write_elf32_header(DumpState *s, Error **errp) { + /* + * phnum in the elf header is 16 bit, if we have more segments we + * set phnum to PN_XNUM and write the real number of segments to a + * special section. + */ + uint16_t phnum = MIN(s->phdr_num, PN_XNUM); Elf32_Ehdr elf_header; int ret; @@ -167,15 +181,13 @@ static void write_elf32_header(DumpState *s, Error **errp) elf_header.e_machine = cpu_to_dump16(s, s->dump_info.d_machine); elf_header.e_version = cpu_to_dump32(s, EV_CURRENT); elf_header.e_ehsize = cpu_to_dump16(s, sizeof(elf_header)); - elf_header.e_phoff = cpu_to_dump32(s, sizeof(Elf32_Ehdr)); + elf_header.e_phoff = cpu_to_dump32(s, s->phdr_offset); elf_header.e_phentsize = cpu_to_dump16(s, sizeof(Elf32_Phdr)); - elf_header.e_phnum = cpu_to_dump16(s, s->phdr_num); - if (s->have_section) { - uint32_t shoff = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * s->sh_info; - - elf_header.e_shoff = cpu_to_dump32(s, shoff); + elf_header.e_phnum = cpu_to_dump16(s, phnum); + if (s->shdr_num) { + elf_header.e_shoff = cpu_to_dump32(s, s->shdr_offset); elf_header.e_shentsize = cpu_to_dump16(s, sizeof(Elf32_Shdr)); - elf_header.e_shnum = cpu_to_dump16(s, 1); + elf_header.e_shnum = cpu_to_dump16(s, s->shdr_num); } ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); @@ -233,25 +245,15 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, } } -static void write_elf64_note(DumpState *s, Error **errp) +static void write_elf64_phdr_note(DumpState *s, Elf64_Phdr *phdr) { - Elf64_Phdr phdr; - hwaddr begin = s->memory_offset - s->note_size; - int ret; - - memset(&phdr, 0, sizeof(Elf64_Phdr)); - phdr.p_type = cpu_to_dump32(s, PT_NOTE); - phdr.p_offset = cpu_to_dump64(s, begin); - phdr.p_paddr = 0; - phdr.p_filesz = cpu_to_dump64(s, s->note_size); - phdr.p_memsz = cpu_to_dump64(s, s->note_size); - phdr.p_vaddr = 0; - - ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s); - if (ret < 0) { - error_setg_errno(errp, -ret, - "dump: failed to write program header table"); - } + memset(phdr, 0, sizeof(*phdr)); + phdr->p_type = cpu_to_dump32(s, PT_NOTE); + phdr->p_offset = cpu_to_dump64(s, s->note_offset); + phdr->p_paddr = 0; + phdr->p_filesz = cpu_to_dump64(s, s->note_size); + phdr->p_memsz = cpu_to_dump64(s, s->note_size); + phdr->p_vaddr = 0; } static inline int cpu_index(CPUState *cpu) @@ -299,25 +301,15 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s, write_guest_note(f, s, errp); } -static void write_elf32_note(DumpState *s, Error **errp) +static void write_elf32_phdr_note(DumpState *s, Elf32_Phdr *phdr) { - hwaddr begin = s->memory_offset - s->note_size; - Elf32_Phdr phdr; - int ret; - - memset(&phdr, 0, sizeof(Elf32_Phdr)); - phdr.p_type = cpu_to_dump32(s, PT_NOTE); - phdr.p_offset = cpu_to_dump32(s, begin); - phdr.p_paddr = 0; - phdr.p_filesz = cpu_to_dump32(s, s->note_size); - phdr.p_memsz = cpu_to_dump32(s, s->note_size); - phdr.p_vaddr = 0; - - ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s); - if (ret < 0) { - error_setg_errno(errp, -ret, - "dump: failed to write program header table"); - } + memset(phdr, 0, sizeof(*phdr)); + phdr->p_type = cpu_to_dump32(s, PT_NOTE); + phdr->p_offset = cpu_to_dump32(s, s->note_offset); + phdr->p_paddr = 0; + phdr->p_filesz = cpu_to_dump32(s, s->note_size); + phdr->p_memsz = cpu_to_dump32(s, s->note_size); + phdr->p_vaddr = 0; } static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s, @@ -347,6 +339,32 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s, write_guest_note(f, s, errp); } +static void write_elf_phdr_note(DumpState *s, Error **errp) +{ + ERRP_GUARD(); + Elf32_Phdr phdr32; + Elf64_Phdr phdr64; + void *phdr; + size_t size; + int ret; + + if (dump_is_64bit(s)) { + write_elf64_phdr_note(s, &phdr64); + size = sizeof(phdr64); + phdr = &phdr64; + } else { + write_elf32_phdr_note(s, &phdr32); + size = sizeof(phdr32); + phdr = &phdr32; + } + + ret = fd_write_vmcore(phdr, size, s); + if (ret < 0) { + error_setg_errno(errp, -ret, + "dump: failed to write program header table"); + } +} + static void write_elf_section(DumpState *s, int type, Error **errp) { Elf32_Shdr shdr32; @@ -358,12 +376,12 @@ static void write_elf_section(DumpState *s, int type, Error **errp) if (type == 0) { shdr_size = sizeof(Elf32_Shdr); memset(&shdr32, 0, shdr_size); - shdr32.sh_info = cpu_to_dump32(s, s->sh_info); + shdr32.sh_info = cpu_to_dump32(s, s->phdr_num); shdr = &shdr32; } else { shdr_size = sizeof(Elf64_Shdr); memset(&shdr64, 0, shdr_size); - shdr64.sh_info = cpu_to_dump32(s, s->sh_info); + shdr64.sh_info = cpu_to_dump32(s, s->phdr_num); shdr = &shdr64; } @@ -390,23 +408,21 @@ static void write_data(DumpState *s, void *buf, int length, Error **errp) static void write_memory(DumpState *s, GuestPhysBlock *block, ram_addr_t start, int64_t size, Error **errp) { + ERRP_GUARD(); int64_t i; - Error *local_err = NULL; for (i = 0; i < size / s->dump_info.page_size; i++) { write_data(s, block->host_addr + start + i * s->dump_info.page_size, - s->dump_info.page_size, &local_err); - if (local_err) { - error_propagate(errp, local_err); + s->dump_info.page_size, errp); + if (*errp) { return; } } if ((size % s->dump_info.page_size) != 0) { write_data(s, block->host_addr + start + i * s->dump_info.page_size, - size % s->dump_info.page_size, &local_err); - if (local_err) { - error_propagate(errp, local_err); + size % s->dump_info.page_size, errp); + if (*errp) { return; } } @@ -476,45 +492,46 @@ static void get_offset_range(hwaddr phys_addr, static void write_elf_loads(DumpState *s, Error **errp) { + ERRP_GUARD(); hwaddr offset, filesz; MemoryMapping *memory_mapping; uint32_t phdr_index = 1; - uint32_t max_index; - Error *local_err = NULL; - - if (s->have_section) { - max_index = s->sh_info; - } else { - max_index = s->phdr_num; - } QTAILQ_FOREACH(memory_mapping, &s->list.head, next) { get_offset_range(memory_mapping->phys_addr, memory_mapping->length, s, &offset, &filesz); - if (s->dump_info.d_class == ELFCLASS64) { + if (dump_is_64bit(s)) { write_elf64_load(s, memory_mapping, phdr_index++, offset, - filesz, &local_err); + filesz, errp); } else { write_elf32_load(s, memory_mapping, phdr_index++, offset, - filesz, &local_err); + filesz, errp); } - if (local_err) { - error_propagate(errp, local_err); + if (*errp) { return; } - if (phdr_index >= max_index) { + if (phdr_index >= s->phdr_num) { break; } } } +static void write_elf_notes(DumpState *s, Error **errp) +{ + if (dump_is_64bit(s)) { + write_elf64_notes(fd_write_vmcore, s, errp); + } else { + write_elf32_notes(fd_write_vmcore, s, errp); + } +} + /* write elf header, PT_NOTE and elf note to vmcore. */ static void dump_begin(DumpState *s, Error **errp) { - Error *local_err = NULL; + ERRP_GUARD(); /* * the vmcore's format is: @@ -541,77 +558,37 @@ static void dump_begin(DumpState *s, Error **errp) */ /* write elf header to vmcore */ - if (s->dump_info.d_class == ELFCLASS64) { - write_elf64_header(s, &local_err); + if (dump_is_64bit(s)) { + write_elf64_header(s, errp); } else { - write_elf32_header(s, &local_err); + write_elf32_header(s, errp); } - if (local_err) { - error_propagate(errp, local_err); + if (*errp) { return; } - if (s->dump_info.d_class == ELFCLASS64) { - /* write PT_NOTE to vmcore */ - write_elf64_note(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + /* write PT_NOTE to vmcore */ + write_elf_phdr_note(s, errp); + if (*errp) { + return; + } - /* write all PT_LOAD to vmcore */ - write_elf_loads(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + /* write all PT_LOAD to vmcore */ + write_elf_loads(s, errp); + if (*errp) { + return; + } - /* write section to vmcore */ - if (s->have_section) { - write_elf_section(s, 1, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - /* write notes to vmcore */ - write_elf64_notes(fd_write_vmcore, s, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } else { - /* write PT_NOTE to vmcore */ - write_elf32_note(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - /* write all PT_LOAD to vmcore */ - write_elf_loads(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - /* write section to vmcore */ - if (s->have_section) { - write_elf_section(s, 0, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - /* write notes to vmcore */ - write_elf32_notes(fd_write_vmcore, s, &local_err); - if (local_err) { - error_propagate(errp, local_err); + /* write section to vmcore */ + if (s->shdr_num) { + write_elf_section(s, 1, errp); + if (*errp) { return; } } + + /* write notes to vmcore */ + write_elf_notes(s, errp); } static int get_next_block(DumpState *s, GuestPhysBlock *block) @@ -644,9 +621,9 @@ static int get_next_block(DumpState *s, GuestPhysBlock *block) /* write all memory to vmcore */ static void dump_iterate(DumpState *s, Error **errp) { + ERRP_GUARD(); GuestPhysBlock *block; int64_t size; - Error *local_err = NULL; do { block = s->next_block; @@ -658,9 +635,8 @@ static void dump_iterate(DumpState *s, Error **errp) size -= block->target_end - (s->begin + s->length); } } - write_memory(s, block, s->start, size, &local_err); - if (local_err) { - error_propagate(errp, local_err); + write_memory(s, block, s->start, size, errp); + if (*errp) { return; } @@ -669,11 +645,10 @@ static void dump_iterate(DumpState *s, Error **errp) static void create_vmcore(DumpState *s, Error **errp) { - Error *local_err = NULL; + ERRP_GUARD(); - dump_begin(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); + dump_begin(s, errp); + if (*errp) { return; } @@ -772,7 +747,7 @@ static void get_note_sizes(DumpState *s, const void *note, uint64_t name_sz; uint64_t desc_sz; - if (s->dump_info.d_class == ELFCLASS64) { + if (dump_is_64bit(s)) { const Elf64_Nhdr *hdr = note; note_head_sz = sizeof(Elf64_Nhdr); name_sz = tswap64(hdr->n_namesz); @@ -810,6 +785,7 @@ static bool note_name_equal(DumpState *s, /* write common header, sub header and elf note to vmcore */ static void create_header32(DumpState *s, Error **errp) { + ERRP_GUARD(); DiskDumpHeader32 *dh = NULL; KdumpSubHeader32 *kh = NULL; size_t size; @@ -818,7 +794,6 @@ static void create_header32(DumpState *s, Error **errp) uint32_t bitmap_blocks; uint32_t status = 0; uint64_t offset_note; - Error *local_err = NULL; /* write common header, the version of kdump-compressed format is 6th */ size = sizeof(DiskDumpHeader32); @@ -894,9 +869,8 @@ static void create_header32(DumpState *s, Error **errp) s->note_buf_offset = 0; /* use s->note_buf to store notes temporarily */ - write_elf32_notes(buf_write_note, s, &local_err); - if (local_err) { - error_propagate(errp, local_err); + write_elf32_notes(buf_write_note, s, errp); + if (*errp) { goto out; } if (write_buffer(s->fd, offset_note, s->note_buf, @@ -922,6 +896,7 @@ out: /* write common header, sub header and elf note to vmcore */ static void create_header64(DumpState *s, Error **errp) { + ERRP_GUARD(); DiskDumpHeader64 *dh = NULL; KdumpSubHeader64 *kh = NULL; size_t size; @@ -930,7 +905,6 @@ static void create_header64(DumpState *s, Error **errp) uint32_t bitmap_blocks; uint32_t status = 0; uint64_t offset_note; - Error *local_err = NULL; /* write common header, the version of kdump-compressed format is 6th */ size = sizeof(DiskDumpHeader64); @@ -1006,9 +980,8 @@ static void create_header64(DumpState *s, Error **errp) s->note_buf_offset = 0; /* use s->note_buf to store notes temporarily */ - write_elf64_notes(buf_write_note, s, &local_err); - if (local_err) { - error_propagate(errp, local_err); + write_elf64_notes(buf_write_note, s, errp); + if (*errp) { goto out; } @@ -1034,10 +1007,10 @@ out: static void write_dump_header(DumpState *s, Error **errp) { - if (s->dump_info.d_class == ELFCLASS32) { - create_header32(s, errp); - } else { + if (dump_is_64bit(s)) { create_header64(s, errp); + } else { + create_header32(s, errp); } } @@ -1464,8 +1437,8 @@ out: static void create_kdump_vmcore(DumpState *s, Error **errp) { + ERRP_GUARD(); int ret; - Error *local_err = NULL; /* * the kdump-compressed format is: @@ -1495,21 +1468,18 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) return; } - write_dump_header(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); + write_dump_header(s, errp); + if (*errp) { return; } - write_dump_bitmap(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); + write_dump_bitmap(s, errp); + if (*errp) { return; } - write_dump_pages(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); + write_dump_pages(s, errp); + if (*errp) { return; } @@ -1564,7 +1534,7 @@ static void dump_state_prepare(DumpState *s) *s = (DumpState) { .status = DUMP_STATUS_ACTIVE }; } -bool dump_in_progress(void) +bool qemu_system_dump_in_progress(void) { DumpState *state = &dump_state_global; return (qatomic_read(&state->status) == DUMP_STATUS_ACTIVE); @@ -1639,10 +1609,10 @@ static void dump_init(DumpState *s, int fd, bool has_format, DumpGuestMemoryFormat format, bool paging, bool has_filter, int64_t begin, int64_t length, Error **errp) { + ERRP_GUARD(); VMCoreInfoState *vmci = vmcoreinfo_find(); CPUState *cpu; int nr_cpus; - Error *err = NULL; int ret; s->has_format = has_format; @@ -1727,8 +1697,8 @@ static void dump_init(DumpState *s, int fd, bool has_format, uint32_t size; uint16_t format; - note_head_size = s->dump_info.d_class == ELFCLASS32 ? - sizeof(Elf32_Nhdr) : sizeof(Elf64_Nhdr); + note_head_size = dump_is_64bit(s) ? + sizeof(Elf64_Nhdr) : sizeof(Elf32_Nhdr); format = le16_to_cpu(vmci->vmcoreinfo.guest_format); size = le32_to_cpu(vmci->vmcoreinfo.size); @@ -1761,9 +1731,8 @@ static void dump_init(DumpState *s, int fd, bool has_format, /* get memory mapping */ if (paging) { - qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, &err); - if (err != NULL) { - error_propagate(errp, err); + qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, errp); + if (*errp) { goto cleanup; } } else { @@ -1818,39 +1787,31 @@ static void dump_init(DumpState *s, int fd, bool has_format, */ s->phdr_num = 1; /* PT_NOTE */ if (s->list.num < UINT16_MAX - 2) { + s->shdr_num = 0; s->phdr_num += s->list.num; - s->have_section = false; } else { - s->have_section = true; - s->phdr_num = PN_XNUM; - s->sh_info = 1; /* PT_NOTE */ + /* sh_info of section 0 holds the real number of phdrs */ + s->shdr_num = 1; /* the type of shdr->sh_info is uint32_t, so we should avoid overflow */ if (s->list.num <= UINT32_MAX - 1) { - s->sh_info += s->list.num; + s->phdr_num += s->list.num; } else { - s->sh_info = UINT32_MAX; + s->phdr_num = UINT32_MAX; } } - if (s->dump_info.d_class == ELFCLASS64) { - if (s->have_section) { - s->memory_offset = sizeof(Elf64_Ehdr) + - sizeof(Elf64_Phdr) * s->sh_info + - sizeof(Elf64_Shdr) + s->note_size; - } else { - s->memory_offset = sizeof(Elf64_Ehdr) + - sizeof(Elf64_Phdr) * s->phdr_num + s->note_size; - } + if (dump_is_64bit(s)) { + s->phdr_offset = sizeof(Elf64_Ehdr); + s->shdr_offset = s->phdr_offset + sizeof(Elf64_Phdr) * s->phdr_num; + s->note_offset = s->shdr_offset + sizeof(Elf64_Shdr) * s->shdr_num; + s->memory_offset = s->note_offset + s->note_size; } else { - if (s->have_section) { - s->memory_offset = sizeof(Elf32_Ehdr) + - sizeof(Elf32_Phdr) * s->sh_info + - sizeof(Elf32_Shdr) + s->note_size; - } else { - s->memory_offset = sizeof(Elf32_Ehdr) + - sizeof(Elf32_Phdr) * s->phdr_num + s->note_size; - } + + s->phdr_offset = sizeof(Elf32_Ehdr); + s->shdr_offset = s->phdr_offset + sizeof(Elf32_Phdr) * s->phdr_num; + s->note_offset = s->shdr_offset + sizeof(Elf32_Shdr) * s->shdr_num; + s->memory_offset = s->note_offset + s->note_size; } return; @@ -1862,33 +1823,32 @@ cleanup: /* this operation might be time consuming. */ static void dump_process(DumpState *s, Error **errp) { - Error *local_err = NULL; + ERRP_GUARD(); DumpQueryResult *result = NULL; if (s->has_format && s->format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { #ifdef TARGET_X86_64 - create_win_dump(s, &local_err); + create_win_dump(s, errp); #endif } else if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { - create_kdump_vmcore(s, &local_err); + create_kdump_vmcore(s, errp); } else { - create_vmcore(s, &local_err); + create_vmcore(s, errp); } /* make sure status is written after written_size updates */ smp_wmb(); qatomic_set(&s->status, - (local_err ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED)); + (*errp ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED)); /* send DUMP_COMPLETED message (unconditionally) */ result = qmp_query_dump(NULL); /* should never fail */ assert(result); - qapi_event_send_dump_completed(result, !!local_err, (local_err ? - error_get_pretty(local_err) : NULL)); + qapi_event_send_dump_completed(result, !!*errp, (*errp ? + error_get_pretty(*errp) : NULL)); qapi_free_DumpQueryResult(result); - error_propagate(errp, local_err); dump_cleanup(s); } @@ -1917,10 +1877,10 @@ void qmp_dump_guest_memory(bool paging, const char *file, int64_t length, bool has_format, DumpGuestMemoryFormat format, Error **errp) { + ERRP_GUARD(); const char *p; int fd = -1; DumpState *s; - Error *local_err = NULL; bool detach_p = false; if (runstate_check(RUN_STATE_INMIGRATE)) { @@ -1930,7 +1890,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, /* if there is a dump in background, we should wait until the dump * finished */ - if (dump_in_progress()) { + if (qemu_system_dump_in_progress()) { error_setg(errp, "There is a dump in process, please wait."); return; } @@ -2020,9 +1980,8 @@ void qmp_dump_guest_memory(bool paging, const char *file, dump_state_prepare(s); dump_init(s, fd, has_format, format, paging, has_begin, - begin, length, &local_err); - if (local_err) { - error_propagate(errp, local_err); + begin, length, errp); + if (*errp) { qatomic_set(&s->status, DUMP_STATUS_FAILED); return; } @@ -2041,7 +2000,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) { DumpGuestMemoryCapability *cap = - g_malloc0(sizeof(DumpGuestMemoryCapability)); + g_new0(DumpGuestMemoryCapability, 1); DumpGuestMemoryFormatList **tail = &cap->formats; /* elf is always available */ diff --git a/dump/win_dump.c b/dump/win_dump.c index c5eb5a9aac..fd91350fbb 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "elf.h" #include "exec/hwaddr.h" @@ -24,11 +23,31 @@ #include "hw/misc/vmcoreinfo.h" #include "win_dump.h" -static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp) +static size_t win_dump_ptr_size(bool x64) +{ + return x64 ? sizeof(uint64_t) : sizeof(uint32_t); +} + +#define _WIN_DUMP_FIELD(f) (x64 ? h->x64.f : h->x32.f) +#define WIN_DUMP_FIELD(field) _WIN_DUMP_FIELD(field) + +#define _WIN_DUMP_FIELD_PTR(f) (x64 ? (void *)&h->x64.f : (void *)&h->x32.f) +#define WIN_DUMP_FIELD_PTR(field) _WIN_DUMP_FIELD_PTR(field) + +#define _WIN_DUMP_FIELD_SIZE(f) (x64 ? sizeof(h->x64.f) : sizeof(h->x32.f)) +#define WIN_DUMP_FIELD_SIZE(field) _WIN_DUMP_FIELD_SIZE(field) + +static size_t win_dump_ctx_size(bool x64) +{ + return x64 ? sizeof(WinContext64) : sizeof(WinContext32); +} + +static size_t write_run(uint64_t base_page, uint64_t page_count, + int fd, Error **errp) { void *buf; - uint64_t addr = run->BasePage << TARGET_PAGE_BITS; - uint64_t size = run->PageCount << TARGET_PAGE_BITS; + uint64_t addr = base_page << TARGET_PAGE_BITS; + uint64_t size = page_count << TARGET_PAGE_BITS; uint64_t len, l; size_t total = 0; @@ -57,15 +76,16 @@ static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp) return total; } -static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp) +static void write_runs(DumpState *s, WinDumpHeader *h, bool x64, Error **errp) { - WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock; - WinDumpPhyMemRun64 *run = desc->Run; + uint64_t BasePage, PageCount; Error *local_err = NULL; int i; - for (i = 0; i < desc->NumberOfRuns; i++) { - s->written_size += write_run(run + i, s->fd, &local_err); + for (i = 0; i < WIN_DUMP_FIELD(PhysicalMemoryBlock.NumberOfRuns); i++) { + BasePage = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].BasePage); + PageCount = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].PageCount); + s->written_size += write_run(BasePage, PageCount, s->fd, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -73,30 +93,45 @@ static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp) } } -static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp) +static int cpu_read_ptr(bool x64, CPUState *cpu, uint64_t addr, uint64_t *ptr) +{ + int ret; + uint32_t ptr32; + uint64_t ptr64; + + ret = cpu_memory_rw_debug(cpu, addr, x64 ? (void *)&ptr64 : (void *)&ptr32, + win_dump_ptr_size(x64), 0); + + *ptr = x64 ? ptr64 : ptr32; + + return ret; +} + +static void patch_mm_pfn_database(WinDumpHeader *h, bool x64, Error **errp) { if (cpu_memory_rw_debug(first_cpu, - h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64, - (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) { + WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_MM_PFN_DATABASE_OFFSET, + WIN_DUMP_FIELD_PTR(PfnDatabase), + WIN_DUMP_FIELD_SIZE(PfnDatabase), 0)) { error_setg(errp, "win-dump: failed to read MmPfnDatabase"); return; } } -static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp) +static void patch_bugcheck_data(WinDumpHeader *h, bool x64, Error **errp) { uint64_t KiBugcheckData; - if (cpu_memory_rw_debug(first_cpu, - h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64, - (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) { + if (cpu_read_ptr(x64, first_cpu, + WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_KI_BUGCHECK_DATA_OFFSET, + &KiBugcheckData)) { error_setg(errp, "win-dump: failed to read KiBugcheckData"); return; } - if (cpu_memory_rw_debug(first_cpu, - KiBugcheckData, - h->BugcheckData, sizeof(h->BugcheckData), 0)) { + if (cpu_memory_rw_debug(first_cpu, KiBugcheckData, + WIN_DUMP_FIELD(BugcheckData), + WIN_DUMP_FIELD_SIZE(BugcheckData), 0)) { error_setg(errp, "win-dump: failed to read bugcheck data"); return; } @@ -105,62 +140,72 @@ static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp) * If BugcheckCode wasn't saved, we consider guest OS as alive. */ - if (!h->BugcheckCode) { - h->BugcheckCode = LIVE_SYSTEM_DUMP; + if (!WIN_DUMP_FIELD(BugcheckCode)) { + *(uint32_t *)WIN_DUMP_FIELD_PTR(BugcheckCode) = LIVE_SYSTEM_DUMP; } } /* * This routine tries to correct mistakes in crashdump header. */ -static void patch_header(WinDumpHeader64 *h) +static void patch_header(WinDumpHeader *h, bool x64) { Error *local_err = NULL; - h->RequiredDumpSpace = sizeof(WinDumpHeader64) + - (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS); - h->PhysicalMemoryBlock.unused = 0; - h->unused1 = 0; + if (x64) { + h->x64.RequiredDumpSpace = sizeof(WinDumpHeader64) + + (h->x64.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS); + h->x64.PhysicalMemoryBlock.unused = 0; + h->x64.unused1 = 0; + } else { + h->x32.RequiredDumpSpace = sizeof(WinDumpHeader32) + + (h->x32.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS); + } - patch_mm_pfn_database(h, &local_err); + patch_mm_pfn_database(h, x64, &local_err); if (local_err) { warn_report_err(local_err); local_err = NULL; } - patch_bugcheck_data(h, &local_err); + patch_bugcheck_data(h, x64, &local_err); if (local_err) { warn_report_err(local_err); } } -static void check_header(WinDumpHeader64 *h, Error **errp) +static bool check_header(WinDumpHeader *h, bool *x64, Error **errp) { const char Signature[] = "PAGE"; - const char ValidDump[] = "DU64"; if (memcmp(h->Signature, Signature, sizeof(h->Signature))) { error_setg(errp, "win-dump: invalid header, expected '%.4s'," " got '%.4s'", Signature, h->Signature); - return; + return false; } - if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) { - error_setg(errp, "win-dump: invalid header, expected '%.4s'," - " got '%.4s'", ValidDump, h->ValidDump); - return; + if (!memcmp(h->ValidDump, "DUMP", sizeof(h->ValidDump))) { + *x64 = false; + } else if (!memcmp(h->ValidDump, "DU64", sizeof(h->ValidDump))) { + *x64 = true; + } else { + error_setg(errp, "win-dump: invalid header, expected 'DUMP' or 'DU64'," + " got '%.4s'", h->ValidDump); + return false; } + + return true; } -static void check_kdbg(WinDumpHeader64 *h, Error **errp) +static void check_kdbg(WinDumpHeader *h, bool x64, Error **errp) { const char OwnerTag[] = "KDBG"; char read_OwnerTag[4]; - uint64_t KdDebuggerDataBlock = h->KdDebuggerDataBlock; + uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock); bool try_fallback = true; try_again: if (cpu_memory_rw_debug(first_cpu, - KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64, + KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET, (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) { error_setg(errp, "win-dump: failed to read OwnerTag"); return; @@ -174,7 +219,7 @@ try_again: * we try to use KDBG obtained by guest driver. */ - KdDebuggerDataBlock = h->BugcheckParameter1; + KdDebuggerDataBlock = WIN_DUMP_FIELD(BugcheckParameter1); try_fallback = false; goto try_again; } else { @@ -185,7 +230,11 @@ try_again: } } - h->KdDebuggerDataBlock = KdDebuggerDataBlock; + if (x64) { + h->x64.KdDebuggerDataBlock = KdDebuggerDataBlock; + } else { + h->x32.KdDebuggerDataBlock = KdDebuggerDataBlock; + } } struct saved_context { @@ -193,24 +242,25 @@ struct saved_context { uint64_t addr; }; -static void patch_and_save_context(WinDumpHeader64 *h, +static void patch_and_save_context(WinDumpHeader *h, bool x64, struct saved_context *saved_ctx, Error **errp) { + uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock); uint64_t KiProcessorBlock; uint16_t OffsetPrcbContext; CPUState *cpu; int i = 0; - if (cpu_memory_rw_debug(first_cpu, - h->KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET64, - (uint8_t *)&KiProcessorBlock, sizeof(KiProcessorBlock), 0)) { + if (cpu_read_ptr(x64, first_cpu, + KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET, + &KiProcessorBlock)) { error_setg(errp, "win-dump: failed to read KiProcessorBlock"); return; } if (cpu_memory_rw_debug(first_cpu, - h->KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET64, + KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET, (uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) { error_setg(errp, "win-dump: failed to read OffsetPrcbContext"); return; @@ -223,17 +273,17 @@ static void patch_and_save_context(WinDumpHeader64 *h, uint64_t Context; WinContext ctx; - if (cpu_memory_rw_debug(first_cpu, - KiProcessorBlock + i * sizeof(uint64_t), - (uint8_t *)&Prcb, sizeof(Prcb), 0)) { + if (cpu_read_ptr(x64, first_cpu, + KiProcessorBlock + i * win_dump_ptr_size(x64), + &Prcb)) { error_setg(errp, "win-dump: failed to read" " CPU #%d PRCB location", i); return; } - if (cpu_memory_rw_debug(first_cpu, + if (cpu_read_ptr(x64, first_cpu, Prcb + OffsetPrcbContext, - (uint8_t *)&Context, sizeof(Context), 0)) { + &Context)) { error_setg(errp, "win-dump: failed to read" " CPU #%d ContextFrame location", i); return; @@ -241,56 +291,88 @@ static void patch_and_save_context(WinDumpHeader64 *h, saved_ctx[i].addr = Context; - ctx = (WinContext){ - .ContextFlags = WIN_CTX_ALL, - .MxCsr = env->mxcsr, - - .SegEs = env->segs[0].selector, - .SegCs = env->segs[1].selector, - .SegSs = env->segs[2].selector, - .SegDs = env->segs[3].selector, - .SegFs = env->segs[4].selector, - .SegGs = env->segs[5].selector, - .EFlags = cpu_compute_eflags(env), - - .Dr0 = env->dr[0], - .Dr1 = env->dr[1], - .Dr2 = env->dr[2], - .Dr3 = env->dr[3], - .Dr6 = env->dr[6], - .Dr7 = env->dr[7], - - .Rax = env->regs[R_EAX], - .Rbx = env->regs[R_EBX], - .Rcx = env->regs[R_ECX], - .Rdx = env->regs[R_EDX], - .Rsp = env->regs[R_ESP], - .Rbp = env->regs[R_EBP], - .Rsi = env->regs[R_ESI], - .Rdi = env->regs[R_EDI], - .R8 = env->regs[8], - .R9 = env->regs[9], - .R10 = env->regs[10], - .R11 = env->regs[11], - .R12 = env->regs[12], - .R13 = env->regs[13], - .R14 = env->regs[14], - .R15 = env->regs[15], - - .Rip = env->eip, - .FltSave = { + if (x64) { + ctx.x64 = (WinContext64){ + .ContextFlags = WIN_CTX64_ALL, .MxCsr = env->mxcsr, - }, - }; + + .SegEs = env->segs[0].selector, + .SegCs = env->segs[1].selector, + .SegSs = env->segs[2].selector, + .SegDs = env->segs[3].selector, + .SegFs = env->segs[4].selector, + .SegGs = env->segs[5].selector, + .EFlags = cpu_compute_eflags(env), + + .Dr0 = env->dr[0], + .Dr1 = env->dr[1], + .Dr2 = env->dr[2], + .Dr3 = env->dr[3], + .Dr6 = env->dr[6], + .Dr7 = env->dr[7], + + .Rax = env->regs[R_EAX], + .Rbx = env->regs[R_EBX], + .Rcx = env->regs[R_ECX], + .Rdx = env->regs[R_EDX], + .Rsp = env->regs[R_ESP], + .Rbp = env->regs[R_EBP], + .Rsi = env->regs[R_ESI], + .Rdi = env->regs[R_EDI], + .R8 = env->regs[8], + .R9 = env->regs[9], + .R10 = env->regs[10], + .R11 = env->regs[11], + .R12 = env->regs[12], + .R13 = env->regs[13], + .R14 = env->regs[14], + .R15 = env->regs[15], + + .Rip = env->eip, + .FltSave = { + .MxCsr = env->mxcsr, + }, + }; + } else { + ctx.x32 = (WinContext32){ + .ContextFlags = WIN_CTX32_FULL | WIN_CTX_DBG, + + .SegEs = env->segs[0].selector, + .SegCs = env->segs[1].selector, + .SegSs = env->segs[2].selector, + .SegDs = env->segs[3].selector, + .SegFs = env->segs[4].selector, + .SegGs = env->segs[5].selector, + .EFlags = cpu_compute_eflags(env), + + .Dr0 = env->dr[0], + .Dr1 = env->dr[1], + .Dr2 = env->dr[2], + .Dr3 = env->dr[3], + .Dr6 = env->dr[6], + .Dr7 = env->dr[7], + + .Eax = env->regs[R_EAX], + .Ebx = env->regs[R_EBX], + .Ecx = env->regs[R_ECX], + .Edx = env->regs[R_EDX], + .Esp = env->regs[R_ESP], + .Ebp = env->regs[R_EBP], + .Esi = env->regs[R_ESI], + .Edi = env->regs[R_EDI], + + .Eip = env->eip, + }; + } if (cpu_memory_rw_debug(first_cpu, Context, - (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 0)) { + &saved_ctx[i].ctx, win_dump_ctx_size(x64), 0)) { error_setg(errp, "win-dump: failed to save CPU #%d context", i); return; } if (cpu_memory_rw_debug(first_cpu, Context, - (uint8_t *)&ctx, sizeof(WinContext), 1)) { + &ctx, win_dump_ctx_size(x64), 1)) { error_setg(errp, "win-dump: failed to write CPU #%d context", i); return; } @@ -299,14 +381,14 @@ static void patch_and_save_context(WinDumpHeader64 *h, } } -static void restore_context(WinDumpHeader64 *h, +static void restore_context(WinDumpHeader *h, bool x64, struct saved_context *saved_ctx) { int i; - for (i = 0; i < h->NumberProcessors; i++) { + for (i = 0; i < WIN_DUMP_FIELD(NumberProcessors); i++) { if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr, - (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 1)) { + &saved_ctx[i].ctx, win_dump_ctx_size(x64), 1)) { warn_report("win-dump: failed to restore CPU #%d context", i); } } @@ -314,69 +396,71 @@ static void restore_context(WinDumpHeader64 *h, void create_win_dump(DumpState *s, Error **errp) { - WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note + - VMCOREINFO_ELF_NOTE_HDR_SIZE); + WinDumpHeader *h = (void *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE); X86CPU *first_x86_cpu = X86_CPU(first_cpu); uint64_t saved_cr3 = first_x86_cpu->env.cr[3]; struct saved_context *saved_ctx = NULL; Error *local_err = NULL; + bool x64 = true; + size_t hdr_size; - if (s->guest_note_size != sizeof(WinDumpHeader64) + - VMCOREINFO_ELF_NOTE_HDR_SIZE) { + if (s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE32 && + s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE64) { error_setg(errp, "win-dump: invalid vmcoreinfo note size"); return; } - check_header(h, &local_err); - if (local_err) { + if (!check_header(h, &x64, &local_err)) { error_propagate(errp, local_err); return; } + hdr_size = x64 ? sizeof(WinDumpHeader64) : sizeof(WinDumpHeader32); + /* * Further access to kernel structures by virtual addresses * should be made from system context. */ - first_x86_cpu->env.cr[3] = h->DirectoryTableBase; + first_x86_cpu->env.cr[3] = WIN_DUMP_FIELD(DirectoryTableBase); - check_kdbg(h, &local_err); + check_kdbg(h, x64, &local_err); if (local_err) { error_propagate(errp, local_err); goto out_cr3; } - patch_header(h); + patch_header(h, x64); - saved_ctx = g_new(struct saved_context, h->NumberProcessors); + saved_ctx = g_new(struct saved_context, WIN_DUMP_FIELD(NumberProcessors)); /* * Always patch context because there is no way * to determine if the system-saved context is valid */ - patch_and_save_context(h, saved_ctx, &local_err); + patch_and_save_context(h, x64, saved_ctx, &local_err); if (local_err) { error_propagate(errp, local_err); goto out_free; } - s->total_size = h->RequiredDumpSpace; + s->total_size = WIN_DUMP_FIELD(RequiredDumpSpace); - s->written_size = qemu_write_full(s->fd, h, sizeof(*h)); - if (s->written_size != sizeof(*h)) { + s->written_size = qemu_write_full(s->fd, h, hdr_size); + if (s->written_size != hdr_size) { error_setg(errp, QERR_IO_ERROR); goto out_restore; } - write_runs(s, h, &local_err); + write_runs(s, h, x64, &local_err); if (local_err) { error_propagate(errp, local_err); goto out_restore; } out_restore: - restore_context(h, saved_ctx); + restore_context(h, x64, saved_ctx); out_free: g_free(saved_ctx); out_cr3: diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index db3e1f393d..bbeadaa189 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -1327,16 +1327,19 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b, float_status *s, bool is_quiet) { int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); - int cmp; if (likely(ab_mask == float_cmask_normal)) { + FloatRelation cmp; + if (a->sign != b->sign) { goto a_sign; } - if (a->exp != b->exp) { - cmp = a->exp < b->exp ? -1 : 1; - } else { + if (a->exp == b->exp) { cmp = frac_cmp(a, b); + } else if (a->exp < b->exp) { + cmp = float_relation_less; + } else { + cmp = float_relation_greater; } if (a->sign) { cmp = -cmp; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 7f524d4377..4a871ef2a1 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -874,10 +874,10 @@ static FloatParts128 *parts128_minmax(FloatParts128 *a, FloatParts128 *b, #define parts_minmax(A, B, S, F) \ PARTS_GENERIC_64_128(minmax, A)(A, B, S, F) -static int parts64_compare(FloatParts64 *a, FloatParts64 *b, - float_status *s, bool q); -static int parts128_compare(FloatParts128 *a, FloatParts128 *b, - float_status *s, bool q); +static FloatRelation parts64_compare(FloatParts64 *a, FloatParts64 *b, + float_status *s, bool q); +static FloatRelation parts128_compare(FloatParts128 *a, FloatParts128 *b, + float_status *s, bool q); #define parts_compare(A, B, S, Q) \ PARTS_GENERIC_64_128(compare, A)(A, B, S, Q) @@ -957,21 +957,23 @@ static void frac128_allones(FloatParts128 *a) #define frac_allones(A) FRAC_GENERIC_64_128(allones, A)(A) -static int frac64_cmp(FloatParts64 *a, FloatParts64 *b) +static FloatRelation frac64_cmp(FloatParts64 *a, FloatParts64 *b) { - return a->frac == b->frac ? 0 : a->frac < b->frac ? -1 : 1; + return (a->frac == b->frac ? float_relation_equal + : a->frac < b->frac ? float_relation_less + : float_relation_greater); } -static int frac128_cmp(FloatParts128 *a, FloatParts128 *b) +static FloatRelation frac128_cmp(FloatParts128 *a, FloatParts128 *b) { uint64_t ta = a->frac_hi, tb = b->frac_hi; if (ta == tb) { ta = a->frac_lo, tb = b->frac_lo; if (ta == tb) { - return 0; + return float_relation_equal; } } - return ta < tb ? -1 : 1; + return ta < tb ? float_relation_less : float_relation_greater; } #define frac_cmp(A, B) FRAC_GENERIC_64_128(cmp, A)(A, B) @@ -3154,6 +3156,60 @@ static int64_t float128_to_int64_scalbn(float128 a, FloatRoundMode rmode, return parts_float_to_sint(&p, rmode, scale, INT64_MIN, INT64_MAX, s); } +static Int128 float128_to_int128_scalbn(float128 a, FloatRoundMode rmode, + int scale, float_status *s) +{ + int flags = 0; + Int128 r; + FloatParts128 p; + + float128_unpack_canonical(&p, a, s); + + switch (p.cls) { + case float_class_snan: + flags |= float_flag_invalid_snan; + /* fall through */ + case float_class_qnan: + flags |= float_flag_invalid; + r = UINT128_MAX; + break; + + case float_class_inf: + flags = float_flag_invalid | float_flag_invalid_cvti; + r = p.sign ? INT128_MIN : INT128_MAX; + break; + + case float_class_zero: + return int128_zero(); + + case float_class_normal: + if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) { + flags = float_flag_inexact; + } + + if (p.exp < 127) { + int shift = 127 - p.exp; + r = int128_urshift(int128_make128(p.frac_lo, p.frac_hi), shift); + if (p.sign) { + r = int128_neg(r); + } + } else if (p.exp == 127 && p.sign && p.frac_lo == 0 && + p.frac_hi == DECOMPOSED_IMPLICIT_BIT) { + r = INT128_MIN; + } else { + flags = float_flag_invalid | float_flag_invalid_cvti; + r = p.sign ? INT128_MIN : INT128_MAX; + } + break; + + default: + g_assert_not_reached(); + } + + float_raise(flags, s); + return r; +} + static int32_t floatx80_to_int32_scalbn(floatx80 a, FloatRoundMode rmode, int scale, float_status *s) { @@ -3236,6 +3292,11 @@ int64_t float128_to_int64(float128 a, float_status *s) return float128_to_int64_scalbn(a, s->float_rounding_mode, 0, s); } +Int128 float128_to_int128(float128 a, float_status *s) +{ + return float128_to_int128_scalbn(a, s->float_rounding_mode, 0, s); +} + int32_t floatx80_to_int32(floatx80 a, float_status *s) { return floatx80_to_int32_scalbn(a, s->float_rounding_mode, 0, s); @@ -3301,6 +3362,11 @@ int64_t float128_to_int64_round_to_zero(float128 a, float_status *s) return float128_to_int64_scalbn(a, float_round_to_zero, 0, s); } +Int128 float128_to_int128_round_to_zero(float128 a, float_status *s) +{ + return float128_to_int128_scalbn(a, float_round_to_zero, 0, s); +} + int32_t floatx80_to_int32_round_to_zero(floatx80 a, float_status *s) { return floatx80_to_int32_scalbn(a, float_round_to_zero, 0, s); @@ -3480,6 +3546,61 @@ static uint64_t float128_to_uint64_scalbn(float128 a, FloatRoundMode rmode, return parts_float_to_uint(&p, rmode, scale, UINT64_MAX, s); } +static Int128 float128_to_uint128_scalbn(float128 a, FloatRoundMode rmode, + int scale, float_status *s) +{ + int flags = 0; + Int128 r; + FloatParts128 p; + + float128_unpack_canonical(&p, a, s); + + switch (p.cls) { + case float_class_snan: + flags |= float_flag_invalid_snan; + /* fall through */ + case float_class_qnan: + flags |= float_flag_invalid; + r = UINT128_MAX; + break; + + case float_class_inf: + flags = float_flag_invalid | float_flag_invalid_cvti; + r = p.sign ? int128_zero() : UINT128_MAX; + break; + + case float_class_zero: + return int128_zero(); + + case float_class_normal: + if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) { + flags = float_flag_inexact; + if (p.cls == float_class_zero) { + r = int128_zero(); + break; + } + } + + if (p.sign) { + flags = float_flag_invalid | float_flag_invalid_cvti; + r = int128_zero(); + } else if (p.exp <= 127) { + int shift = 127 - p.exp; + r = int128_urshift(int128_make128(p.frac_lo, p.frac_hi), shift); + } else { + flags = float_flag_invalid | float_flag_invalid_cvti; + r = UINT128_MAX; + } + break; + + default: + g_assert_not_reached(); + } + + float_raise(flags, s); + return r; +} + uint8_t float16_to_uint8(float16 a, float_status *s) { return float16_to_uint8_scalbn(a, s->float_rounding_mode, 0, s); @@ -3540,6 +3661,11 @@ uint64_t float128_to_uint64(float128 a, float_status *s) return float128_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); } +Int128 float128_to_uint128(float128 a, float_status *s) +{ + return float128_to_uint128_scalbn(a, s->float_rounding_mode, 0, s); +} + uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *s) { return float16_to_uint16_scalbn(a, float_round_to_zero, 0, s); @@ -3595,6 +3721,11 @@ uint64_t float128_to_uint64_round_to_zero(float128 a, float_status *s) return float128_to_uint64_scalbn(a, float_round_to_zero, 0, s); } +Int128 float128_to_uint128_round_to_zero(float128 a, float_status *s) +{ + return float128_to_uint128_scalbn(a, float_round_to_zero, 0, s); +} + uint16_t bfloat16_to_uint16(bfloat16 a, float_status *s) { return bfloat16_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); @@ -3780,6 +3911,35 @@ bfloat16 int16_to_bfloat16(int16_t a, float_status *status) return int64_to_bfloat16_scalbn(a, 0, status); } +float128 int128_to_float128(Int128 a, float_status *status) +{ + FloatParts128 p = { }; + int shift; + + if (int128_nz(a)) { + p.cls = float_class_normal; + if (!int128_nonneg(a)) { + p.sign = true; + a = int128_neg(a); + } + + shift = clz64(int128_gethi(a)); + if (shift == 64) { + shift += clz64(int128_getlo(a)); + } + + p.exp = 127 - shift; + a = int128_lshift(a, shift); + + p.frac_hi = int128_gethi(a); + p.frac_lo = int128_getlo(a); + } else { + p.cls = float_class_zero; + } + + return float128_round_pack_canonical(&p, status); +} + float128 int64_to_float128(int64_t a, float_status *status) { FloatParts128 p; @@ -3969,6 +4129,31 @@ float128 uint64_to_float128(uint64_t a, float_status *status) return float128_round_pack_canonical(&p, status); } +float128 uint128_to_float128(Int128 a, float_status *status) +{ + FloatParts128 p = { }; + int shift; + + if (int128_nz(a)) { + p.cls = float_class_normal; + + shift = clz64(int128_gethi(a)); + if (shift == 64) { + shift += clz64(int128_getlo(a)); + } + + p.exp = 127 - shift; + a = int128_lshift(a, shift); + + p.frac_hi = int128_gethi(a); + p.frac_lo = int128_getlo(a); + } else { + p.cls = float_class_zero; + } + + return float128_round_pack_canonical(&p, status); +} + /* * Minimum and maximum */ diff --git a/fsdev/9p-marshal.c b/fsdev/9p-marshal.c index 51881fe220..f9b0336cd5 100644 --- a/fsdev/9p-marshal.c +++ b/fsdev/9p-marshal.c @@ -27,7 +27,7 @@ void v9fs_string_free(V9fsString *str) str->size = 0; } -void GCC_FMT_ATTR(2, 3) +void G_GNUC_PRINTF(2, 3) v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) { va_list ap; diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index 8fd89f0447..4997677460 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -16,10 +16,17 @@ #include #include -#include #include "qemu-fsdev-throttle.h" #include "p9array.h" +#ifdef CONFIG_LINUX +# include +#endif +#ifdef CONFIG_DARWIN +# include +# include +#endif + #define SM_LOCAL_MODE_BITS 0600 #define SM_LOCAL_DIR_MODE_BITS 0700 diff --git a/fsdev/meson.build b/fsdev/meson.build index adf57cc43e..b632b66348 100644 --- a/fsdev/meson.build +++ b/fsdev/meson.build @@ -7,6 +7,7 @@ fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files( 'qemu-fsdev.c', ), if_false: files('qemu-fsdev-dummy.c')) softmmu_ss.add_all(when: 'CONFIG_LINUX', if_true: fsdev_ss) +softmmu_ss.add_all(when: 'CONFIG_DARWIN', if_true: fsdev_ss) if have_virtfs_proxy_helper executable('virtfs-proxy-helper', diff --git a/fsdev/p9array.h b/fsdev/p9array.h index 6aa25327ca..90e83a7c7b 100644 --- a/fsdev/p9array.h +++ b/fsdev/p9array.h @@ -81,11 +81,11 @@ */ /** - * Declares an array type for the passed @a scalar_type. + * P9ARRAY_DECLARE_TYPE() - Declares an array type for the passed @scalar_type. + * + * @scalar_type: type of the individual array elements * * This is typically used from a shared header file. - * - * @param scalar_type - type of the individual array elements */ #define P9ARRAY_DECLARE_TYPE(scalar_type) \ typedef struct P9Array##scalar_type { \ @@ -97,14 +97,14 @@ void p9array_auto_free_##scalar_type(scalar_type **auto_var); \ /** - * Defines an array type for the passed @a scalar_type and appropriate - * @a scalar_cleanup_func. + * P9ARRAY_DEFINE_TYPE() - Defines an array type for the passed @scalar_type + * and appropriate @scalar_cleanup_func. + * + * @scalar_type: type of the individual array elements + * @scalar_cleanup_func: appropriate function to free memory dynamically + * allocated by individual array elements before * * This is typically used from a C unit file. - * - * @param scalar_type - type of the individual array elements - * @param scalar_cleanup_func - appropriate function to free memory dynamically - * allocated by individual array elements before */ #define P9ARRAY_DEFINE_TYPE(scalar_type, scalar_cleanup_func) \ void p9array_new_##scalar_type(scalar_type **auto_var, size_t len) \ @@ -132,23 +132,27 @@ } \ /** + * P9ARRAY_REF() - Declare a reference variable for an array. + * + * @scalar_type: type of the individual array elements + * * Used to declare a reference variable (unique pointer) for an array. After * leaving the scope of the reference variable, the associated array is * automatically freed. - * - * @param scalar_type - type of the individual array elements */ #define P9ARRAY_REF(scalar_type) \ __attribute((__cleanup__(p9array_auto_free_##scalar_type))) scalar_type* /** - * Allocates a new array of passed @a scalar_type with @a len number of array - * elements and assigns the created array to the reference variable - * @a auto_var. + * P9ARRAY_NEW() - Allocate a new array. * - * @param scalar_type - type of the individual array elements - * @param auto_var - destination reference variable - * @param len - amount of array elements to be allocated immediately + * @scalar_type: type of the individual array elements + * @auto_var: destination reference variable + * @len: amount of array elements to be allocated immediately + * + * Allocates a new array of passed @scalar_type with @len number of array + * elements and assigns the created array to the reference variable + * @auto_var. */ #define P9ARRAY_NEW(scalar_type, auto_var, len) \ QEMU_BUILD_BUG_MSG( \ diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index 15c0e79b06..2dde27922f 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -21,7 +21,6 @@ #include #endif #include -#include "qemu-common.h" #include "qemu/sockets.h" #include "qemu/xattr.h" #include "9p-iov-marshal.h" @@ -57,7 +56,7 @@ static bool is_daemon; static bool get_version; /* IOC getversion IOCTL supported */ static char *prog_name; -static void GCC_FMT_ATTR(2, 3) do_log(int loglevel, const char *format, ...) +static void G_GNUC_PRINTF(2, 3) do_log(int loglevel, const char *format, ...) { va_list ap; diff --git a/gdbstub.c b/gdbstub.c index 3c14c6a038..a3ff8702ce 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/ctype.h" @@ -426,7 +425,7 @@ static int get_char(void) int ret; for(;;) { - ret = qemu_recv(gdbserver_state.fd, &ch, 1, 0); + ret = recv(gdbserver_state.fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) gdbserver_state.fd = -1; @@ -519,7 +518,15 @@ static int gdb_continue_partial(char *newstates) int flag = 0; if (!runstate_needs_reset()) { - if (vm_prepare_start()) { + bool step_requested = false; + CPU_FOREACH(cpu) { + if (newstates[cpu->cpu_index] == 's') { + step_requested = true; + break; + } + } + + if (vm_prepare_start(step_requested)) { return 0; } diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index e90f20a107..adfa085a9b 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -879,3 +879,18 @@ SRST ``info sgx`` Show intel SGX information. ERST + +#if defined(TARGET_M68K) || defined(TARGET_PPC) + { + .name = "via", + .args_type = "", + .params = "", + .help = "show guest mos6522 VIA devices", + .cmd = hmp_info_via, + }, +#endif + +SRST + ``info via`` + Show guest mos6522 VIA devices. +ERST diff --git a/hmp-commands.hx b/hmp-commands.hx index 70a9136ac2..03e6a73d1f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -202,9 +202,9 @@ ERST { .name = "change", - .args_type = "device:B,target:F,arg:s?,read-only-mode:s?", - .params = "device filename [format [read-only-mode]]", - .help = "change a removable medium, optional format", + .args_type = "device:B,force:-f,target:F,arg:s?,read-only-mode:s?", + .params = "device [-f] filename [format [read-only-mode]]", + .help = "change a removable medium, optional format, use -f to force the operation", .cmd = hmp_change, }, @@ -212,11 +212,14 @@ SRST ``change`` *device* *setting* Change the configuration of a device. - ``change`` *diskdevice* *filename* [*format* [*read-only-mode*]] + ``change`` *diskdevice* [-f] *filename* [*format* [*read-only-mode*]] Change the medium for a removable disk device to point to *filename*. eg:: (qemu) change ide1-cd0 /path/to/some.iso + ``-f`` + forces the operation even if the guest has locked the tray. + *format* is optional. *read-only-mode* may be used to change the read-only status of the device. @@ -244,11 +247,12 @@ ERST { .name = "screendump", - .args_type = "filename:F,device:s?,head:i?", - .params = "filename [device [head]]", - .help = "save screen from head 'head' of display device 'device' " - "into PPM image 'filename'", - .cmd = hmp_screendump, + .args_type = "filename:F,format:-fs,device:s?,head:i?", + .params = "filename [-f format] [device [head]]", + .help = "save screen from head 'head' of display device 'device'" + "in specified format 'format' as image 'filename'." + "Currently only 'png' and 'ppm' formats are supported.", + .cmd = hmp_screendump, .coroutine = true, }, @@ -1064,7 +1068,7 @@ ERST "-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t" "-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t" "-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\n\t\t\t" - " for Windows x64 guests with vmcoreinfo driver only.\n\t\t\t" + " for Windows x86 and x64 guests with vmcoreinfo driver only.\n\t\t\t" "begin: the starting physical address.\n\t\t\t" "length: the memory size, in bytes.", .cmd = hmp_dump_guest_memory, @@ -1514,33 +1518,35 @@ ERST { .name = "set_password", - .args_type = "protocol:s,password:s,connected:s?", - .params = "protocol password action-if-connected", + .args_type = "protocol:s,password:s,display:-ds,connected:s?", + .params = "protocol password [-d display] [action-if-connected]", .help = "set spice/vnc password", .cmd = hmp_set_password, }, SRST -``set_password [ vnc | spice ] password [ action-if-connected ]`` - Change spice/vnc password. *action-if-connected* specifies what - should happen in case a connection is established: *fail* makes the - password change fail. *disconnect* changes the password and +``set_password [ vnc | spice ] password [ -d display ] [ action-if-connected ]`` + Change spice/vnc password. *display* can be used with 'vnc' to specify + which display to set the password on. *action-if-connected* specifies + what should happen in case a connection is established: *fail* makes + the password change fail. *disconnect* changes the password and disconnects the client. *keep* changes the password and keeps the connection up. *keep* is the default. ERST { .name = "expire_password", - .args_type = "protocol:s,time:s", - .params = "protocol time", + .args_type = "protocol:s,time:s,display:-ds", + .params = "protocol time [-d display]", .help = "set spice/vnc password expire-time", .cmd = hmp_expire_password, }, SRST -``expire_password [ vnc | spice ]`` *expire-time* - Specify when a password for spice/vnc becomes - invalid. *expire-time* accepts: +``expire_password [ vnc | spice ] expire-time [ -d display ]`` + Specify when a password for spice/vnc becomes invalid. + *display* behaves the same as in ``set_password``. + *expire-time* accepts: ``now`` Invalidate password instantly. diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 210d9e7705..d42ce6d8b8 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -32,10 +32,12 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include +#ifdef CONFIG_LINUX #include #ifdef CONFIG_LINUX_MAGIC_H #include #endif +#endif #include #ifndef XFS_SUPER_MAGIC @@ -560,6 +562,15 @@ again: if (!entry) { return NULL; } +#ifdef CONFIG_DARWIN + int off; + off = telldir(fs->dir.stream); + /* If telldir fails, fail the entire readdir call */ + if (off < 0) { + return NULL; + } + entry->d_seekoff = off; +#endif if (ctx->export_flags & V9FS_SM_MAPPED) { entry->d_type = DT_UNKNOWN; @@ -671,7 +682,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, if (fs_ctx->export_flags & V9FS_SM_MAPPED || fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - err = mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0); + err = qemu_mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0); if (err == -1) { goto out; } @@ -686,7 +697,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, } } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || fs_ctx->export_flags & V9FS_SM_NONE) { - err = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); + err = qemu_mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); if (err == -1) { goto out; } @@ -779,16 +790,20 @@ static int local_fstat(FsContext *fs_ctx, int fid_type, mode_t tmp_mode; dev_t tmp_dev; - if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { + if (qemu_fgetxattr(fd, "user.virtfs.uid", + &tmp_uid, sizeof(uid_t)) > 0) { stbuf->st_uid = le32_to_cpu(tmp_uid); } - if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { + if (qemu_fgetxattr(fd, "user.virtfs.gid", + &tmp_gid, sizeof(gid_t)) > 0) { stbuf->st_gid = le32_to_cpu(tmp_gid); } - if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { + if (qemu_fgetxattr(fd, "user.virtfs.mode", + &tmp_mode, sizeof(mode_t)) > 0) { stbuf->st_mode = le32_to_cpu(tmp_mode); } - if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { + if (qemu_fgetxattr(fd, "user.virtfs.rdev", + &tmp_dev, sizeof(dev_t)) > 0) { stbuf->st_rdev = le64_to_cpu(tmp_dev); } } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { diff --git a/hw/9pfs/9p-posix-acl.c b/hw/9pfs/9p-posix-acl.c index eadae270dd..4b2cb3c66c 100644 --- a/hw/9pfs/9p-posix-acl.c +++ b/hw/9pfs/9p-posix-acl.c @@ -65,7 +65,11 @@ static int mp_pacl_removexattr(FsContext *ctx, int ret; ret = local_removexattr_nofollow(ctx, path, MAP_ACL_ACCESS); - if (ret == -1 && errno == ENODATA) { + /* + * macOS returns ENOATTR (!=ENODATA on macOS), whereas Linux returns + * ENODATA (==ENOATTR on Linux), so checking for ENOATTR is fine + */ + if (ret == -1 && errno == ENOATTR) { /* * We don't get ENODATA error when trying to remove a * posix acl that is not present. So don't throw the error @@ -115,7 +119,11 @@ static int mp_dacl_removexattr(FsContext *ctx, int ret; ret = local_removexattr_nofollow(ctx, path, MAP_ACL_DEFAULT); - if (ret == -1 && errno == ENODATA) { + /* + * macOS returns ENOATTR (!=ENODATA on macOS), whereas Linux returns + * ENODATA (==ENOATTR on Linux), so checking for ENOATTR is fine + */ + if (ret == -1 && errno == ENOATTR) { /* * We don't get ENODATA error when trying to remove a * posix acl that is not present. So don't throw the error diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index 09bd9f1464..99d115ff0d 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include #include -#include "qemu-common.h" #include "9p.h" #include "qapi/error.h" #include "qemu/cutils.h" @@ -123,10 +122,16 @@ static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs) stfs->f_bavail = prstfs->f_bavail; stfs->f_files = prstfs->f_files; stfs->f_ffree = prstfs->f_ffree; +#ifdef CONFIG_DARWIN + /* f_namelen and f_frsize do not exist on Darwin */ + stfs->f_fsid.val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; + stfs->f_fsid.val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; +#else stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; stfs->f_namelen = prstfs->f_namelen; stfs->f_frsize = prstfs->f_frsize; +#endif } /* Converts proxy_stat structure to VFS stat structure */ @@ -143,12 +148,24 @@ static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat) stbuf->st_size = prstat->st_size; stbuf->st_blksize = prstat->st_blksize; stbuf->st_blocks = prstat->st_blocks; - stbuf->st_atim.tv_sec = prstat->st_atim_sec; - stbuf->st_atim.tv_nsec = prstat->st_atim_nsec; + stbuf->st_atime = prstat->st_atim_sec; stbuf->st_mtime = prstat->st_mtim_sec; - stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec; stbuf->st_ctime = prstat->st_ctim_sec; +#ifdef CONFIG_DARWIN + stbuf->st_atimespec.tv_sec = prstat->st_atim_sec; + stbuf->st_mtimespec.tv_sec = prstat->st_mtim_sec; + stbuf->st_ctimespec.tv_sec = prstat->st_ctim_sec; + stbuf->st_atimespec.tv_nsec = prstat->st_atim_nsec; + stbuf->st_mtimespec.tv_nsec = prstat->st_mtim_nsec; + stbuf->st_ctimespec.tv_nsec = prstat->st_ctim_nsec; +#else + stbuf->st_atim.tv_sec = prstat->st_atim_sec; + stbuf->st_mtim.tv_sec = prstat->st_mtim_sec; + stbuf->st_ctim.tv_sec = prstat->st_ctim_sec; + stbuf->st_atim.tv_nsec = prstat->st_atim_nsec; + stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec; stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec; +#endif } /* @@ -688,7 +705,21 @@ static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) static struct dirent *proxy_readdir(FsContext *ctx, V9fsFidOpenState *fs) { - return readdir(fs->dir.stream); + struct dirent *entry; + entry = readdir(fs->dir.stream); +#ifdef CONFIG_DARWIN + if (!entry) { + return NULL; + } + int td; + td = telldir(fs->dir.stream); + /* If telldir fails, fail the entire readdir call */ + if (td < 0) { + return NULL; + } + entry->d_seekoff = td; +#endif + return entry; } static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) @@ -1155,7 +1186,7 @@ static int proxy_parse_opts(QemuOpts *opts, FsDriverEntry *fs, Error **errp) static int proxy_init(FsContext *ctx, Error **errp) { - V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy)); + V9fsProxy *proxy = g_new(V9fsProxy, 1); int sock_id; if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 7a7cd5c5ba..1c5813e4dd 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -49,7 +49,7 @@ static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, /* Add directory type and remove write bits */ mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH); - node = g_malloc0(sizeof(V9fsSynthNode)); + node = g_new0(V9fsSynthNode, 1); if (attr) { /* We are adding .. or . entries */ node->attr = attr; @@ -92,7 +92,7 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, } } /* Add the name */ - node = v9fs_add_dir_node(parent, mode, name, NULL, synth_node_count++); + node = v9fs_add_dir_node(parent, mode, name, NULL, ++synth_node_count); v9fs_add_dir_node(node, parent->attr->mode, "..", parent->attr, parent->attr->inode); v9fs_add_dir_node(node, node->attr->mode, ".", @@ -128,9 +128,9 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, } /* Add file type and remove write bits */ mode = ((mode & 0777) | S_IFREG); - node = g_malloc0(sizeof(V9fsSynthNode)); + node = g_new0(V9fsSynthNode, 1); node->attr = &node->actual_attr; - node->attr->inode = synth_node_count++; + node->attr->inode = ++synth_node_count; node->attr->nlink = 1; node->attr->read = read; node->attr->write = write; @@ -234,7 +234,11 @@ static void synth_direntry(V9fsSynthNode *node, offsetof(struct dirent, d_name) + sz); memcpy(entry->d_name, node->name, sz); entry->d_ino = node->attr->inode; +#ifdef CONFIG_DARWIN + entry->d_seekoff = off + 1; +#else entry->d_off = off + 1; +#endif } static struct dirent *synth_get_dentry(V9fsSynthNode *dir, @@ -439,7 +443,9 @@ static int synth_statfs(FsContext *s, V9fsPath *fs_path, stbuf->f_bsize = 512; stbuf->f_blocks = 0; stbuf->f_files = synth_node_count; +#ifndef CONFIG_DARWIN stbuf->f_namelen = NAME_MAX; +#endif return 0; } diff --git a/hw/9pfs/9p-util-darwin.c b/hw/9pfs/9p-util-darwin.c new file mode 100644 index 0000000000..95146e7354 --- /dev/null +++ b/hw/9pfs/9p-util-darwin.c @@ -0,0 +1,147 @@ +/* + * 9p utilities (Darwin Implementation) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/xattr.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "9p-util.h" + +ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name, + void *value, size_t size) +{ + int ret; + int fd = openat_file(dirfd, filename, + O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + ret = fgetxattr(fd, name, value, size, 0, 0); + close_preserve_errno(fd); + return ret; +} + +ssize_t flistxattrat_nofollow(int dirfd, const char *filename, + char *list, size_t size) +{ + int ret; + int fd = openat_file(dirfd, filename, + O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + ret = flistxattr(fd, list, size, 0); + close_preserve_errno(fd); + return ret; +} + +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, + const char *name) +{ + int ret; + int fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + ret = fremovexattr(fd, name, 0); + close_preserve_errno(fd); + return ret; +} + +int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, + void *value, size_t size, int flags) +{ + int ret; + int fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0); + if (fd == -1) { + return -1; + } + ret = fsetxattr(fd, name, value, size, 0, flags); + close_preserve_errno(fd); + return ret; +} + +/* + * As long as mknodat is not available on macOS, this workaround + * using pthread_fchdir_np is needed. + * + * Radar filed with Apple for implementing mknodat: + * rdar://FB9862426 (https://openradar.appspot.com/FB9862426) + */ +#if defined CONFIG_PTHREAD_FCHDIR_NP + +static int create_socket_file_at_cwd(const char *filename, mode_t mode) { + int fd, err; + struct sockaddr_un addr = { + .sun_family = AF_UNIX + }; + + err = snprintf(addr.sun_path, sizeof(addr.sun_path), "./%s", filename); + if (err < 0 || err >= sizeof(addr.sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (fd == -1) { + return fd; + } + err = bind(fd, (struct sockaddr *) &addr, sizeof(addr)); + if (err == -1) { + goto out; + } + /* + * FIXME: Should rather be using descriptor-based fchmod() on the + * socket file descriptor above (preferably before bind() call), + * instead of path-based fchmodat(), to prevent concurrent transient + * state issues between creating the named FIFO file at bind() and + * delayed adjustment of permissions at fchmodat(). However currently + * macOS (12.x) does not support such operations on socket file + * descriptors yet. + * + * Filed report with Apple: FB9997731 + */ + err = fchmodat(AT_FDCWD, filename, mode, AT_SYMLINK_NOFOLLOW); +out: + close_preserve_errno(fd); + return err; +} + +int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev) +{ + int preserved_errno, err; + + if (S_ISREG(mode) || !(mode & S_IFMT)) { + int fd = openat_file(dirfd, filename, O_CREAT, mode); + if (fd == -1) { + return fd; + } + close(fd); + return 0; + } + if (!pthread_fchdir_np) { + error_report_once("pthread_fchdir_np() not available on this version of macOS"); + errno = ENOTSUP; + return -1; + } + if (pthread_fchdir_np(dirfd) < 0) { + return -1; + } + if (S_ISSOCK(mode)) { + err = create_socket_file_at_cwd(filename, mode); + } else { + err = mknod(filename, mode, dev); + } + preserved_errno = errno; + /* Stop using the thread-local cwd */ + pthread_fchdir_np(-1); + if (err < 0) { + errno = preserved_errno; + } + return err; +} + +#endif diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util-linux.c similarity index 90% rename from hw/9pfs/9p-util.c rename to hw/9pfs/9p-util-linux.c index 3221d9b498..db451b0784 100644 --- a/hw/9pfs/9p-util.c +++ b/hw/9pfs/9p-util-linux.c @@ -1,5 +1,5 @@ /* - * 9p utilities + * 9p utilities (Linux Implementation) * * Copyright IBM, Corp. 2017 * @@ -61,4 +61,10 @@ int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, ret = lsetxattr(proc_path, name, value, size, flags); g_free(proc_path); return ret; + +} + +int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev) +{ + return mknodat(dirfd, filename, mode, dev); } diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index 546f46dc7d..c3526144c9 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -19,6 +19,92 @@ #define O_PATH_9P_UTIL 0 #endif +#if !defined(CONFIG_LINUX) + +/* + * Generates a Linux device number (a.k.a. dev_t) for given device major + * and minor numbers. + * + * To be more precise: it generates a device number in glibc's format + * (MMMM_Mmmm_mmmM_MMmm, 64 bits) actually, which is compatible with + * Linux's format (mmmM_MMmm, 32 bits), as described in . + */ +static inline uint64_t makedev_dotl(uint32_t dev_major, uint32_t dev_minor) +{ + uint64_t dev; + + // from glibc sysmacros.h: + dev = (((uint64_t) (dev_major & 0x00000fffu)) << 8); + dev |= (((uint64_t) (dev_major & 0xfffff000u)) << 32); + dev |= (((uint64_t) (dev_minor & 0x000000ffu)) << 0); + dev |= (((uint64_t) (dev_minor & 0xffffff00u)) << 12); + return dev; +} + +#endif + +/* + * Converts given device number from host's device number format to Linux + * device number format. As both the size of type dev_t and encoding of + * dev_t is system dependant, we have to convert them for Linux guests if + * host is not running Linux. + */ +static inline uint64_t host_dev_to_dotl_dev(dev_t dev) +{ +#ifdef CONFIG_LINUX + return dev; +#else + return makedev_dotl(major(dev), minor(dev)); +#endif +} + +/* Translates errno from host -> Linux if needed */ +static inline int errno_to_dotl(int err) { +#if defined(CONFIG_LINUX) + /* nothing to translate (Linux -> Linux) */ +#elif defined(CONFIG_DARWIN) + /* + * translation mandatory for macOS hosts + * + * FIXME: Only most important errnos translated here yet, this should be + * extended to as many errnos being translated as possible in future. + */ + if (err == ENAMETOOLONG) { + err = 36; /* ==ENAMETOOLONG on Linux */ + } else if (err == ENOTEMPTY) { + err = 39; /* ==ENOTEMPTY on Linux */ + } else if (err == ELOOP) { + err = 40; /* ==ELOOP on Linux */ + } else if (err == ENOATTR) { + err = 61; /* ==ENODATA on Linux */ + } else if (err == ENOTSUP) { + err = 95; /* ==EOPNOTSUPP on Linux */ + } else if (err == EOPNOTSUPP) { + err = 95; /* ==EOPNOTSUPP on Linux */ + } +#else +#error Missing errno translation to Linux for this host system +#endif + return err; +} + +#ifdef CONFIG_DARWIN +#define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0) +#define qemu_lgetxattr(...) getxattr(__VA_ARGS__, 0, XATTR_NOFOLLOW) +#define qemu_llistxattr(...) listxattr(__VA_ARGS__, XATTR_NOFOLLOW) +#define qemu_lremovexattr(...) removexattr(__VA_ARGS__, XATTR_NOFOLLOW) +static inline int qemu_lsetxattr(const char *path, const char *name, + const void *value, size_t size, int flags) { + return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW); +} +#else +#define qemu_fgetxattr fgetxattr +#define qemu_lgetxattr lgetxattr +#define qemu_llistxattr llistxattr +#define qemu_lremovexattr lremovexattr +#define qemu_lsetxattr lsetxattr +#endif + static inline void close_preserve_errno(int fd) { int serrno = errno; @@ -37,10 +123,13 @@ static inline int openat_file(int dirfd, const char *name, int flags, { int fd, serrno, ret; +#ifndef CONFIG_DARWIN again: +#endif fd = openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK, mode); if (fd == -1) { +#ifndef CONFIG_DARWIN if (errno == EPERM && (flags & O_NOATIME)) { /* * The client passed O_NOATIME but we lack permissions to honor it. @@ -53,6 +142,7 @@ again: flags &= ~O_NOATIME; goto again; } +#endif return -1; } @@ -78,4 +168,61 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename, ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, const char *name); +/* + * Darwin has d_seekoff, which appears to function similarly to d_off. + * However, it does not appear to be supported on all file systems, + * so ensure it is manually injected earlier and call here when + * needed. + */ +static inline off_t qemu_dirent_off(struct dirent *dent) +{ +#ifdef CONFIG_DARWIN + return dent->d_seekoff; +#else + return dent->d_off; +#endif +} + +/** + * qemu_dirent_dup() - Duplicate directory entry @dent. + * + * @dent: original directory entry to be duplicated + * Return: duplicated directory entry which should be freed with g_free() + * + * It is highly recommended to use this function instead of open coding + * duplication of dirent objects, because the actual struct dirent + * size may be bigger or shorter than sizeof(struct dirent) and correct + * handling is platform specific (see gitlab issue #841). + */ +static inline struct dirent *qemu_dirent_dup(struct dirent *dent) +{ + size_t sz = 0; +#if defined _DIRENT_HAVE_D_RECLEN + /* Avoid use of strlen() if platform supports d_reclen. */ + sz = dent->d_reclen; +#endif + /* + * Test sz for zero even if d_reclen is available + * because some drivers may set d_reclen to zero. + */ + if (sz == 0) { + /* Fallback to the most portable way. */ + sz = offsetof(struct dirent, d_name) + + strlen(dent->d_name) + 1; + } + return g_memdup(dent, sz); +} + +/* + * As long as mknodat is not available on macOS, this workaround + * using pthread_fchdir_np is needed. qemu_mknodat is defined in + * os-posix.c. pthread_fchdir_np is weakly linked here as a guard + * in case it disappears in future macOS versions, because it is + * is a private API. + */ +#if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP +int pthread_fchdir_np(int fd) __attribute__((weak_import)); +#endif +int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); + #endif diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 15b3f4d385..0cd0c14c2a 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -17,6 +17,11 @@ */ #include "qemu/osdep.h" +#ifdef CONFIG_LINUX +#include +#else +#include +#endif #include #include "hw/virtio/virtio.h" #include "qapi/error.h" @@ -27,12 +32,12 @@ #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" #include "9p-xattr.h" +#include "9p-util.h" #include "coth.h" #include "trace.h" #include "migration/blocker.h" #include "qemu/xxhash.h" #include -#include int open_fd_hw; int total_open_fd; @@ -133,11 +138,20 @@ static int dotl_to_open_flags(int flags) { P9_DOTL_NONBLOCK, O_NONBLOCK } , { P9_DOTL_DSYNC, O_DSYNC }, { P9_DOTL_FASYNC, FASYNC }, +#ifndef CONFIG_DARWIN + { P9_DOTL_NOATIME, O_NOATIME }, + /* + * On Darwin, we could map to F_NOCACHE, which is + * similar, but doesn't quite have the same + * semantics. However, we don't support O_DIRECT + * even on linux at the moment, so we just ignore + * it here. + */ { P9_DOTL_DIRECT, O_DIRECT }, +#endif { P9_DOTL_LARGEFILE, O_LARGEFILE }, { P9_DOTL_DIRECTORY, O_DIRECTORY }, { P9_DOTL_NOFOLLOW, O_NOFOLLOW }, - { P9_DOTL_NOATIME, O_NOATIME }, { P9_DOTL_SYNC, O_SYNC }, }; @@ -166,10 +180,12 @@ static int get_dotl_openflags(V9fsState *s, int oflags) */ flags = dotl_to_open_flags(oflags); flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); +#ifndef CONFIG_DARWIN /* * Ignore direct disk access hint until the server supports it. */ flags &= ~O_DIRECT; +#endif return flags; } @@ -187,7 +203,7 @@ void v9fs_path_free(V9fsPath *path) } -void GCC_FMT_ATTR(2, 3) +void G_GNUC_PRINTF(2, 3) v9fs_path_sprintf(V9fsPath *path, const char *fmt, ...) { va_list ap; @@ -308,7 +324,7 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) return NULL; } } - f = g_malloc0(sizeof(V9fsFidState)); + f = g_new0(V9fsFidState, 1); f->fid = fid; f->fid_type = P9_FID_NONE; f->ref = 1; @@ -612,8 +628,8 @@ static inline uint64_t mirror64bit(uint64_t value) ((uint64_t)mirror8bit((value >> 56) & 0xff)); } -/** - * @brief Parameter k for the Exponential Golomb algorihm to be used. +/* + * Parameter k for the Exponential Golomb algorihm to be used. * * The smaller this value, the smaller the minimum bit count for the Exp. * Golomb generated affixes will be (at lowest index) however for the @@ -626,28 +642,30 @@ static inline uint64_t mirror64bit(uint64_t value) * should be small, for a large amount of devices k might be increased * instead. The default of k=0 should be fine for most users though. * - * @b IMPORTANT: In case this ever becomes a runtime parameter; the value of + * IMPORTANT: In case this ever becomes a runtime parameter; the value of * k should not change as long as guest is still running! Because that would * cause completely different inode numbers to be generated on guest. */ #define EXP_GOLOMB_K 0 /** - * @brief Exponential Golomb algorithm for arbitrary k (including k=0). + * expGolombEncode() - Exponential Golomb algorithm for arbitrary k + * (including k=0). * - * The Exponential Golomb algorithm generates @b prefixes (@b not suffixes!) + * @n: natural number (or index) of the prefix to be generated + * (1, 2, 3, ...) + * @k: parameter k of Exp. Golomb algorithm to be used + * (see comment on EXP_GOLOMB_K macro for details about k) + * Return: prefix for given @n and @k + * + * The Exponential Golomb algorithm generates prefixes (NOT suffixes!) * with growing length and with the mathematical property of being * "prefix-free". The latter means the generated prefixes can be prepended * in front of arbitrary numbers and the resulting concatenated numbers are * guaranteed to be always unique. * * This is a minor adjustment to the original Exp. Golomb algorithm in the - * sense that lowest allowed index (@param n) starts with 1, not with zero. - * - * @param n - natural number (or index) of the prefix to be generated - * (1, 2, 3, ...) - * @param k - parameter k of Exp. Golomb algorithm to be used - * (see comment on EXP_GOLOMB_K macro for details about k) + * sense that lowest allowed index (@n) starts with 1, not with zero. */ static VariLenAffix expGolombEncode(uint64_t n, int k) { @@ -661,7 +679,9 @@ static VariLenAffix expGolombEncode(uint64_t n, int k) } /** - * @brief Converts a suffix into a prefix, or a prefix into a suffix. + * invertAffix() - Converts a suffix into a prefix, or a prefix into a suffix. + * @affix: either suffix or prefix to be inverted + * Return: inversion of passed @affix * * Simply mirror all bits of the affix value, for the purpose to preserve * respectively the mathematical "prefix-free" or "suffix-free" property @@ -685,16 +705,16 @@ static VariLenAffix invertAffix(const VariLenAffix *affix) } /** - * @brief Generates suffix numbers with "suffix-free" property. + * affixForIndex() - Generates suffix numbers with "suffix-free" property. + * @index: natural number (or index) of the suffix to be generated + * (1, 2, 3, ...) + * Return: Suffix suitable to assemble unique number. * * This is just a wrapper function on top of the Exp. Golomb algorithm. * * Since the Exp. Golomb algorithm generates prefixes, but we need suffixes, * this function converts the Exp. Golomb prefixes into appropriate suffixes * which are still suitable for generating unique numbers. - * - * @param n - natural number (or index) of the suffix to be generated - * (1, 2, 3, ...) */ static VariLenAffix affixForIndex(uint64_t index) { @@ -784,7 +804,7 @@ static int qid_inode_prefix_hash_bits(V9fsPDU *pdu, dev_t dev) val = qht_lookup(&pdu->s->qpd_table, &lookup, hash); if (!val) { - val = g_malloc0(sizeof(QpdEntry)); + val = g_new0(QpdEntry, 1); *val = lookup; affix = affixForIndex(pdu->s->qp_affix_next); val->prefix_bits = affix.bits; @@ -794,8 +814,8 @@ static int qid_inode_prefix_hash_bits(V9fsPDU *pdu, dev_t dev) return val->prefix_bits; } -/** - * @brief Slow / full mapping host inode nr -> guest inode nr. +/* + * Slow / full mapping host inode nr -> guest inode nr. * * This function performs a slower and much more costly remapping of an * original file inode number on host to an appropriate different inode @@ -807,7 +827,7 @@ static int qid_inode_prefix_hash_bits(V9fsPDU *pdu, dev_t dev) * qid_path_suffixmap() failed. In practice this slow / full mapping is not * expected ever to be used at all though. * - * @see qid_path_suffixmap() for details + * See qid_path_suffixmap() for details * */ static int qid_path_fullmap(V9fsPDU *pdu, const struct stat *stbuf, @@ -832,7 +852,7 @@ static int qid_path_fullmap(V9fsPDU *pdu, const struct stat *stbuf, return -ENFILE; } - val = g_malloc0(sizeof(QppEntry)); + val = g_new0(QpfEntry, 1); *val = lookup; /* new unique inode and device combo */ @@ -848,8 +868,8 @@ static int qid_path_fullmap(V9fsPDU *pdu, const struct stat *stbuf, return 0; } -/** - * @brief Quick mapping host inode nr -> guest inode nr. +/* + * Quick mapping host inode nr -> guest inode nr. * * This function performs quick remapping of an original file inode number * on host to an appropriate different inode number on guest. This remapping @@ -908,7 +928,7 @@ static int qid_path_suffixmap(V9fsPDU *pdu, const struct stat *stbuf, return -ENFILE; } - val = g_malloc0(sizeof(QppEntry)); + val = g_new0(QppEntry, 1); *val = lookup; /* new unique inode affix and device combo */ @@ -1034,6 +1054,8 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) } len += ret; id = P9_RERROR; + } else { + err = errno_to_dotl(err); } ret = pdu_marshal(pdu, len, "d", err); @@ -1265,12 +1287,15 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, /** - * Convert host filesystem's block size into an appropriate block size for - * 9p client (guest OS side). The value returned suggests an "optimum" block - * size for 9p I/O, i.e. to maximize performance. + * blksize_to_iounit() - Block size exposed to 9p client. + * Return: block size * * @pdu: 9p client request * @blksize: host filesystem's block size + * + * Convert host filesystem's block size into an appropriate block size for + * 9p client (guest OS side). The value returned suggests an "optimum" block + * size for 9p I/O, i.e. to maximize performance. */ static int32_t blksize_to_iounit(const V9fsPDU *pdu, int32_t blksize) { @@ -1304,16 +1329,22 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, v9lstat->st_nlink = stbuf->st_nlink; v9lstat->st_uid = stbuf->st_uid; v9lstat->st_gid = stbuf->st_gid; - v9lstat->st_rdev = stbuf->st_rdev; + v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev); v9lstat->st_size = stbuf->st_size; v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); v9lstat->st_blocks = stbuf->st_blocks; v9lstat->st_atime_sec = stbuf->st_atime; - v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; v9lstat->st_mtime_sec = stbuf->st_mtime; - v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; v9lstat->st_ctime_sec = stbuf->st_ctime; +#ifdef CONFIG_DARWIN + v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec; + v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec; + v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec; +#else + v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; + v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; +#endif /* Currently we only support BASIC fields in stat */ v9lstat->st_result_mask = P9_STATS_BASIC; @@ -2271,7 +2302,7 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU *pdu, count += len; v9fs_stat_free(&v9stat); v9fs_path_free(&path); - saved_dir_pos = dent->d_off; + saved_dir_pos = qemu_dirent_off(dent); } v9fs_readdir_unlock(&fidp->fs.dir); @@ -2376,10 +2407,11 @@ out_nofid: } /** - * Returns size required in Rreaddir response for the passed dirent @p name. + * v9fs_readdir_response_size() - Returns size required in Rreaddir response + * for the passed dirent @name. * - * @param name - directory entry's name (i.e. file name, directory name) - * @returns required size in bytes + * @name: directory entry's name (i.e. file name, directory name) + * Return: required size in bytes */ size_t v9fs_readdir_response_size(V9fsString *name) { @@ -2410,6 +2442,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString name; int len, err = 0; int32_t count = 0; + off_t off; struct dirent *dent; struct stat *st; struct V9fsDirEnt *entries = NULL; @@ -2470,12 +2503,13 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp, qid.version = 0; } + off = qemu_dirent_off(dent); v9fs_string_init(&name); v9fs_string_sprintf(&name, "%s", dent->d_name); /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ len = pdu_marshal(pdu, 11 + count, "Qqbs", - &qid, dent->d_off, + &qid, off, dent->d_type, &name); v9fs_string_free(&name); @@ -3515,9 +3549,15 @@ static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) f_bavail = stbuf->f_bavail / bsize_factor; f_files = stbuf->f_files; f_ffree = stbuf->f_ffree; +#ifdef CONFIG_DARWIN + fsid_val = (unsigned int)stbuf->f_fsid.val[0] | + (unsigned long long)stbuf->f_fsid.val[1] << 32; + f_namelen = NAME_MAX; +#else fsid_val = (unsigned int) stbuf->f_fsid.__val[0] | (unsigned long long)stbuf->f_fsid.__val[1] << 32; f_namelen = stbuf->f_namelen; +#endif return pdu_marshal(pdu, offset, "ddqqqqqqd", f_type, f_bsize, f_blocks, f_bfree, @@ -3887,6 +3927,24 @@ out_nofid: v9fs_string_free(&name); } +#if defined(CONFIG_LINUX) +/* Currently, only Linux has XATTR_SIZE_MAX */ +#define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX +#elif defined(CONFIG_DARWIN) +/* + * Darwin doesn't seem to define a maximum xattr size in its user + * space header, so manually configure it across platforms as 64k. + * + * Having no limit at all can lead to QEMU crashing during large g_malloc() + * calls. Because QEMU does not currently support macOS guests, the below + * preliminary solution only works due to its being a reflection of the limit of + * Linux guests. + */ +#define P9_XATTR_SIZE_MAX 65536 +#else +#error Missing definition for P9_XATTR_SIZE_MAX for this host system +#endif + static void coroutine_fn v9fs_xattrcreate(void *opaque) { int flags, rflags = 0; @@ -3919,7 +3977,7 @@ static void coroutine_fn v9fs_xattrcreate(void *opaque) rflags |= XATTR_REPLACE; } - if (size > XATTR_SIZE_MAX) { + if (size > P9_XATTR_SIZE_MAX) { err = -E2BIG; goto out_nofid; } diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 1567b67841..994f952600 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -100,8 +100,8 @@ typedef enum P9ProtoVersion { V9FS_PROTO_2000L = 0x02, } P9ProtoVersion; -/** - * @brief Minimum message size supported by this 9pfs server. +/* + * Minimum message size supported by this 9pfs server. * * A client establishes a session by sending a Tversion request along with a * 'msize' parameter which suggests the server a maximum message size ever to be @@ -231,7 +231,7 @@ static inline void v9fs_readdir_init(P9ProtoVersion proto_version, V9fsDir *dir) } } -/** +/* * Type for 9p fs drivers' (a.k.a. 9p backends) result of readdir requests, * which is a chained list of directory entries. */ @@ -289,8 +289,8 @@ typedef enum AffixType_t { AffixType_Suffix, /* A.k.a. postfix. */ } AffixType_t; -/** - * @brief Unique affix of variable length. +/* + * Unique affix of variable length. * * An affix is (currently) either a suffix or a prefix, which is either * going to be prepended (prefix) or appended (suffix) with some other @@ -304,7 +304,7 @@ typedef struct VariLenAffix { AffixType_t type; /* Whether this affix is a suffix or a prefix. */ uint64_t value; /* Actual numerical value of this affix. */ /* - * Lenght of the affix, that is how many (of the lowest) bits of @c value + * Lenght of the affix, that is how many (of the lowest) bits of ``value`` * must be used for appending/prepending this affix to its final resulting, * unique number. */ diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c index c0873bde16..93ba44fb75 100644 --- a/hw/9pfs/codir.c +++ b/hw/9pfs/codir.c @@ -22,6 +22,8 @@ #include "qemu/coroutine.h" #include "qemu/main-loop.h" #include "coth.h" +#include "9p-xattr.h" +#include "9p-util.h" /* * Intended to be called from bottom-half (e.g. background I/O thread) @@ -139,9 +141,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, /* append next node to result chain */ if (!e) { - *entries = e = g_malloc0(sizeof(V9fsDirEnt)); + *entries = e = g_new0(V9fsDirEnt, 1); } else { - e = e->next = g_malloc0(sizeof(V9fsDirEnt)); + e = e->next = g_new0(V9fsDirEnt, 1); } e->dent = qemu_dirent_dup(dent); @@ -161,12 +163,12 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, break; } - e->st = g_malloc0(sizeof(struct stat)); + e->st = g_new0(struct stat, 1); memcpy(e->st, &stbuf, sizeof(struct stat)); } size += len; - saved_dir_pos = dent->d_off; + saved_dir_pos = qemu_dirent_off(dent); } /* restore (last) saved position */ @@ -182,14 +184,25 @@ out: } /** - * @brief Reads multiple directory entries in one rush. + * v9fs_co_readdir_many() - Reads multiple directory entries in one rush. + * + * @pdu: the causing 9p (T_readdir) client request + * @fidp: already opened directory where readdir shall be performed on + * @entries: output for directory entries (must not be NULL) + * @offset: initial position inside the directory the function shall + * seek to before retrieving the directory entries + * @maxsize: maximum result message body size (in bytes) + * @dostat: whether a stat() should be performed and returned for + * each directory entry + * Return: resulting response message body size (in bytes) on success, + * negative error code otherwise * * Retrieves the requested (max. amount of) directory entries from the fs * driver. This function must only be called by the main IO thread (top half). * Internally this function call will be dispatched to a background IO thread * (bottom half) where it is eventually executed by the fs driver. * - * @discussion Acquiring multiple directory entries in one rush from the fs + * Acquiring multiple directory entries in one rush from the fs * driver, instead of retrieving each directory entry individually, is very * beneficial from performance point of view. Because for every fs driver * request latency is added, which in practice could lead to overall @@ -197,20 +210,9 @@ out: * directory) if every directory entry was individually requested from fs * driver. * - * @note You must @b ALWAYS call @c v9fs_free_dirents(entries) after calling + * NOTE: You must ALWAYS call v9fs_free_dirents(entries) after calling * v9fs_co_readdir_many(), both on success and on error cases of this - * function, to avoid memory leaks once @p entries are no longer needed. - * - * @param pdu - the causing 9p (T_readdir) client request - * @param fidp - already opened directory where readdir shall be performed on - * @param entries - output for directory entries (must not be NULL) - * @param offset - initial position inside the directory the function shall - * seek to before retrieving the directory entries - * @param maxsize - maximum result message body size (in bytes) - * @param dostat - whether a stat() should be performed and returned for - * each directory entry - * @returns resulting response message body size (in bytes) on success, - * negative error code otherwise + * function, to avoid memory leaks once @entries are no longer needed. */ int coroutine_fn v9fs_co_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, struct V9fsDirEnt **entries, diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h index f83c7dda7b..1a1edbdc2a 100644 --- a/hw/9pfs/coth.h +++ b/hw/9pfs/coth.h @@ -19,7 +19,7 @@ #include "qemu/coroutine.h" #include "9p.h" -/** +/* * we want to use bottom half because we want to make sure the below * sequence of events. * @@ -29,7 +29,7 @@ * we cannot swap step 1 and 2, because that would imply worker thread * can enter coroutine while step1 is still running * - * @b PERFORMANCE @b CONSIDERATIONS: As a rule of thumb, keep in mind + * PERFORMANCE CONSIDERATIONS: As a rule of thumb, keep in mind * that hopping between threads adds @b latency! So when handling a * 9pfs request, avoid calling v9fs_co_run_in_worker() too often, because * this might otherwise sum up to a significant, huge overall latency for diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index 99be5d9119..12443b6ad5 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -4,7 +4,6 @@ fs_ss.add(files( '9p-posix-acl.c', '9p-proxy.c', '9p-synth.c', - '9p-util.c', '9p-xattr-user.c', '9p-xattr.c', '9p.c', @@ -14,6 +13,8 @@ fs_ss.add(files( 'coth.c', 'coxattr.c', )) +fs_ss.add(when: 'CONFIG_LINUX', if_true: files('9p-util-linux.c')) +fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-util-darwin.c')) fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.c')) softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c index 734e4c5986..a43f6dafc9 100644 --- a/hw/acpi/acpi-pci-hotplug-stub.c +++ b/hw/acpi/acpi-pci-hotplug-stub.c @@ -41,7 +41,3 @@ void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off) return; } -bool vmstate_acpi_pcihp_use_acpi_index(void *opaque, int version_id) -{ - return false; -} diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 8966e16320..4086879ebf 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -207,7 +207,7 @@ build_append_nameseg(GArray *array, const char *seg) g_array_append_vals(array, "____", ACPI_NAMESEG_LEN - len); } -static void GCC_FMT_ATTR(2, 0) +static void G_GNUC_PRINTF(2, 0) build_append_namestringv(GArray *array, const char *format, va_list ap) { char *s; @@ -270,7 +270,7 @@ build_append_namestringv(GArray *array, const char *format, va_list ap) g_strfreev(segs); } -GCC_FMT_ATTR(2, 3) +G_GNUC_PRINTF(2, 3) static void build_append_namestring(GArray *array, const char *format, ...) { va_list ap; @@ -2152,7 +2152,13 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, build_append_int_noprefix(tbl, 0, 1); /* DAY_ALRM */ build_append_int_noprefix(tbl, 0, 1); /* MON_ALRM */ build_append_int_noprefix(tbl, f->rtc_century, 1); /* CENTURY */ - build_append_int_noprefix(tbl, 0, 2); /* IAPC_BOOT_ARCH */ + /* IAPC_BOOT_ARCH */ + if (f->rev == 1) { + build_append_int_noprefix(tbl, 0, 2); + } else { + /* since ACPI v2.0 */ + build_append_int_noprefix(tbl, f->iapc_boot_arch, 2); + } build_append_int_noprefix(tbl, 0, 1); /* Reserved */ build_append_int_noprefix(tbl, f->flags, 4); /* Flags */ diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index c0a23cf467..de509c2b48 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -80,11 +80,6 @@ #define UEFI_CPER_RECORD_MIN_SIZE 128U #define UEFI_CPER_RECORD_LENGTH_OFFSET 20U #define UEFI_CPER_RECORD_ID_OFFSET 96U -#define IS_UEFI_CPER_RECORD(ptr) \ - (((ptr)[0] == 'C') && \ - ((ptr)[1] == 'P') && \ - ((ptr)[2] == 'E') && \ - ((ptr)[3] == 'R')) /* * NOTE that when accessing CPER fields within a record, memcpy() diff --git a/hw/acpi/hmat.c b/hw/acpi/hmat.c index 6913ebf730..3a6d51282a 100644 --- a/hw/acpi/hmat.c +++ b/hw/acpi/hmat.c @@ -128,7 +128,7 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb, } /* Latency or Bandwidth Entries */ - entry_list = g_malloc0(num_initiator * num_target * sizeof(uint16_t)); + entry_list = g_new0(uint16_t, num_initiator * num_target); for (i = 0; i < hmat_lb->list->len; i++) { lb_data = &g_array_index(hmat_lb->list, HMAT_LB_Data, i); index = lb_data->initiator * num_target + lb_data->target; diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 6befd23e16..bf65bbea49 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -32,6 +32,7 @@ #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_host.h" #include "hw/pci/pcie_port.h" +#include "hw/pci-bridge/xio3130_downstream.h" #include "hw/i386/acpi-build.h" #include "hw/acpi/acpi.h" #include "hw/pci/pci_bus.h" @@ -336,6 +337,8 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, { PCIDevice *pdev = PCI_DEVICE(dev); int slot = PCI_SLOT(pdev->devfn); + PCIDevice *bridge; + PCIBus *bus; int bsel; /* Don't send event when device is enabled during qemu machine creation: @@ -365,7 +368,14 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, return; } - bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev)); + bus = pci_get_bus(pdev); + bridge = pci_bridge_get_device(bus); + if (object_dynamic_cast(OBJECT(bridge), TYPE_PCIE_ROOT_PORT) || + object_dynamic_cast(OBJECT(bridge), TYPE_XIO3130_DOWNSTREAM)) { + pcie_cap_slot_enable_power(bridge); + } + + bsel = acpi_pcihp_get_bsel(bus); g_assert(bsel >= 0); s->acpi_pcihp_pci_status[bsel].up |= (1U << slot); acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); @@ -544,12 +554,6 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, OBJ_PROP_FLAG_READ); } -bool vmstate_acpi_pcihp_use_acpi_index(void *opaque, int version_id) -{ - AcpiPciHpState *s = opaque; - return s->acpi_index; -} - const VMStateDescription vmstate_acpi_pcihp_pci_status = { .name = "acpi_pcihp_pci_status", .version_id = 1, diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index cc37fa3416..fe5625d07a 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -82,6 +82,7 @@ struct PIIX4PMState { AcpiPciHpState acpi_pci_hotplug; bool use_acpi_hotplug_bridge; bool use_acpi_root_pci_hotplug; + bool not_migrate_acpi_index; uint8_t disable_s3; uint8_t disable_s4; @@ -267,6 +268,16 @@ static bool piix4_vmstate_need_smbus(void *opaque, int version_id) return pm_smbus_vmstate_needed(); } +/* + * This is a fudge to turn off the acpi_index field, + * whose test was always broken on piix4 with 6.2 and older machine types. + */ +static bool vmstate_test_migrate_acpi_index(void *opaque, int version_id) +{ + PIIX4PMState *s = PIIX4_PM(opaque); + return s->use_acpi_hotplug_bridge && !s->not_migrate_acpi_index; +} + /* qemu-kvm 1.2 uses version 3 but advertised as 2 * To support incoming qemu-kvm 1.2 migration, change version_id * and minimum_version_id to 2 below (which breaks migration from @@ -297,7 +308,7 @@ static const VMStateDescription vmstate_acpi = { struct AcpiPciHpPciStatus), VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState, vmstate_test_use_acpi_hotplug_bridge, - vmstate_acpi_pcihp_use_acpi_index), + vmstate_test_migrate_acpi_index), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription*[]) { @@ -652,6 +663,8 @@ static Property piix4_pm_properties[] = { DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState, acpi_memory_hotplug.is_enabled, true), DEFINE_PROP_BOOL("smm-compat", PIIX4PMState, smm_compat, false), + DEFINE_PROP_BOOL("x-not-migrate-acpi-index", PIIX4PMState, + not_migrate_acpi_index, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c index c5fcae29f6..0d29663129 100644 --- a/hw/adc/aspeed_adc.c +++ b/hw/adc/aspeed_adc.c @@ -389,6 +389,15 @@ static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 2; } +static void aspeed_1030_adc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); + + dc->desc = "ASPEED 1030 ADC Controller"; + aac->nr_engines = 2; +} + static const TypeInfo aspeed_adc_info = { .name = TYPE_ASPEED_ADC, .parent = TYPE_SYS_BUS_DEVICE, @@ -415,6 +424,12 @@ static const TypeInfo aspeed_2600_adc_info = { .class_init = aspeed_2600_adc_class_init, }; +static const TypeInfo aspeed_1030_adc_info = { + .name = TYPE_ASPEED_1030_ADC, + .parent = TYPE_ASPEED_ADC, + .class_init = aspeed_1030_adc_class_init, /* No change since AST2600 */ +}; + static void aspeed_adc_register_types(void) { type_register_static(&aspeed_adc_engine_info); @@ -422,6 +437,7 @@ static void aspeed_adc_register_types(void) type_register_static(&aspeed_2400_adc_info); type_register_static(&aspeed_2500_adc_info); type_register_static(&aspeed_2600_adc_info); + type_register_static(&aspeed_1030_adc_info); } type_init(aspeed_adc_register_types); diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index c78ed96d0e..f4349eba83 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -7,7 +7,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "elf.h" #include "hw/loader.h" diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 6945330030..97f3b38019 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -400,6 +400,7 @@ config NPCM7XX select SMBUS select AT24C # EEPROM select MAX34451 + select ISL_PMBUS_VR select PL310 # cache controller select PMBUS select SERIAL diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 05e84728cb..79082289ea 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -130,9 +130,7 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) int i; for (i = 0; i < AW_A10_NUM_USB; i++) { - char bus[16]; - - sprintf(bus, "usb-bus.%d", i); + g_autofree char *bus = g_strdup_printf("usb-bus.%d", i); object_property_set_bool(OBJECT(&s->ehci[i]), "companion-enable", true, &error_fatal); diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 41cfca0f22..32349ec94b 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -577,7 +577,7 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) int asidx; CPUState *cs = CPU(cpu); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN big_endian = 1; #else big_endian = 0; diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index d911dc904f..a74c13ab0f 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -24,6 +24,7 @@ #include "hw/loader.h" #include "qemu/error-report.h" #include "qemu/units.h" +#include "hw/qdev-clock.h" static struct arm_boot_info aspeed_board_binfo = { .board_id = -1, /* device-tree-only board */ @@ -106,17 +107,6 @@ struct AspeedMachineState { SCU_HW_STRAP_VGA_SIZE_SET(VGA_16M_DRAM) | \ SCU_AST2500_HW_STRAP_RESERVED1) -/* Swift hardware value: 0xF11AD206 */ -#define SWIFT_BMC_HW_STRAP1 ( \ - AST2500_HW_STRAP1_DEFAULTS | \ - SCU_AST2500_HW_STRAP_SPI_AUTOFETCH_ENABLE | \ - SCU_AST2500_HW_STRAP_GPIO_STRAP_ENABLE | \ - SCU_AST2500_HW_STRAP_UART_DEBUG | \ - SCU_AST2500_HW_STRAP_DDR4_ENABLE | \ - SCU_H_PLL_BYPASS_EN | \ - SCU_AST2500_HW_STRAP_ACPI_ENABLE | \ - SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER)) - #define G220A_BMC_HW_STRAP1 ( \ SCU_AST2500_HW_STRAP_SPI_AUTOFETCH_ENABLE | \ SCU_AST2500_HW_STRAP_GPIO_STRAP_ENABLE | \ @@ -171,13 +161,18 @@ struct AspeedMachineState { #define TACOMA_BMC_HW_STRAP2 0x00000040 /* Rainier hardware value: (QEMU prototype) */ -#define RAINIER_BMC_HW_STRAP1 0x00000000 -#define RAINIER_BMC_HW_STRAP2 0x00000000 +#define RAINIER_BMC_HW_STRAP1 0x00422016 +#define RAINIER_BMC_HW_STRAP2 0x80000848 /* Fuji hardware value */ #define FUJI_BMC_HW_STRAP1 0x00000000 #define FUJI_BMC_HW_STRAP2 0x00000000 +/* Bletchley hardware value */ +/* TODO: Leave same as EVB for now. */ +#define BLETCHLEY_BMC_HW_STRAP1 AST2600_EVB_HW_STRAP1 +#define BLETCHLEY_BMC_HW_STRAP2 AST2600_EVB_HW_STRAP2 + /* * The max ram region is for firmwares that scan the address space * with load/store to guess how much RAM the SoC has. @@ -257,7 +252,7 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, Error **errp) { BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - uint8_t *storage; + g_autofree void *storage = NULL; int64_t size; /* The block backend size should have already been 'validated' by @@ -273,23 +268,25 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, rom_size = size; } - storage = g_new0(uint8_t, rom_size); + storage = g_malloc0(rom_size); if (blk_pread(blk, 0, storage, rom_size) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); - g_free(storage); } -static void aspeed_board_init_flashes(AspeedSMCState *s, - const char *flashtype, - int unit0) +static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, + unsigned int count, int unit0) { - int i ; + int i; - for (i = 0; i < s->num_cs; ++i) { + if (!flashtype) { + return; + } + + for (i = 0; i < count; ++i) { DriveInfo *dinfo = drive_get(IF_MTD, 0, unit0 + i); qemu_irq cs_line; DeviceState *dev; @@ -356,8 +353,6 @@ static void aspeed_machine_init(MachineState *machine) &error_abort); object_property_set_int(OBJECT(&bmc->soc), "hw-strap2", amc->hw_strap2, &error_abort); - object_property_set_int(OBJECT(&bmc->soc), "num-cs", amc->num_cs, - &error_abort); object_property_set_link(OBJECT(&bmc->soc), "dram", OBJECT(machine->ram), &error_abort); if (machine->kernel_filename) { @@ -385,10 +380,10 @@ static void aspeed_machine_init(MachineState *machine) aspeed_board_init_flashes(&bmc->soc.fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, - 0); + amc->num_cs, 0); aspeed_board_init_flashes(&bmc->soc.spi[0], bmc->spi_model ? bmc->spi_model : amc->spi_model, - bmc->soc.fmc.num_cs); + 1, amc->num_cs); /* Install first FMC flash content as a boot rom. */ if (drive0) { @@ -544,33 +539,10 @@ static void romulus_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "ds1338", 0x32); } -static void swift_bmc_i2c_init(AspeedMachineState *bmc) +static void create_pca9552(AspeedSoCState *soc, int bus_id, int addr) { - AspeedSoCState *soc = &bmc->soc; - - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), "pca9552", 0x60); - - /* The swift board expects a TMP275 but a TMP105 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "tmp105", 0x48); - /* The swift board expects a pca9551 but a pca9552 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "pca9552", 0x60); - - /* The swift board expects an Epson RX8900 RTC but a ds1338 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), "ds1338", 0x32); - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), "pca9552", 0x60); - - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "tmp423", 0x4c); - /* The swift board expects a pca9539 but a pca9552 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "pca9552", 0x74); - - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 10), "tmp423", 0x4c); - /* The swift board expects a pca9539 but a pca9552 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 10), "pca9552", - 0x74); - - /* The swift board expects a TMP275 but a TMP105 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 12), "tmp105", 0x48); - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 12), "tmp105", 0x4a); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, bus_id), + TYPE_PCA9552, addr); } static void sonorapass_bmc_i2c_init(AspeedMachineState *bmc) @@ -589,9 +561,9 @@ static void sonorapass_bmc_i2c_init(AspeedMachineState *bmc) smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 4), 0x54, eeprom4_54); /* PCA9539 @ 0x76, but PCA9552 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "pca9552", 0x76); + create_pca9552(soc, 4, 0x76); /* PCA9539 @ 0x77, but PCA9552 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "pca9552", 0x77); + create_pca9552(soc, 4, 0x77); /* bus 6 : */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 6), "tmp105", 0x48); @@ -602,8 +574,8 @@ static void sonorapass_bmc_i2c_init(AspeedMachineState *bmc) uint8_t *eeprom8_56 = g_malloc0(8 * 1024); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 8), 0x56, eeprom8_56); - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), "pca9552", 0x60); - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), "pca9552", 0x61); + create_pca9552(soc, 8, 0x60); + create_pca9552(soc, 8, 0x61); /* bus 8 : adc128d818 @ 0x1d */ /* bus 8 : adc128d818 @ 0x1f */ @@ -741,8 +713,7 @@ static void fp5280g2_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "ds1338", 0x68); /* It expects a pca9555 but a pca9552 is compatible */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), TYPE_PCA9552, - 0x20); + create_pca9552(soc, 8, 0x30); } static void rainier_bmc_i2c_init(AspeedMachineState *bmc) @@ -752,6 +723,8 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 0), 0x51, 32 * KiB); + create_pca9552(soc, 3, 0x61); + /* The rainier expects a TMP275 but a TMP105 is compatible */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), TYPE_TMP105, 0x48); @@ -764,11 +737,14 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 2), 0x52, 64 * KiB); + create_pca9552(soc, 4, 0x60); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), TYPE_TMP105, 0x48); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), TYPE_TMP105, 0x49); + create_pca9552(soc, 5, 0x60); + create_pca9552(soc, 5, 0x61); i2c_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), "pca9546", 0x70); aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); @@ -787,8 +763,13 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 2), 0x50, 64 * KiB); aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 3), 0x51, 64 * KiB); + create_pca9552(soc, 7, 0x30); + create_pca9552(soc, 7, 0x31); + create_pca9552(soc, 7, 0x32); + create_pca9552(soc, 7, 0x33); /* Bus 7: TODO max31785@52 */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "pca9552", 0x61); + create_pca9552(soc, 7, 0x60); + create_pca9552(soc, 7, 0x61); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "dps310", 0x76); /* Bus 7: TODO si7021-a20@20 */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), TYPE_TMP105, @@ -802,7 +783,8 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) 0x4a); aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 8), 0x50, 64 * KiB); aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, 64 * KiB); - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), "pca9552", 0x61); + create_pca9552(soc, 8, 0x60); + create_pca9552(soc, 8, 0x61); /* Bus 8: ucd90320@11 */ /* Bus 8: ucd90320@b */ /* Bus 8: ucd90320@c */ @@ -823,13 +805,17 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) "pca9546", 0x70); aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); + create_pca9552(soc, 11, 0x60); aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 13), 0x50, 64 * KiB); + create_pca9552(soc, 13, 0x60); aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 14), 0x50, 64 * KiB); + create_pca9552(soc, 14, 0x60); aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 15), 0x50, 64 * KiB); + create_pca9552(soc, 15, 0x60); } static void get_pca9548_channels(I2CBus *bus, uint8_t mux_addr, @@ -917,6 +903,54 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc) } } +#define TYPE_TMP421 "tmp421" + +static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + I2CBus *i2c[13] = {}; + for (int i = 0; i < 13; i++) { + if ((i == 8) || (i == 11)) { + continue; + } + i2c[i] = aspeed_i2c_get_bus(&soc->i2c, i); + } + + /* Bus 0 - 5 all have the same config. */ + for (int i = 0; i < 6; i++) { + /* Missing model: ti,ina230 @ 0x45 */ + /* Missing model: mps,mp5023 @ 0x40 */ + i2c_slave_create_simple(i2c[i], TYPE_TMP421, 0x4f); + /* Missing model: nxp,pca9539 @ 0x76, but PCA9552 works enough */ + i2c_slave_create_simple(i2c[i], TYPE_PCA9552, 0x76); + i2c_slave_create_simple(i2c[i], TYPE_PCA9552, 0x67); + /* Missing model: fsc,fusb302 @ 0x22 */ + } + + /* Bus 6 */ + at24c_eeprom_init(i2c[6], 0x56, 65536); + /* Missing model: nxp,pcf85263 @ 0x51 , but ds1338 works enough */ + i2c_slave_create_simple(i2c[6], "ds1338", 0x51); + + + /* Bus 7 */ + at24c_eeprom_init(i2c[7], 0x54, 65536); + + /* Bus 9 */ + i2c_slave_create_simple(i2c[9], TYPE_TMP421, 0x4f); + + /* Bus 10 */ + i2c_slave_create_simple(i2c[10], TYPE_TMP421, 0x4f); + /* Missing model: ti,hdc1080 @ 0x40 */ + i2c_slave_create_simple(i2c[10], TYPE_PCA9552, 0x67); + + /* Bus 12 */ + /* Missing model: adi,adm1278 @ 0x11 */ + i2c_slave_create_simple(i2c[12], TYPE_TMP421, 0x4c); + i2c_slave_create_simple(i2c[12], TYPE_TMP421, 0x4d); + i2c_slave_create_simple(i2c[12], TYPE_PCA9552, 0x67); +} + static bool aspeed_get_mmio_exec(Object *obj, Error **errp) { return ASPEED_MACHINE(obj)->mmio_exec; @@ -1059,7 +1093,7 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) mc->desc = "Aspeed AST2500 EVB (ARM1176)"; amc->soc_name = "ast2500-a1"; amc->hw_strap1 = AST2500_EVB_HW_STRAP1; - amc->fmc_model = "w25q256"; + amc->fmc_model = "mx25l25635e"; amc->spi_model = "mx25l25635e"; amc->num_cs = 1; amc->i2c_init = ast2500_evb_i2c_init; @@ -1102,26 +1136,6 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) aspeed_soc_num_cpus(amc->soc_name); }; -static void aspeed_machine_swift_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); - - mc->desc = "OpenPOWER Swift BMC (ARM1176)"; - amc->soc_name = "ast2500-a1"; - amc->hw_strap1 = SWIFT_BMC_HW_STRAP1; - amc->fmc_model = "mx66l1g45g"; - amc->spi_model = "mx66l1g45g"; - amc->num_cs = 2; - amc->i2c_init = swift_bmc_i2c_init; - mc->default_ram_size = 512 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); - - mc->deprecation_reason = "redundant system. Please use a similar " - "OpenPOWER BMC, Witherspoon or Romulus."; -}; - static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1148,7 +1162,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) amc->soc_name = "ast2600-a3"; amc->hw_strap1 = AST2600_EVB_HW_STRAP1; amc->hw_strap2 = AST2600_EVB_HW_STRAP2; - amc->fmc_model = "w25q512jv"; + amc->fmc_model = "mx66u51235f"; amc->spi_model = "mx66u51235f"; amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON | @@ -1260,6 +1274,86 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) aspeed_soc_num_cpus(amc->soc_name); }; +static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Facebook Bletchley BMC (Cortex-A7)"; + amc->soc_name = "ast2600-a3"; + amc->hw_strap1 = BLETCHLEY_BMC_HW_STRAP1; + amc->hw_strap2 = BLETCHLEY_BMC_HW_STRAP2; + amc->fmc_model = "w25q01jvq"; + amc->spi_model = NULL; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC2_ON; + amc->i2c_init = bletchley_bmc_i2c_init; + mc->default_ram_size = 512 * MiB; + mc->default_cpus = mc->min_cpus = mc->max_cpus = + aspeed_soc_num_cpus(amc->soc_name); +} + +#define AST1030_INTERNAL_FLASH_SIZE (1024 * 1024) +/* Main SYSCLK frequency in Hz (200MHz) */ +#define SYSCLK_FRQ 200000000ULL + +static void aspeed_minibmc_machine_init(MachineState *machine) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(machine); + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); + Clock *sysclk; + + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, SYSCLK_FRQ); + + object_initialize_child(OBJECT(machine), "soc", &bmc->soc, amc->soc_name); + qdev_connect_clock_in(DEVICE(&bmc->soc), "sysclk", sysclk); + + qdev_prop_set_uint32(DEVICE(&bmc->soc), "uart-default", + amc->uart_default); + qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort); + + aspeed_board_init_flashes(&bmc->soc.fmc, + bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, + amc->num_cs, + 0); + + aspeed_board_init_flashes(&bmc->soc.spi[0], + bmc->spi_model ? bmc->spi_model : amc->spi_model, + amc->num_cs, amc->num_cs); + + aspeed_board_init_flashes(&bmc->soc.spi[1], + bmc->spi_model ? bmc->spi_model : amc->spi_model, + amc->num_cs, (amc->num_cs * 2)); + + if (amc->i2c_init) { + amc->i2c_init(bmc); + } + + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + AST1030_INTERNAL_FLASH_SIZE); +} + +static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, + void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST1030 MiniBMC (Cortex-M4)"; + amc->soc_name = "ast1030-a1"; + amc->hw_strap1 = 0; + amc->hw_strap2 = 0; + mc->init = aspeed_minibmc_machine_init; + mc->default_ram_size = 0; + mc->default_cpus = mc->min_cpus = mc->max_cpus = 1; + amc->fmc_model = "sst25vf032b"; + amc->spi_model = "sst25vf032b"; + amc->num_cs = 2; + amc->macs_mask = 0; +} + static const TypeInfo aspeed_machine_types[] = { { .name = MACHINE_TYPE_NAME("palmetto-bmc"), @@ -1277,10 +1371,6 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("romulus-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_romulus_class_init, - }, { - .name = MACHINE_TYPE_NAME("swift-bmc"), - .parent = TYPE_ASPEED_MACHINE, - .class_init = aspeed_machine_swift_class_init, }, { .name = MACHINE_TYPE_NAME("sonorapass-bmc"), .parent = TYPE_ASPEED_MACHINE, @@ -1317,6 +1407,14 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("fuji-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_fuji_class_init, + }, { + .name = MACHINE_TYPE_NAME("bletchley-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_bletchley_class_init, + }, { + .name = MACHINE_TYPE_NAME("ast1030-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_minibmc_machine_ast1030_evb_class_init, }, { .name = TYPE_ASPEED_MACHINE, .parent = TYPE_MACHINE, diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c new file mode 100644 index 0000000000..4271549282 --- /dev/null +++ b/hw/arm/aspeed_ast10x0.c @@ -0,0 +1,299 @@ +/* + * ASPEED Ast10x0 SoC + * + * Copyright (C) 2022 ASPEED Technology Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * Implementation extracted from the AST2600 and adapted for Ast10x0. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" +#include "hw/char/serial.h" +#include "hw/arm/aspeed_soc.h" + +#define ASPEED_SOC_IOMEM_SIZE 0x00200000 + +static const hwaddr aspeed_soc_ast1030_memmap[] = { + [ASPEED_DEV_SRAM] = 0x00000000, + [ASPEED_DEV_SBC] = 0x79000000, + [ASPEED_DEV_IOMEM] = 0x7E600000, + [ASPEED_DEV_PWM] = 0x7E610000, + [ASPEED_DEV_FMC] = 0x7E620000, + [ASPEED_DEV_SPI1] = 0x7E630000, + [ASPEED_DEV_SPI2] = 0x7E640000, + [ASPEED_DEV_SCU] = 0x7E6E2000, + [ASPEED_DEV_ADC] = 0x7E6E9000, + [ASPEED_DEV_SBC] = 0x7E6F2000, + [ASPEED_DEV_GPIO] = 0x7E780000, + [ASPEED_DEV_TIMER1] = 0x7E782000, + [ASPEED_DEV_UART5] = 0x7E784000, + [ASPEED_DEV_WDT] = 0x7E785000, + [ASPEED_DEV_LPC] = 0x7E789000, + [ASPEED_DEV_I2C] = 0x7E7B0000, +}; + +static const int aspeed_soc_ast1030_irqmap[] = { + [ASPEED_DEV_UART5] = 8, + [ASPEED_DEV_GPIO] = 11, + [ASPEED_DEV_TIMER1] = 16, + [ASPEED_DEV_TIMER2] = 17, + [ASPEED_DEV_TIMER3] = 18, + [ASPEED_DEV_TIMER4] = 19, + [ASPEED_DEV_TIMER5] = 20, + [ASPEED_DEV_TIMER6] = 21, + [ASPEED_DEV_TIMER7] = 22, + [ASPEED_DEV_TIMER8] = 23, + [ASPEED_DEV_WDT] = 24, + [ASPEED_DEV_LPC] = 35, + [ASPEED_DEV_FMC] = 39, + [ASPEED_DEV_PWM] = 44, + [ASPEED_DEV_ADC] = 46, + [ASPEED_DEV_SPI1] = 65, + [ASPEED_DEV_SPI2] = 66, + [ASPEED_DEV_I2C] = 110, /* 110 ~ 123 */ + [ASPEED_DEV_KCS] = 138, /* 138 -> 142 */ +}; + +static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + return qdev_get_gpio_in(DEVICE(&s->armv7m), sc->irqmap[ctrl]); +} + +static void aspeed_soc_ast1030_init(Object *obj) +{ + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + char socname[8]; + char typename[64]; + int i; + + if (sscanf(sc->name, "%7s", socname) != 1) { + g_assert_not_reached(); + } + + object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + + snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); + object_initialize_child(obj, "scu", &s->scu, typename); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev); + + object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), "hw-strap1"); + object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), "hw-strap2"); + + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); + object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); + + snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); + object_initialize_child(obj, "adc", &s->adc, typename); + + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); + object_initialize_child(obj, "fmc", &s->fmc, typename); + + for (i = 0; i < sc->spis_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); + object_initialize_child(obj, "spi[*]", &s->spi[i], typename); + } + + object_initialize_child(obj, "lpc", &s->lpc, TYPE_ASPEED_LPC); + + object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); + + for (i = 0; i < sc->wdts_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); + object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); + } +} + +static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) +{ + AspeedSoCState *s = ASPEED_SOC(dev_soc); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *armv7m; + Error *err = NULL; + int i; + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* General I/O memory space to catch all unimplemented device */ + create_unimplemented_device("aspeed.sbc", + sc->memmap[ASPEED_DEV_SBC], + 0x40000); + create_unimplemented_device("aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); + + /* AST1030 CPU Core */ + armv7m = DEVICE(&s->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 256); + qdev_prop_set_string(armv7m, "cpu-type", sc->cpu_type); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + object_property_set_link(OBJECT(&s->armv7m), "memory", + OBJECT(system_memory), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), &error_abort); + + /* Internal SRAM */ + memory_region_init_ram(&s->sram, NULL, "aspeed.sram", sc->sram_size, &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(system_memory, + sc->memmap[ASPEED_DEV_SRAM], + &s->sram); + + /* SCU */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* LPC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + + /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); + + /* + * On the AST1030 LPC subdevice IRQs are connected straight to the GIC. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, + qdev_get_gpio_in(DEVICE(&s->armv7m), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_1)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, + qdev_get_gpio_in(DEVICE(&s->armv7m), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_2)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, + qdev_get_gpio_in(DEVICE(&s->armv7m), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_3)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, + qdev_get_gpio_in(DEVICE(&s->armv7m), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); + + /* UART5 - attach an 8250 to the IO space as our UART */ + serial_mm_init(get_system_memory(), sc->memmap[ASPEED_DEV_UART5], 2, + aspeed_soc_get_irq(s, ASPEED_DEV_UART5), + 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN); + + /* Timer */ + object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + sc->memmap[ASPEED_DEV_TIMER1]); + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); + } + + /* ADC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + + /* FMC, The number of CS is set at the board level */ + object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(&s->sram), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + + /* SPI */ + for (i = 0; i < sc->spis_num; i++) { + object_property_set_link(OBJECT(&s->spi[i]), "dram", + OBJECT(&s->sram), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + sc->memmap[ASPEED_DEV_SPI1 + i]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); + } + + /* Secure Boot Controller */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + + /* Watch dog */ + for (i = 0; i < sc->wdts_num; i++) { + AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + + object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, + sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); + } +} + +static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc); + + dc->realize = aspeed_soc_ast1030_realize; + + sc->name = "ast1030-a1"; + sc->cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); + sc->silicon_rev = AST1030_A1_SILICON_REV; + sc->sram_size = 0xc0000; + sc->spis_num = 2; + sc->ehcis_num = 0; + sc->wdts_num = 4; + sc->macs_num = 1; + sc->irqmap = aspeed_soc_ast1030_irqmap; + sc->memmap = aspeed_soc_ast1030_memmap; + sc->num_cpus = 1; +} + +static const TypeInfo aspeed_soc_ast1030_type_info = { + .name = "ast1030-a1", + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(AspeedSoCState), + .instance_init = aspeed_soc_ast1030_init, + .class_init = aspeed_soc_ast1030_class_init, + .class_size = sizeof(AspeedSoCClass), +}; + +static void aspeed_soc_register_types(void) +{ + type_register_static(&aspeed_soc_ast1030_type_info); +} + +type_init(aspeed_soc_register_types) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 12f6edc081..eedda7badc 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -47,6 +47,8 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_XDMA] = 0x1E6E7000, [ASPEED_DEV_ADC] = 0x1E6E9000, [ASPEED_DEV_DP] = 0x1E6EB000, + [ASPEED_DEV_SBC] = 0x1E6F2000, + [ASPEED_DEV_EMMC_BC] = 0x1E6f5000, [ASPEED_DEV_VIDEO] = 0x1E700000, [ASPEED_DEV_SDHCI] = 0x1E740000, [ASPEED_DEV_EMMC] = 0x1E750000, @@ -162,7 +164,6 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); object_initialize_child(obj, "fmc", &s->fmc, typename); - object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs"); for (i = 0; i < sc->spis_num; i++) { snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); @@ -227,6 +228,8 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "hace", &s->hace, typename); object_initialize_child(obj, "i3c", &s->i3c, TYPE_ASPEED_I3C); + + object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); } /* @@ -255,6 +258,11 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_DEV_VIDEO], 0x1000); + /* eMMC Boot Controller stub */ + create_unimplemented_device("aspeed.emmc-boot-controller", + sc->memmap[ASPEED_DEV_EMMC_BC], + 0x1000); + /* CPU */ for (i = 0; i < sc->num_cpus; i++) { if (sc->num_cpus > 1) { @@ -380,7 +388,6 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) for (i = 0; i < sc->spis_num; i++) { object_property_set_link(OBJECT(&s->spi[i]), "dram", OBJECT(s->dram_mr), &error_abort); - object_property_set_int(OBJECT(&s->spi[i]), "num-cs", 1, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } @@ -539,6 +546,12 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) /* The AST2600 I3C controller has one IRQ per bus. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq); } + + /* Secure Boot Controller */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); } static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 7d53cf2f51..58714cb2a0 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -170,7 +170,6 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); object_initialize_child(obj, "fmc", &s->fmc, typename); - object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs"); for (i = 0; i < sc->spis_num; i++) { snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); @@ -327,7 +326,6 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) /* SPI */ for (i = 0; i < sc->spis_num; i++) { - object_property_set_int(OBJECT(&s->spi[i]), "num-cs", 1, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } diff --git a/hw/arm/boot.c b/hw/arm/boot.c index b1e95978f2..a47f38dfc9 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -488,9 +488,14 @@ static void fdt_add_psci_node(void *fdt) } qemu_fdt_add_subnode(fdt, "/psci"); - if (armcpu->psci_version == 2) { - const char comp[] = "arm,psci-0.2\0arm,psci"; - qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); + if (armcpu->psci_version >= QEMU_PSCI_VERSION_0_2) { + if (armcpu->psci_version < QEMU_PSCI_VERSION_1_0) { + const char comp[] = "arm,psci-0.2\0arm,psci"; + qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); + } else { + const char comp[] = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; + qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); + } cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF; if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) { diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 614232165c..6df5547977 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -39,10 +39,7 @@ static void digic_init(Object *obj) object_initialize_child(obj, "cpu", &s->cpu, ARM_CPU_TYPE_NAME("arm946")); for (i = 0; i < DIGIC4_NB_TIMERS; i++) { -#define DIGIC_TIMER_NAME_MLEN 11 - char name[DIGIC_TIMER_NAME_MLEN]; - - snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i); + g_autofree char *name = g_strdup_printf("timer[%d]", i); object_initialize_child(obj, name, &s->timer[i], TYPE_DIGIC_TIMER); } diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 0299e81f85..8dafa2215b 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -101,6 +101,348 @@ #define EXYNOS4210_PL330_BASE1_ADDR 0x12690000 #define EXYNOS4210_PL330_BASE2_ADDR 0x12850000 +enum ExtGicId { + EXT_GIC_ID_MDMA_LCD0 = 66, + EXT_GIC_ID_PDMA0, + EXT_GIC_ID_PDMA1, + EXT_GIC_ID_TIMER0, + EXT_GIC_ID_TIMER1, + EXT_GIC_ID_TIMER2, + EXT_GIC_ID_TIMER3, + EXT_GIC_ID_TIMER4, + EXT_GIC_ID_MCT_L0, + EXT_GIC_ID_WDT, + EXT_GIC_ID_RTC_ALARM, + EXT_GIC_ID_RTC_TIC, + EXT_GIC_ID_GPIO_XB, + EXT_GIC_ID_GPIO_XA, + EXT_GIC_ID_MCT_L1, + EXT_GIC_ID_IEM_APC, + EXT_GIC_ID_IEM_IEC, + EXT_GIC_ID_NFC, + EXT_GIC_ID_UART0, + EXT_GIC_ID_UART1, + EXT_GIC_ID_UART2, + EXT_GIC_ID_UART3, + EXT_GIC_ID_UART4, + EXT_GIC_ID_MCT_G0, + EXT_GIC_ID_I2C0, + EXT_GIC_ID_I2C1, + EXT_GIC_ID_I2C2, + EXT_GIC_ID_I2C3, + EXT_GIC_ID_I2C4, + EXT_GIC_ID_I2C5, + EXT_GIC_ID_I2C6, + EXT_GIC_ID_I2C7, + EXT_GIC_ID_SPI0, + EXT_GIC_ID_SPI1, + EXT_GIC_ID_SPI2, + EXT_GIC_ID_MCT_G1, + EXT_GIC_ID_USB_HOST, + EXT_GIC_ID_USB_DEVICE, + EXT_GIC_ID_MODEMIF, + EXT_GIC_ID_HSMMC0, + EXT_GIC_ID_HSMMC1, + EXT_GIC_ID_HSMMC2, + EXT_GIC_ID_HSMMC3, + EXT_GIC_ID_SDMMC, + EXT_GIC_ID_MIPI_CSI_4LANE, + EXT_GIC_ID_MIPI_DSI_4LANE, + EXT_GIC_ID_MIPI_CSI_2LANE, + EXT_GIC_ID_MIPI_DSI_2LANE, + EXT_GIC_ID_ONENAND_AUDI, + EXT_GIC_ID_ROTATOR, + EXT_GIC_ID_FIMC0, + EXT_GIC_ID_FIMC1, + EXT_GIC_ID_FIMC2, + EXT_GIC_ID_FIMC3, + EXT_GIC_ID_JPEG, + EXT_GIC_ID_2D, + EXT_GIC_ID_PCIe, + EXT_GIC_ID_MIXER, + EXT_GIC_ID_HDMI, + EXT_GIC_ID_HDMI_I2C, + EXT_GIC_ID_MFC, + EXT_GIC_ID_TVENC, +}; + +enum ExtInt { + EXT_GIC_ID_EXTINT0 = 48, + EXT_GIC_ID_EXTINT1, + EXT_GIC_ID_EXTINT2, + EXT_GIC_ID_EXTINT3, + EXT_GIC_ID_EXTINT4, + EXT_GIC_ID_EXTINT5, + EXT_GIC_ID_EXTINT6, + EXT_GIC_ID_EXTINT7, + EXT_GIC_ID_EXTINT8, + EXT_GIC_ID_EXTINT9, + EXT_GIC_ID_EXTINT10, + EXT_GIC_ID_EXTINT11, + EXT_GIC_ID_EXTINT12, + EXT_GIC_ID_EXTINT13, + EXT_GIC_ID_EXTINT14, + EXT_GIC_ID_EXTINT15 +}; + +/* + * External GIC sources which are not from External Interrupt Combiner or + * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ, + * which is INTG16 in Internal Interrupt Combiner. + */ + +static const uint32_t +combiner_grp_to_gic_id[64 - EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { + /* int combiner groups 16-19 */ + { }, { }, { }, { }, + /* int combiner group 20 */ + { 0, EXT_GIC_ID_MDMA_LCD0 }, + /* int combiner group 21 */ + { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 }, + /* int combiner group 22 */ + { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2, + EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 }, + /* int combiner group 23 */ + { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC }, + /* int combiner group 24 */ + { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA }, + /* int combiner group 25 */ + { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC }, + /* int combiner group 26 */ + { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3, + EXT_GIC_ID_UART4 }, + /* int combiner group 27 */ + { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3, + EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6, + EXT_GIC_ID_I2C7 }, + /* int combiner group 28 */ + { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST}, + /* int combiner group 29 */ + { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2, + EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC }, + /* int combiner group 30 */ + { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE }, + /* int combiner group 31 */ + { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE }, + /* int combiner group 32 */ + { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 }, + /* int combiner group 33 */ + { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 }, + /* int combiner group 34 */ + { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC }, + /* int combiner group 35 */ + { 0, 0, 0, EXT_GIC_ID_MCT_L1 }, + /* int combiner group 36 */ + { EXT_GIC_ID_MIXER }, + /* int combiner group 37 */ + { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6, + EXT_GIC_ID_EXTINT7 }, + /* groups 38-50 */ + { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, + /* int combiner group 51 */ + { EXT_GIC_ID_MCT_L0 }, + /* group 52 */ + { }, + /* int combiner group 53 */ + { EXT_GIC_ID_WDT }, + /* groups 54-63 */ + { }, { }, { }, { }, { }, { }, { }, { }, { }, { } +}; + +#define EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit) ((grp) * 8 + (bit)) +#define EXYNOS4210_COMBINER_GET_GRP_NUM(irq) ((irq) / 8) +#define EXYNOS4210_COMBINER_GET_BIT_NUM(irq) \ + ((irq) - 8 * EXYNOS4210_COMBINER_GET_GRP_NUM(irq)) + +/* + * Some interrupt lines go to multiple combiner inputs. + * This data structure defines those: each array element is + * a list of combiner inputs which are connected together; + * the one with the smallest interrupt ID value must be first. + * As with combiner_grp_to_gic_id[], we rely on (0, 0) not being + * wired to anything so we can use 0 as a terminator. + */ +#define IRQNO(G, B) EXYNOS4210_COMBINER_GET_IRQ_NUM(G, B) +#define IRQNONE 0 + +#define COMBINERMAP_SIZE 16 + +static const int combinermap[COMBINERMAP_SIZE][6] = { + /* MDNIE_LCD1 */ + { IRQNO(0, 4), IRQNO(1, 0), IRQNONE }, + { IRQNO(0, 5), IRQNO(1, 1), IRQNONE }, + { IRQNO(0, 6), IRQNO(1, 2), IRQNONE }, + { IRQNO(0, 7), IRQNO(1, 3), IRQNONE }, + /* TMU */ + { IRQNO(2, 4), IRQNO(3, 4), IRQNONE }, + { IRQNO(2, 5), IRQNO(3, 5), IRQNONE }, + { IRQNO(2, 6), IRQNO(3, 6), IRQNONE }, + { IRQNO(2, 7), IRQNO(3, 7), IRQNONE }, + /* LCD1 */ + { IRQNO(11, 4), IRQNO(12, 0), IRQNONE }, + { IRQNO(11, 5), IRQNO(12, 1), IRQNONE }, + { IRQNO(11, 6), IRQNO(12, 2), IRQNONE }, + { IRQNO(11, 7), IRQNO(12, 3), IRQNONE }, + /* Multi-core timer */ + { IRQNO(1, 4), IRQNO(12, 4), IRQNO(35, 4), IRQNO(51, 4), IRQNO(53, 4), IRQNONE }, + { IRQNO(1, 5), IRQNO(12, 5), IRQNO(35, 5), IRQNO(51, 5), IRQNO(53, 5), IRQNONE }, + { IRQNO(1, 6), IRQNO(12, 6), IRQNO(35, 6), IRQNO(51, 6), IRQNO(53, 6), IRQNONE }, + { IRQNO(1, 7), IRQNO(12, 7), IRQNO(35, 7), IRQNO(51, 7), IRQNO(53, 7), IRQNONE }, +}; + +#undef IRQNO + +static const int *combinermap_entry(int irq) +{ + /* + * If the interrupt number passed in is the first entry in some + * line of the combinermap, return a pointer to that line; + * otherwise return NULL. + */ + int i; + for (i = 0; i < COMBINERMAP_SIZE; i++) { + if (combinermap[i][0] == irq) { + return combinermap[i]; + } + } + return NULL; +} + +static int mapline_size(const int *mapline) +{ + /* Return number of entries in this mapline in total */ + int i = 0; + + if (!mapline) { + /* Not in the map? IRQ goes to exactly one combiner input */ + return 1; + } + while (*mapline != IRQNONE) { + mapline++; + i++; + } + return i; +} + +/* + * Initialize board IRQs. + * These IRQs contain splitted Int/External Combiner and External Gic IRQs. + */ +static void exynos4210_init_board_irqs(Exynos4210State *s) +{ + uint32_t grp, bit, irq_id, n; + DeviceState *extgicdev = DEVICE(&s->ext_gic); + DeviceState *intcdev = DEVICE(&s->int_combiner); + DeviceState *extcdev = DEVICE(&s->ext_combiner); + int splitcount = 0; + DeviceState *splitter; + const int *mapline; + int numlines, splitin, in; + + for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) { + irq_id = 0; + if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4)) { + /* MCT_G0 is passed to External GIC */ + irq_id = EXT_GIC_ID_MCT_G0; + } + if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5)) { + /* MCT_G1 is passed to External and GIC */ + irq_id = EXT_GIC_ID_MCT_G1; + } + + if (s->irq_table[n]) { + /* + * This must be some non-first entry in a combinermap line, + * and we've already filled it in. + */ + continue; + } + mapline = combinermap_entry(n); + /* + * We need to connect the IRQ to multiple inputs on both combiners + * and possibly also to the external GIC. + */ + numlines = 2 * mapline_size(mapline); + if (irq_id) { + numlines++; + } + assert(splitcount < EXYNOS4210_NUM_SPLITTERS); + splitter = DEVICE(&s->splitter[splitcount]); + qdev_prop_set_uint16(splitter, "num-lines", numlines); + qdev_realize(splitter, NULL, &error_abort); + splitcount++; + + in = n; + splitin = 0; + for (;;) { + s->irq_table[in] = qdev_get_gpio_in(splitter, 0); + qdev_connect_gpio_out(splitter, splitin, + qdev_get_gpio_in(intcdev, in)); + qdev_connect_gpio_out(splitter, splitin + 1, + qdev_get_gpio_in(extcdev, in)); + splitin += 2; + if (!mapline) { + break; + } + mapline++; + in = *mapline; + if (in == IRQNONE) { + break; + } + } + if (irq_id) { + qdev_connect_gpio_out(splitter, splitin, + qdev_get_gpio_in(extgicdev, irq_id - 32)); + } + } + for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) { + /* these IDs are passed to Internal Combiner and External GIC */ + grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n); + bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); + irq_id = combiner_grp_to_gic_id[grp - + EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit]; + + if (s->irq_table[n]) { + /* + * This must be some non-first entry in a combinermap line, + * and we've already filled it in. + */ + continue; + } + + if (irq_id) { + assert(splitcount < EXYNOS4210_NUM_SPLITTERS); + splitter = DEVICE(&s->splitter[splitcount]); + qdev_prop_set_uint16(splitter, "num-lines", 2); + qdev_realize(splitter, NULL, &error_abort); + splitcount++; + s->irq_table[n] = qdev_get_gpio_in(splitter, 0); + qdev_connect_gpio_out(splitter, 0, qdev_get_gpio_in(intcdev, n)); + qdev_connect_gpio_out(splitter, 1, + qdev_get_gpio_in(extgicdev, irq_id - 32)); + } else { + s->irq_table[n] = qdev_get_gpio_in(intcdev, n); + } + } + /* + * We check this here to avoid a more obscure assert later when + * qdev_assert_realized_properly() checks that we realized every + * child object we initialized. + */ + assert(splitcount == EXYNOS4210_NUM_SPLITTERS); +} + +/* + * Get IRQ number from exynos4210 IRQ subsystem stub. + * To identify IRQ source use internal combiner group and bit number + * grp - group number + * bit - bit number inside group + */ +uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit) +{ + return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit); +} + static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43, 0x09, 0x00, 0x00, 0x00 }; @@ -205,7 +547,6 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) { Exynos4210State *s = EXYNOS4210_SOC(socdev); MemoryRegion *system_mem = get_system_memory(); - qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; SysBusDevice *busdev; DeviceState *dev, *uart[4], *pl330[3]; int i, n; @@ -229,81 +570,63 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); } - /*** IRQs ***/ - - s->irq_table = exynos4210_init_irq(&s->irqs); - /* IRQ Gate */ for (i = 0; i < EXYNOS4210_NCPUS; i++) { - dev = qdev_new("exynos4210.irq_gate"); - qdev_prop_set_uint32(dev, "n_in", EXYNOS4210_IRQ_GATE_NINPUTS); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - /* Get IRQ Gate input in gate_irq */ - for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { - gate_irq[i][n] = qdev_get_gpio_in(dev, n); - } - busdev = SYS_BUS_DEVICE(dev); - - /* Connect IRQ Gate output to CPU's IRQ line */ - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->cpu[i]), ARM_CPU_IRQ)); + DeviceState *orgate = DEVICE(&s->cpu_irq_orgate[i]); + object_property_set_int(OBJECT(orgate), "num-lines", + EXYNOS4210_IRQ_GATE_NINPUTS, + &error_abort); + qdev_realize(orgate, NULL, &error_abort); + qdev_connect_gpio_out(orgate, 0, + qdev_get_gpio_in(DEVICE(s->cpu[i]), ARM_CPU_IRQ)); } /* Private memory region and Internal GIC */ - dev = qdev_new(TYPE_A9MPCORE_PRIV); - qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); + qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-cpu", EXYNOS4210_NCPUS); + busdev = SYS_BUS_DEVICE(&s->a9mpcore); + sysbus_realize(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n][0]); - } - for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) { - s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n); + sysbus_connect_irq(busdev, n, + qdev_get_gpio_in(DEVICE(&s->cpu_irq_orgate[n]), 0)); } /* Cache controller */ sysbus_create_simple("l2x0", EXYNOS4210_L2X0_BASE_ADDR, NULL); /* External GIC */ - dev = qdev_new("exynos4210.gic"); - qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); + qdev_prop_set_uint32(DEVICE(&s->ext_gic), "num-cpu", EXYNOS4210_NCPUS); + busdev = SYS_BUS_DEVICE(&s->ext_gic); + sysbus_realize(busdev, &error_fatal); /* Map CPU interface */ sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR); /* Map Distributer interface */ sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n][1]); - } - for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) { - s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n); + sysbus_connect_irq(busdev, n, + qdev_get_gpio_in(DEVICE(&s->cpu_irq_orgate[n]), 1)); } /* Internal Interrupt Combiner */ - dev = qdev_new("exynos4210.combiner"); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); + busdev = SYS_BUS_DEVICE(&s->int_combiner); + sysbus_realize(busdev, &error_fatal); for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { - sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]); + sysbus_connect_irq(busdev, n, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), n)); } - exynos4210_combiner_get_gpioin(&s->irqs, dev, 0); sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR); /* External Interrupt Combiner */ - dev = qdev_new("exynos4210.combiner"); - qdev_prop_set_uint32(dev, "external", 1); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); + qdev_prop_set_uint32(DEVICE(&s->ext_combiner), "external", 1); + busdev = SYS_BUS_DEVICE(&s->ext_combiner); + sysbus_realize(busdev, &error_fatal); for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { - sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]); + sysbus_connect_irq(busdev, n, qdev_get_gpio_in(DEVICE(&s->ext_gic), n)); } - exynos4210_combiner_get_gpioin(&s->irqs, dev, 1); sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR); /* Initialize board IRQs. */ - exynos4210_init_board_irqs(&s->irqs); + exynos4210_init_board_irqs(s); /*** Memory ***/ @@ -488,6 +811,23 @@ static void exynos4210_init(Object *obj) object_initialize_child(obj, name, orgate, TYPE_OR_IRQ); g_free(name); } + + for (i = 0; i < ARRAY_SIZE(s->cpu_irq_orgate); i++) { + g_autofree char *name = g_strdup_printf("cpu-irq-orgate%d", i); + object_initialize_child(obj, name, &s->cpu_irq_orgate[i], TYPE_OR_IRQ); + } + + for (i = 0; i < ARRAY_SIZE(s->splitter); i++) { + g_autofree char *name = g_strdup_printf("irq-splitter%d", i); + object_initialize_child(obj, name, &s->splitter[i], TYPE_SPLIT_IRQ); + } + + object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV); + object_initialize_child(obj, "ext-gic", &s->ext_gic, TYPE_EXYNOS4210_GIC); + object_initialize_child(obj, "int-combiner", &s->int_combiner, + TYPE_EXYNOS4210_COMBINER); + object_initialize_child(obj, "ext-combiner", &s->ext_combiner, + TYPE_EXYNOS4210_COMBINER); } static void exynos4210_class_init(ObjectClass *klass, void *data) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 721a8eb8be..2d8381339c 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -1,6 +1,5 @@ arm_ss = ss.source_set() arm_ss.add(files('boot.c'), fdt) -arm_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) @@ -48,7 +47,11 @@ arm_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-ver arm_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c')) arm_ss.add(when: 'CONFIG_FSL_IMX31', if_true: files('fsl-imx31.c', 'kzm.c')) arm_ss.add(when: 'CONFIG_FSL_IMX6', if_true: files('fsl-imx6.c')) -arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_soc.c', 'aspeed.c', 'aspeed_ast2600.c')) +arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( + 'aspeed_soc.c', + 'aspeed.c', + 'aspeed_ast2600.c', + 'aspeed_ast10x0.c')) arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) arm_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index f40e854dec..4017392bf5 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -32,7 +32,7 @@ * Application Note AN524: * https://developer.arm.com/documentation/dai0524/latest/ * Application Note AN547: - * https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/DAI0547B_SSE300_PLUS_U55_FPGA_for_mps3.pdf + * https://developer.arm.com/documentation/dai0547/latest/ * * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide * (ARM ECM0601256) for the details of some of the device layout: @@ -1078,6 +1078,10 @@ static void mps2tz_common_init(MachineState *machine) { "gpio1", make_unimp_dev, &mms->gpio[1], 0x41101000, 0x1000 }, { "gpio2", make_unimp_dev, &mms->gpio[2], 0x41102000, 0x1000 }, { "gpio3", make_unimp_dev, &mms->gpio[3], 0x41103000, 0x1000 }, + { /* port 4 USER AHB interface 0 */ }, + { /* port 5 USER AHB interface 1 */ }, + { /* port 6 USER AHB interface 2 */ }, + { /* port 7 USER AHB interface 3 */ }, { "eth-usb", make_eth_usb, NULL, 0x41400000, 0x200000, { 49 } }, }, }, diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index 0678a56156..6bc6f5d2fe 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -30,11 +30,25 @@ #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" -#define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7 -#define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff -#define QUANTA_GBS_POWER_ON_STRAPS 0x000017ff -#define KUDO_BMC_POWER_ON_STRAPS 0x00001fff -#define MORI_BMC_POWER_ON_STRAPS 0x00001fff +#define NPCM7XX_POWER_ON_STRAPS_DEFAULT ( \ + NPCM7XX_PWRON_STRAP_SPI0F18 | \ + NPCM7XX_PWRON_STRAP_SFAB | \ + NPCM7XX_PWRON_STRAP_BSPA | \ + NPCM7XX_PWRON_STRAP_FUP(FUP_NORM_UART2) | \ + NPCM7XX_PWRON_STRAP_SECEN | \ + NPCM7XX_PWRON_STRAP_HIZ | \ + NPCM7XX_PWRON_STRAP_ECC | \ + NPCM7XX_PWRON_STRAP_RESERVE1 | \ + NPCM7XX_PWRON_STRAP_J2EN | \ + NPCM7XX_PWRON_STRAP_CKFRQ(CKFRQ_DEFAULT)) + +#define NPCM750_EVB_POWER_ON_STRAPS ( \ + NPCM7XX_POWER_ON_STRAPS_DEFAULT & ~NPCM7XX_PWRON_STRAP_J2EN) +#define QUANTA_GSJ_POWER_ON_STRAPS NPCM7XX_POWER_ON_STRAPS_DEFAULT +#define QUANTA_GBS_POWER_ON_STRAPS ( \ + NPCM7XX_POWER_ON_STRAPS_DEFAULT & ~NPCM7XX_PWRON_STRAP_SFAB) +#define KUDO_BMC_POWER_ON_STRAPS NPCM7XX_POWER_ON_STRAPS_DEFAULT +#define MORI_BMC_POWER_ON_STRAPS NPCM7XX_POWER_ON_STRAPS_DEFAULT static const char npcm7xx_default_bootrom[] = "npcm7xx_bootrom.bin"; diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 7b424e94a5..d2dc8a8952 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -13,9 +13,11 @@ #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/arm/primecell.h" +#include "hw/core/split-irq.h" #include "hw/net/lan9118.h" #include "hw/net/smc91c111.h" #include "hw/pci/pci.h" +#include "hw/qdev-core.h" #include "net/net.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -53,6 +55,20 @@ static const int realview_board_id[] = { 0x76d }; +static void split_irq_from_named(DeviceState *src, const char* outname, + qemu_irq out1, qemu_irq out2) { + DeviceState *splitter = qdev_new(TYPE_SPLIT_IRQ); + + qdev_prop_set_uint32(splitter, "num-lines", 2); + + qdev_realize_and_unref(splitter, NULL, &error_fatal); + + qdev_connect_gpio_out(splitter, 0, out1); + qdev_connect_gpio_out(splitter, 1, out2); + qdev_connect_gpio_out_named(src, outname, 0, + qdev_get_gpio_in(splitter, 0)); +} + static void realview_init(MachineState *machine, enum realview_board_type board_type) { @@ -66,7 +82,6 @@ static void realview_init(MachineState *machine, DeviceState *dev, *sysctl, *gpio2, *pl041; SysBusDevice *busdev; qemu_irq pic[64]; - qemu_irq mmc_irq[2]; PCIBus *pci_bus = NULL; NICInfo *nd; DriveInfo *dinfo; @@ -229,14 +244,14 @@ static void realview_init(MachineState *machine, * and the PL061 has them the other way about. Also the card * detect line is inverted. */ - mmc_irq[0] = qemu_irq_split( - qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT), - qdev_get_gpio_in(gpio2, 1)); - mmc_irq[1] = qemu_irq_split( - qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN), - qemu_irq_invert(qdev_get_gpio_in(gpio2, 0))); - qdev_connect_gpio_out_named(dev, "card-read-only", 0, mmc_irq[0]); - qdev_connect_gpio_out_named(dev, "card-inserted", 0, mmc_irq[1]); + split_irq_from_named(dev, "card-read-only", + qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT), + qdev_get_gpio_in(gpio2, 1)); + + split_irq_from_named(dev, "card-inserted", + qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN), + qemu_irq_invert(qdev_get_gpio_in(gpio2, 0))); + dinfo = drive_get(IF_SD, 0, 0); if (dinfo) { DeviceState *card; diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index d1885ae3f2..bce161870f 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -56,6 +56,7 @@ REG32(IDR2, 0x8) REG32(IDR3, 0xc) FIELD(IDR3, HAD, 2, 1); FIELD(IDR3, RIL, 10, 1); + FIELD(IDR3, BBML, 11, 2); REG32(IDR4, 0x10) REG32(IDR5, 0x14) FIELD(IDR5, OAS, 0, 3); @@ -387,7 +388,6 @@ typedef struct SMMUEventInfo { SMMUEventType type; uint32_t sid; bool recorded; - bool record_trans_faults; bool inval_ste_allowed; union { struct { diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 674623aabe..daa80e9c7b 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -259,6 +259,7 @@ static void smmuv3_init_regs(SMMUv3State *s) s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 1); s->idr[3] = FIELD_DP32(s->idr[3], IDR3, HAD, 1); + s->idr[3] = FIELD_DP32(s->idr[3], IDR3, BBML, 2); /* 4K, 16K and 64K granule support */ s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1); @@ -527,7 +528,7 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz, tt->had); } - event->record_trans_faults = CD_R(cd); + cfg->record_faults = CD_R(cd); return 0; @@ -680,7 +681,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, tt = select_tt(cfg, addr); if (!tt) { - if (event.record_trans_faults) { + if (cfg->record_faults) { event.type = SMMU_EVT_F_TRANSLATION; event.u.f_translation.addr = addr; event.u.f_translation.rnw = flag & 0x1; @@ -696,7 +697,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, if (cached_entry) { if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { status = SMMU_TRANS_ERROR; - if (event.record_trans_faults) { + if (cfg->record_faults) { event.type = SMMU_EVT_F_PERMISSION; event.u.f_permission.addr = addr; event.u.f_permission.rnw = flag & 0x1; @@ -720,28 +721,28 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.u.f_walk_eabt.addr2 = ptw_info.addr; break; case SMMU_PTW_ERR_TRANSLATION: - if (event.record_trans_faults) { + if (cfg->record_faults) { event.type = SMMU_EVT_F_TRANSLATION; event.u.f_translation.addr = addr; event.u.f_translation.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_ADDR_SIZE: - if (event.record_trans_faults) { + if (cfg->record_faults) { event.type = SMMU_EVT_F_ADDR_SIZE; event.u.f_addr_size.addr = addr; event.u.f_addr_size.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_ACCESS: - if (event.record_trans_faults) { + if (cfg->record_faults) { event.type = SMMU_EVT_F_ACCESS; event.u.f_access.addr = addr; event.u.f_access.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_PERMISSION: - if (event.record_trans_faults) { + if (cfg->record_faults) { event.type = SMMU_EVT_F_PERMISSION; event.u.f_permission.addr = addr; event.u.f_permission.rnw = flag & 0x1; @@ -760,7 +761,7 @@ epilogue: qemu_mutex_unlock(&s->mutex); switch (status) { case SMMU_TRANS_SUCCESS: - entry.perm = flag; + entry.perm = cached_entry->entry.perm; entry.translated_addr = cached_entry->entry.translated_addr + (addr & cached_entry->entry.addr_mask); entry.addr_mask = cached_entry->entry.addr_mask; @@ -786,7 +787,7 @@ epilogue: break; case SMMU_TRANS_ERROR: qemu_log_mask(LOG_GUEST_ERROR, - "%s translation failed for iova=0x%"PRIx64"(%s)\n", + "%s translation failed for iova=0x%"PRIx64" (%s)\n", mr->parent_obj.name, addr, smmu_event_string(event.type)); smmuv3_record_event(s, &event); break; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index b6c8a5d609..12c673c917 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "hw/core/split-irq.h" #include "hw/sysbus.h" #include "hw/sd/sd.h" #include "hw/ssi/ssi.h" @@ -1160,6 +1161,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) DeviceState *ssddev; DriveInfo *dinfo; DeviceState *carddev; + DeviceState *gpio_d_splitter; BlockBackend *blk; /* @@ -1237,9 +1239,18 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) &error_fatal); ssddev = ssi_create_peripheral(bus, "ssd0323"); - gpio_out[GPIO_D][0] = qemu_irq_split( - qdev_get_gpio_in_named(sddev, SSI_GPIO_CS, 0), + + gpio_d_splitter = qdev_new(TYPE_SPLIT_IRQ); + qdev_prop_set_uint32(gpio_d_splitter, "num-lines", 2); + qdev_realize_and_unref(gpio_d_splitter, NULL, &error_fatal); + qdev_connect_gpio_out( + gpio_d_splitter, 0, + qdev_get_gpio_in_named(sddev, SSI_GPIO_CS, 0)); + qdev_connect_gpio_out( + gpio_d_splitter, 1, qdev_get_gpio_in_named(ssddev, SSI_GPIO_CS, 0)); + gpio_out[GPIO_D][0] = qdev_get_gpio_in(gpio_d_splitter, 0); + gpio_out[GPIO_C][7] = qdev_get_gpio_in(ssddev, 0); /* Make sure the select pin is high. */ diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 46bf7ceddf..f94278935f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -56,7 +56,7 @@ #include "qemu/module.h" #include "hw/pci-host/gpex.h" #include "hw/virtio/virtio-pci.h" -#include "hw/arm/sysbus-fdt.h" +#include "hw/core/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/qdev-properties.h" #include "hw/arm/fdt.h" @@ -522,7 +522,7 @@ static void fdt_add_gic_node(VirtMachineState *vms) qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2); qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2); qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0); - if (vms->gic_version == VIRT_GIC_VERSION_3) { + if (vms->gic_version != VIRT_GIC_VERSION_2) { int nb_redist_regions = virt_gicv3_redist_region_count(vms); qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", @@ -690,14 +690,32 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) /* We create a standalone GIC */ SysBusDevice *gicbusdev; const char *gictype; - int type = vms->gic_version, i; + int i; unsigned int smp_cpus = ms->smp.cpus; uint32_t nb_redist_regions = 0; + int revision; - gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); + if (vms->gic_version == VIRT_GIC_VERSION_2) { + gictype = gic_class_name(); + } else { + gictype = gicv3_class_name(); + } + switch (vms->gic_version) { + case VIRT_GIC_VERSION_2: + revision = 2; + break; + case VIRT_GIC_VERSION_3: + revision = 3; + break; + case VIRT_GIC_VERSION_4: + revision = 4; + break; + default: + g_assert_not_reached(); + } vms->gic = qdev_new(gictype); - qdev_prop_set_uint32(vms->gic, "revision", type); + qdev_prop_set_uint32(vms->gic, "revision", revision); qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); /* Note that the num-irq property counts both internal and external * interrupts; there are always 32 of the former (mandated by GIC spec). @@ -707,9 +725,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure); } - if (type == 3) { - uint32_t redist0_capacity = - vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + if (vms->gic_version != VIRT_GIC_VERSION_2) { + uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST); uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); nb_redist_regions = virt_gicv3_redist_region_count(vms); @@ -728,7 +745,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) if (nb_redist_regions == 2) { uint32_t redist1_capacity = - vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; + virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); qdev_prop_set_uint32(vms->gic, "redist-region-count[1]", MIN(smp_cpus - redist0_count, redist1_capacity)); @@ -742,7 +759,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) gicbusdev = SYS_BUS_DEVICE(vms->gic); sysbus_realize_and_unref(gicbusdev, &error_fatal); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); - if (type == 3) { + if (vms->gic_version != VIRT_GIC_VERSION_2) { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base); if (nb_redist_regions == 2) { sysbus_mmio_map(gicbusdev, 2, @@ -780,7 +797,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) ppibase + timer_irq[irq])); } - if (type == 3) { + if (vms->gic_version != VIRT_GIC_VERSION_2) { qemu_irq irq = qdev_get_gpio_in(vms->gic, ppibase + ARCH_GIC_MAINT_IRQ); qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", @@ -806,9 +823,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) fdt_add_gic_node(vms); - if (type == 3 && vms->its) { + if (vms->gic_version != VIRT_GIC_VERSION_2 && vms->its) { create_its(vms); - } else if (type == 2) { + } else if (vms->gic_version == VIRT_GIC_VERSION_2) { create_v2m(vms); } } @@ -1658,10 +1675,10 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx) * purposes are to make TCG consistent (with 64-bit KVM hosts) * and to improve SGI efficiency. */ - if (vms->gic_version == VIRT_GIC_VERSION_3) { - clustersz = GICV3_TARGETLIST_BITS; - } else { + if (vms->gic_version == VIRT_GIC_VERSION_2) { clustersz = GIC_TARGETLIST_BITS; + } else { + clustersz = GICV3_TARGETLIST_BITS; } } return arm_cpu_mp_affinity(idx, clustersz); @@ -1794,6 +1811,10 @@ static void finalize_gic_version(VirtMachineState *vms) error_report( "gic-version=3 is not supported with kernel-irqchip=off"); exit(1); + case VIRT_GIC_VERSION_4: + error_report( + "gic-version=4 is not supported with kernel-irqchip=off"); + exit(1); } } @@ -1831,6 +1852,9 @@ static void finalize_gic_version(VirtMachineState *vms) case VIRT_GIC_VERSION_2: case VIRT_GIC_VERSION_3: break; + case VIRT_GIC_VERSION_4: + error_report("gic-version=4 is not supported with KVM"); + exit(1); } /* Check chosen version is effectively supported by the host */ @@ -1852,11 +1876,27 @@ static void finalize_gic_version(VirtMachineState *vms) vms->gic_version = VIRT_GIC_VERSION_2; break; case VIRT_GIC_VERSION_MAX: - vms->gic_version = VIRT_GIC_VERSION_3; + if (module_object_class_by_name("arm-gicv3")) { + /* CONFIG_ARM_GICV3_TCG was set */ + if (vms->virt) { + /* GICv4 only makes sense if CPU has EL2 */ + vms->gic_version = VIRT_GIC_VERSION_4; + } else { + vms->gic_version = VIRT_GIC_VERSION_3; + } + } else { + vms->gic_version = VIRT_GIC_VERSION_2; + } break; case VIRT_GIC_VERSION_HOST: error_report("gic-version=host requires KVM"); exit(1); + case VIRT_GIC_VERSION_4: + if (!vms->virt) { + error_report("gic-version=4 requires virtualization enabled"); + exit(1); + } + break; case VIRT_GIC_VERSION_2: case VIRT_GIC_VERSION_3: break; @@ -2024,16 +2064,16 @@ static void machvirt_init(MachineState *machine) vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC; } - /* The maximum number of CPUs depends on the GIC version, or on how - * many redistributors we can fit into the memory map. + /* + * The maximum number of CPUs depends on the GIC version, or on how + * many redistributors we can fit into the memory map (which in turn + * depends on whether this is a GICv3 or v4). */ - if (vms->gic_version == VIRT_GIC_VERSION_3) { - virt_max_cpus = - vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; - virt_max_cpus += - vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; - } else { + if (vms->gic_version == VIRT_GIC_VERSION_2) { virt_max_cpus = GIC_NCPU; + } else { + virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST) + + virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); } if (max_cpus > virt_max_cpus) { @@ -2043,6 +2083,13 @@ static void machvirt_init(MachineState *machine) exit(1); } + if (vms->secure && (kvm_enabled() || hvf_enabled())) { + error_report("mach-virt: %s does not support providing " + "Security extensions (TrustZone) to the guest CPU", + kvm_enabled() ? "KVM" : "HVF"); + exit(1); + } + if (vms->virt && (kvm_enabled() || hvf_enabled())) { error_report("mach-virt: %s does not support providing " "Virtualization extensions to the guest CPU", @@ -2102,6 +2149,10 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, "pmu", false, NULL); } + if (vmc->no_tcg_lpa2 && object_property_find(cpuobj, "lpa2")) { + object_property_set_bool(cpuobj, "lpa2", false, NULL); + } + if (object_property_find(cpuobj, "reset-cbar")) { object_property_set_int(cpuobj, "reset-cbar", vms->memmap[VIRT_CPUPERIPHS].base, @@ -2410,8 +2461,19 @@ static void virt_set_mte(Object *obj, bool value, Error **errp) static char *virt_get_gic_version(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - const char *val = vms->gic_version == VIRT_GIC_VERSION_3 ? "3" : "2"; + const char *val; + switch (vms->gic_version) { + case VIRT_GIC_VERSION_4: + val = "4"; + break; + case VIRT_GIC_VERSION_3: + val = "3"; + break; + default: + val = "2"; + break; + } return g_strdup(val); } @@ -2419,7 +2481,9 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - if (!strcmp(value, "3")) { + if (!strcmp(value, "4")) { + vms->gic_version = VIRT_GIC_VERSION_4; + } else if (!strcmp(value, "3")) { vms->gic_version = VIRT_GIC_VERSION_3; } else if (!strcmp(value, "2")) { vms->gic_version = VIRT_GIC_VERSION_2; @@ -2877,7 +2941,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) virt_set_gic_version); object_class_property_set_description(oc, "gic-version", "Set GIC version. " - "Valid values are 2, 3, host and max"); + "Valid values are 2, 3, 4, host and max"); object_class_property_add_str(oc, "iommu", virt_get_iommu, virt_set_iommu); object_class_property_set_description(oc, "iommu", @@ -3013,15 +3077,25 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_7_0_options(MachineClass *mc) +static void virt_machine_7_1_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(7, 0) +DEFINE_VIRT_MACHINE_AS_LATEST(7, 1) + +static void virt_machine_7_0_options(MachineClass *mc) +{ + virt_machine_7_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); +} +DEFINE_VIRT_MACHINE(7, 0) static void virt_machine_6_2_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_7_0_options(mc); compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); + vmc->no_tcg_lpa2 = true; } DEFINE_VIRT_MACHINE(6, 2) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 7c7baff8b7..37fc9b919c 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -15,7 +15,6 @@ #include "sysemu/device_tree.h" #include "hw/boards.h" #include "hw/sysbus.h" -#include "hw/arm/sysbus-fdt.h" #include "hw/arm/fdt.h" #include "cpu.h" #include "hw/qdev-properties.h" @@ -721,9 +720,9 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Xilinx Versal Virtual development board"; mc->init = versal_virt_init; - mc->min_cpus = XLNX_VERSAL_NR_ACPUS; - mc->max_cpus = XLNX_VERSAL_NR_ACPUS; - mc->default_cpus = XLNX_VERSAL_NR_ACPUS; + mc->min_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; + mc->max_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; + mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->no_cdrom = true; mc->default_ram_id = "ddr"; } diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 2551dfc22d..57276e1506 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -25,6 +25,7 @@ #include "hw/sysbus.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") +#define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") #define GEM_REVISION 0x40070106 #define VERSAL_NUM_PMC_APB_IRQS 3 @@ -34,10 +35,15 @@ static void versal_create_apu_cpus(Versal *s) { int i; + object_initialize_child(OBJECT(s), "apu-cluster", &s->fpd.apu.cluster, + TYPE_CPU_CLUSTER); + qdev_prop_set_uint32(DEVICE(&s->fpd.apu.cluster), "cluster-id", 0); + for (i = 0; i < ARRAY_SIZE(s->fpd.apu.cpu); i++) { Object *obj; - object_initialize_child(OBJECT(s), "apu-cpu[*]", &s->fpd.apu.cpu[i], + object_initialize_child(OBJECT(&s->fpd.apu.cluster), + "apu-cpu[*]", &s->fpd.apu.cpu[i], XLNX_VERSAL_ACPU_TYPE); obj = OBJECT(&s->fpd.apu.cpu[i]); if (i) { @@ -52,6 +58,8 @@ static void versal_create_apu_cpus(Versal *s) &error_abort); qdev_realize(DEVICE(obj), NULL, &error_fatal); } + + qdev_realize(DEVICE(&s->fpd.apu.cluster), NULL, &error_fatal); } static void versal_create_apu_gic(Versal *s, qemu_irq *pic) @@ -123,6 +131,35 @@ static void versal_create_apu_gic(Versal *s, qemu_irq *pic) } } +static void versal_create_rpu_cpus(Versal *s) +{ + int i; + + object_initialize_child(OBJECT(s), "rpu-cluster", &s->lpd.rpu.cluster, + TYPE_CPU_CLUSTER); + qdev_prop_set_uint32(DEVICE(&s->lpd.rpu.cluster), "cluster-id", 1); + + for (i = 0; i < ARRAY_SIZE(s->lpd.rpu.cpu); i++) { + Object *obj; + + object_initialize_child(OBJECT(&s->lpd.rpu.cluster), + "rpu-cpu[*]", &s->lpd.rpu.cpu[i], + XLNX_VERSAL_RCPU_TYPE); + obj = OBJECT(&s->lpd.rpu.cpu[i]); + object_property_set_bool(obj, "start-powered-off", true, + &error_abort); + + object_property_set_int(obj, "mp-affinity", 0x100 | i, &error_abort); + object_property_set_int(obj, "core-count", ARRAY_SIZE(s->lpd.rpu.cpu), + &error_abort); + object_property_set_link(obj, "memory", OBJECT(&s->lpd.rpu.mr), + &error_abort); + qdev_realize(DEVICE(obj), NULL, &error_fatal); + } + + qdev_realize(DEVICE(&s->lpd.rpu.cluster), NULL, &error_fatal); +} + static void versal_create_uarts(Versal *s, qemu_irq *pic) { int i; @@ -502,6 +539,57 @@ static void versal_create_ospi(Versal *s, qemu_irq *pic) qdev_connect_gpio_out(orgate, 0, pic[VERSAL_OSPI_IRQ]); } +static void versal_create_crl(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + int i; + + object_initialize_child(OBJECT(s), "crl", &s->lpd.crl, + TYPE_XLNX_VERSAL_CRL); + sbd = SYS_BUS_DEVICE(&s->lpd.crl); + + for (i = 0; i < ARRAY_SIZE(s->lpd.rpu.cpu); i++) { + g_autofree gchar *name = g_strdup_printf("cpu_r5[%d]", i); + + object_property_set_link(OBJECT(&s->lpd.crl), + name, OBJECT(&s->lpd.rpu.cpu[i]), + &error_abort); + } + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) { + g_autofree gchar *name = g_strdup_printf("gem[%d]", i); + + object_property_set_link(OBJECT(&s->lpd.crl), + name, OBJECT(&s->lpd.iou.gem[i]), + &error_abort); + } + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.adma); i++) { + g_autofree gchar *name = g_strdup_printf("adma[%d]", i); + + object_property_set_link(OBJECT(&s->lpd.crl), + name, OBJECT(&s->lpd.iou.adma[i]), + &error_abort); + } + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) { + g_autofree gchar *name = g_strdup_printf("uart[%d]", i); + + object_property_set_link(OBJECT(&s->lpd.crl), + name, OBJECT(&s->lpd.iou.uart[i]), + &error_abort); + } + + object_property_set_link(OBJECT(&s->lpd.crl), + "usb", OBJECT(&s->lpd.iou.usb), + &error_abort); + + sysbus_realize(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, MM_CRL, + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, pic[VERSAL_CRL_IRQ]); +} + /* This takes the board allocated linear DDR memory and creates aliases * for each split DDR range/aperture on the Versal address map. */ @@ -585,8 +673,6 @@ static void versal_unimp(Versal *s) versal_unimp_area(s, "psm", &s->mr_ps, MM_PSM_START, MM_PSM_END - MM_PSM_START); - versal_unimp_area(s, "crl", &s->mr_ps, - MM_CRL, MM_CRL_SIZE); versal_unimp_area(s, "crf", &s->mr_ps, MM_FPD_CRF, MM_FPD_CRF_SIZE); versal_unimp_area(s, "apu", &s->mr_ps, @@ -631,6 +717,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_apu_cpus(s); versal_create_apu_gic(s, pic); + versal_create_rpu_cpus(s); versal_create_uarts(s, pic); versal_create_usbs(s, pic); versal_create_gems(s, pic); @@ -643,6 +730,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_efuse(s, pic); versal_create_pmc_iou_slcr(s, pic); versal_create_ospi(s, pic); + versal_create_crl(s, pic); versal_map_ddr(s); versal_unimp(s); @@ -652,6 +740,8 @@ static void versal_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, 0); memory_region_add_subregion_overlap(&s->fpd.apu.mr, 0, &s->mr_ps, 0); + memory_region_add_subregion_overlap(&s->lpd.rpu.mr, 0, + &s->lpd.rpu.mr_ps_alias, 0); } static void versal_init(Object *obj) @@ -659,7 +749,10 @@ static void versal_init(Object *obj) Versal *s = XLNX_VERSAL(obj); memory_region_init(&s->fpd.apu.mr, obj, "mr-apu", UINT64_MAX); + memory_region_init(&s->lpd.rpu.mr, obj, "mr-rpu", UINT64_MAX); memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); + memory_region_init_alias(&s->lpd.rpu.mr_ps_alias, OBJECT(s), + "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX); } static Property versal_properties[] = { diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 6d0e4116db..375309e68e 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -52,6 +52,13 @@ #define QSPI_DMA_ADDR 0xff0f0800 #define NUM_QSPI_IRQ_LINES 2 +#define CRF_ADDR 0xfd1a0000 +#define CRF_IRQ 120 + +/* Serializer/Deserializer. */ +#define SERDES_ADDR 0xfd400000 +#define SERDES_SIZE 0x20000 + #define DP_ADDR 0xfd4a0000 #define DP_IRQ 113 @@ -59,7 +66,10 @@ #define DPDMA_IRQ 116 #define APU_ADDR 0xfd5c0000 -#define APU_SIZE 0x100 +#define APU_IRQ 153 + +#define TTC0_ADDR 0xFF110000 +#define TTC0_IRQ 36 #define IPI_ADDR 0xFF300000 #define IPI_IRQ 64 @@ -276,6 +286,57 @@ static void xlnx_zynqmp_create_efuse(XlnxZynqMPState *s, qemu_irq *gic) sysbus_connect_irq(sbd, 0, gic[EFUSE_IRQ]); } +static void xlnx_zynqmp_create_apu_ctrl(XlnxZynqMPState *s, qemu_irq *gic) +{ + SysBusDevice *sbd; + int i; + + object_initialize_child(OBJECT(s), "apu-ctrl", &s->apu_ctrl, + TYPE_XLNX_ZYNQMP_APU_CTRL); + sbd = SYS_BUS_DEVICE(&s->apu_ctrl); + + for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) { + g_autofree gchar *name = g_strdup_printf("cpu%d", i); + + object_property_set_link(OBJECT(&s->apu_ctrl), name, + OBJECT(&s->apu_cpu[i]), &error_abort); + } + + sysbus_realize(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, APU_ADDR); + sysbus_connect_irq(sbd, 0, gic[APU_IRQ]); +} + +static void xlnx_zynqmp_create_crf(XlnxZynqMPState *s, qemu_irq *gic) +{ + SysBusDevice *sbd; + + object_initialize_child(OBJECT(s), "crf", &s->crf, TYPE_XLNX_ZYNQMP_CRF); + sbd = SYS_BUS_DEVICE(&s->crf); + + sysbus_realize(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, CRF_ADDR); + sysbus_connect_irq(sbd, 0, gic[CRF_IRQ]); +} + +static void xlnx_zynqmp_create_ttc(XlnxZynqMPState *s, qemu_irq *gic) +{ + SysBusDevice *sbd; + int i, irq; + + for (i = 0; i < XLNX_ZYNQMP_NUM_TTC; i++) { + object_initialize_child(OBJECT(s), "ttc[*]", &s->ttc[i], + TYPE_CADENCE_TTC); + sbd = SYS_BUS_DEVICE(&s->ttc[i]); + + sysbus_realize(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, TTC0_ADDR + i * 0x10000); + for (irq = 0; irq < 3; irq++) { + sysbus_connect_irq(sbd, irq, gic[TTC0_IRQ + i * 3 + irq]); + } + } +} + static void xlnx_zynqmp_create_unimp_mmio(XlnxZynqMPState *s) { static const struct UnimpInfo { @@ -283,7 +344,7 @@ static void xlnx_zynqmp_create_unimp_mmio(XlnxZynqMPState *s) hwaddr base; hwaddr size; } unimp_areas[ARRAY_SIZE(s->mr_unimp)] = { - { .name = "apu", APU_ADDR, APU_SIZE }, + { .name = "serdes", SERDES_ADDR, SERDES_SIZE }, }; unsigned int nr; @@ -679,6 +740,9 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) xlnx_zynqmp_create_bbram(s, gic_spi); xlnx_zynqmp_create_efuse(s, gic_spi); + xlnx_zynqmp_create_apu_ctrl(s, gic_spi); + xlnx_zynqmp_create_crf(s, gic_spi); + xlnx_zynqmp_create_ttc(s, gic_spi); xlnx_zynqmp_create_unimp_mmio(s); for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 7d60ce6f0e..0723e39430 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -677,7 +677,7 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp) return; } - isa_init_irq(d, &s->pic, s->irq); + s->pic = isa_get_irq(d, s->irq); k = ISADMA_GET_CLASS(s->isa_dma); k->register_channel(s->isa_dma, s->dma, cs_dma_read, s); diff --git a/hw/audio/gus.c b/hw/audio/gus.c index e8719ee117..42f010b671 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -282,7 +282,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) s->emu.himemaddr = s->himem; s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; s->emu.opaque = s; - isa_init_irq (d, &s->pic, s->emu.gusirq); + s->pic = isa_get_irq(d, s->emu.gusirq); AUD_set_active_out (s->voice, 1); } diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 5f8a878f20..bc77e3d8c9 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -345,11 +345,12 @@ static void intel_hda_corb_run(IntelHDAState *d) static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response) { - const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + const MemTxAttrs attrs = { .memory = true }; HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); IntelHDAState *d = container_of(bus, IntelHDAState, codecs); hwaddr addr; uint32_t wp, ex; + MemTxResult res = MEMTX_OK; if (d->ics & ICH6_IRS_BUSY) { dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n", @@ -368,8 +369,12 @@ static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t res ex = (solicited ? 0 : (1 << 4)) | dev->cad; wp = (d->rirb_wp + 1) & 0xff; addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase); - stl_le_pci_dma(&d->pci, addr + 8 * wp, response, attrs); - stl_le_pci_dma(&d->pci, addr + 8 * wp + 4, ex, attrs); + res |= stl_le_pci_dma(&d->pci, addr + 8 * wp, response, attrs); + res |= stl_le_pci_dma(&d->pci, addr + 8 * wp + 4, ex, attrs); + if (res != MEMTX_OK && (d->rirb_ctl & ICH6_RBCTL_OVERRUN_EN)) { + d->rirb_sts |= ICH6_RBSTS_OVERRUN; + intel_hda_update_irq(d); + } d->rirb_wp = wp; dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n", @@ -473,7 +478,7 @@ static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase); st->bentries = st->lvi +1; g_free(st->bpl); - st->bpl = g_malloc(sizeof(bpl) * st->bentries); + st->bpl = g_new(bpl, st->bentries); for (i = 0; i < st->bentries; i++, addr += 16) { pci_dma_read(&d->pci, addr, buf, 16); st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf); diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 60f1f75e3a..2215386ddb 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1408,7 +1408,7 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) return; } - isa_init_irq (isadev, &s->pic, s->irq); + s->pic = isa_get_irq(isadev, s->irq); s->mixer_regs[0x80] = magic_of_irq (s->irq); s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma); diff --git a/hw/avr/boot.c b/hw/avr/boot.c index cbede775ce..617f3a144c 100644 --- a/hw/avr/boot.c +++ b/hw/avr/boot.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "hw/loader.h" #include "elf.h" diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c index 860787580a..2785b9e849 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qemu/memalign.h" #include "qapi/error.h" #include "hw/xen/xen_common.h" #include "hw/block/xen_blkif.h" diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index ab663dce93..fa20450747 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -94,7 +94,7 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) isa->iobase, fdc_portio_list, fdctrl, "fdc"); - isa_init_irq(isadev, &fdctrl->irq, isa->irq); + fdctrl->irq = isa_get_irq(isadev, isa->irq); fdctrl->dma_chann = isa->dma; if (fdctrl->dma_chann != -1) { IsaDmaClass *k; diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 21d18ac2e3..347875a0cd 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -32,6 +32,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "qemu/memalign.h" #include "hw/irq.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index c6bf3c6bfa..7d3d8b12e0 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -340,6 +340,7 @@ static const FlashPartInfo known_devices[] = { { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) }, { INFO("w25q512jv", 0xef4020, 0, 64 << 10, 1024, ER_4K) }, + { INFO("w25q01jvq", 0xef4021, 0, 64 << 10, 2048, ER_4K) }, }; typedef enum { diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 81f9f971d8..74c7190302 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -1023,7 +1023,7 @@ static void postload_update_cb(void *opaque, bool running, RunState state) { PFlashCFI01 *pfl = opaque; - /* This is called after bdrv_invalidate_cache_all. */ + /* This is called after bdrv_activate_all. */ qemu_del_vm_change_state_handler(pfl->vmstate); pfl->vmstate = NULL; diff --git a/hw/char/escc.c b/hw/char/escc.c index 8755d8d34f..17a908c59b 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -828,7 +828,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } } - if (qcode > qemu_input_map_qcode_to_sun_len) { + if (qcode >= qemu_input_map_qcode_to_sun_len) { return; } diff --git a/hw/char/parallel.c b/hw/char/parallel.c index b45e67bfbb..f735a6cd7f 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -553,7 +553,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) index++; base = isa->iobase; - isa_init_irq(isadev, &s->irq, isa->isairq); + s->irq = isa_get_irq(isadev, isa->isairq); qemu_register_reset(parallel_reset, s); qemu_chr_fe_set_handlers(&s->chr, parallel_can_receive, NULL, @@ -622,7 +622,7 @@ bool parallel_mm_init(MemoryRegion *address_space, { ParallelState *s; - s = g_malloc0(sizeof(ParallelState)); + s = g_new0(ParallelState, 1); s->irq = irq; qemu_chr_fe_init(&s->chr, chr, &error_abort); s->it_shift = it_shift; diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index 729edbf968..6577f0e640 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -248,7 +248,7 @@ HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, tohost_offset = tohost_addr - base; fromhost_offset = fromhost_addr - base; - HTIFState *s = g_malloc0(sizeof(HTIFState)); + HTIFState *s = g_new0(HTIFState, 1); s->address_space = address_space; s->main_mem = main_mem; s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem); diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 1b8b303079..7a7ed239cd 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -75,7 +75,7 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp) } index++; - isa_init_irq(isadev, &s->irq, isa->isairq); + s->irq = isa_get_irq(isadev, isa->isairq); qdev_realize(DEVICE(s), NULL, errp); qdev_set_legacy_instance_id(dev, isa->iobase, 3); diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index f01ec2137c..6048d408b8 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -1055,10 +1055,8 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) QTAILQ_INIT(&vser->ports); vser->bus.max_nr_ports = vser->serial.max_virtserial_ports; - vser->ivqs = g_malloc(vser->serial.max_virtserial_ports - * sizeof(VirtQueue *)); - vser->ovqs = g_malloc(vser->serial.max_virtserial_ports - * sizeof(VirtQueue *)); + vser->ivqs = g_new(VirtQueue *, vser->serial.max_virtserial_ports); + vser->ovqs = g_new(VirtQueue *, vser->serial.max_virtserial_ports); /* Add a queue for host to guest transfers for port 0 (backward compat) */ vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); diff --git a/hw/core/clock-vmstate.c b/hw/core/clock-vmstate.c index 9d9174ffbd..7eccb6d4ea 100644 --- a/hw/core/clock-vmstate.c +++ b/hw/core/clock-vmstate.c @@ -44,6 +44,7 @@ const VMStateDescription vmstate_muldiv = { .fields = (VMStateField[]) { VMSTATE_UINT32(multiplier, Clock), VMSTATE_UINT32(divider, Clock), + VMSTATE_END_OF_LIST() }, }; diff --git a/hw/core/irq.c b/hw/core/irq.c index 8a9cbdd556..3623f711fe 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -106,21 +106,6 @@ qemu_irq qemu_irq_invert(qemu_irq irq) return qemu_allocate_irq(qemu_notirq, irq, 0); } -static void qemu_splitirq(void *opaque, int line, int level) -{ - struct IRQState **irq = opaque; - irq[0]->handler(irq[0]->opaque, irq[0]->n, level); - irq[1]->handler(irq[1]->opaque, irq[1]->n, level); -} - -qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2) -{ - qemu_irq *s = g_malloc0(2 * sizeof(qemu_irq)); - s[0] = irq1; - s[1] = irq2; - return qemu_allocate_irq(qemu_splitirq, s, 0); -} - void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n) { int i; diff --git a/hw/core/loader.c b/hw/core/loader.c index 19edb928e9..8167301f04 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -43,7 +43,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" @@ -474,7 +473,7 @@ ssize_t load_elf_ram_sym(const char *filename, ret = ELF_LOAD_NOT_ELF; goto fail; } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN data_order = ELFDATA2MSB; #else data_order = ELFDATA2LSB; @@ -511,7 +510,7 @@ ssize_t load_elf_ram_sym(const char *filename, static void bswap_uboot_header(uboot_image_header_t *hdr) { -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN bswap32s(&hdr->ih_magic); bswap32s(&hdr->ih_hcrc); bswap32s(&hdr->ih_time); @@ -1333,6 +1332,92 @@ static Rom *find_rom(hwaddr addr, size_t size) return NULL; } +typedef struct RomSec { + hwaddr base; + int se; /* start/end flag */ +} RomSec; + + +/* + * Sort into address order. We break ties between rom-startpoints + * and rom-endpoints in favour of the startpoint, by sorting the 0->1 + * transition before the 1->0 transition. Either way round would + * work, but this way saves a little work later by avoiding + * dealing with "gaps" of 0 length. + */ +static gint sort_secs(gconstpointer a, gconstpointer b) +{ + RomSec *ra = (RomSec *) a; + RomSec *rb = (RomSec *) b; + + if (ra->base == rb->base) { + return ra->se - rb->se; + } + return ra->base > rb->base ? 1 : -1; +} + +static GList *add_romsec_to_list(GList *secs, hwaddr base, int se) +{ + RomSec *cand = g_new(RomSec, 1); + cand->base = base; + cand->se = se; + return g_list_prepend(secs, cand); +} + +RomGap rom_find_largest_gap_between(hwaddr base, size_t size) +{ + Rom *rom; + RomSec *cand; + RomGap res = {0, 0}; + hwaddr gapstart = base; + GList *it, *secs = NULL; + int count = 0; + + QTAILQ_FOREACH(rom, &roms, next) { + /* Ignore blobs being loaded to special places */ + if (rom->mr || rom->fw_file) { + continue; + } + /* ignore anything finishing bellow base */ + if (rom->addr + rom->romsize <= base) { + continue; + } + /* ignore anything starting above the region */ + if (rom->addr >= base + size) { + continue; + } + + /* Save the start and end of each relevant ROM */ + secs = add_romsec_to_list(secs, rom->addr, 1); + + if (rom->addr + rom->romsize < base + size) { + secs = add_romsec_to_list(secs, rom->addr + rom->romsize, -1); + } + } + + /* sentinel */ + secs = add_romsec_to_list(secs, base + size, 1); + + secs = g_list_sort(secs, sort_secs); + + for (it = g_list_first(secs); it; it = g_list_next(it)) { + cand = (RomSec *) it->data; + if (count == 0 && count + cand->se == 1) { + size_t gap = cand->base - gapstart; + if (gap > res.size) { + res.base = gapstart; + res.size = gap; + } + } else if (count == 1 && count + cand->se == 0) { + gapstart = cand->base; + } + count += cand->se; + } + + g_list_free_full(secs, g_free); + return res; +} + /* * Copies memory from registered ROMs to dest. Any memory that is contained in * a ROM between addr and addr + size is copied. Note that this can involve diff --git a/hw/core/machine.c b/hw/core/machine.c index d856485cb4..cb9bbc844d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,7 +37,12 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-pci.h" -GlobalProperty hw_compat_6_2[] = {}; +GlobalProperty hw_compat_7_0[] = {}; +const size_t hw_compat_7_0_len = G_N_ELEMENTS(hw_compat_7_0); + +GlobalProperty hw_compat_6_2[] = { + { "PIIX4_PM", "x-not-migrate-acpi-index", "on"}, +}; const size_t hw_compat_6_2_len = G_N_ELEMENTS(hw_compat_6_2); GlobalProperty hw_compat_6_1[] = { diff --git a/hw/core/meson.build b/hw/core/meson.build index 0f884d6fd4..7a4d02b6c0 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -33,6 +33,7 @@ softmmu_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) softmmu_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) softmmu_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) +softmmu_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) softmmu_ss.add(files( 'cpu-sysemu.c', diff --git a/hw/core/reset.c b/hw/core/reset.c index 9c477f2bf5..36be82c491 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -40,7 +40,7 @@ static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers = void qemu_register_reset(QEMUResetHandler *func, void *opaque) { - QEMUResetEntry *re = g_malloc0(sizeof(QEMUResetEntry)); + QEMUResetEntry *re = g_new0(QEMUResetEntry, 1); re->func = func; re->opaque = opaque; diff --git a/hw/arm/sysbus-fdt.c b/hw/core/sysbus-fdt.c similarity index 99% rename from hw/arm/sysbus-fdt.c rename to hw/core/sysbus-fdt.c index 48c5fe9bf1..19d22cbe73 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -27,7 +27,7 @@ #ifdef CONFIG_LINUX #include #endif -#include "hw/arm/sysbus-fdt.h" +#include "hw/core/sysbus-fdt.h" #include "qemu/error-report.h" #include "sysemu/device_tree.h" #include "sysemu/tpm.h" diff --git a/hw/display/artist.c b/hw/display/artist.c index 1d877998b9..8e121bb0b4 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -7,7 +7,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" @@ -26,7 +25,7 @@ #define TYPE_ARTIST "artist" OBJECT_DECLARE_SIMPLE_TYPE(ARTISTState, ARTIST) -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define ROP8OFF(_i) (3 - (_i)) #else #define ROP8OFF @@ -712,7 +711,7 @@ static void combine_write_reg(hwaddr addr, uint64_t val, int size, void *out) * FIXME: is there a qemu helper for this? */ -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN addr ^= 3; #endif @@ -1087,7 +1086,7 @@ static uint64_t combine_read_reg(hwaddr addr, int size, void *in) * FIXME: is there a qemu helper for this? */ -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN addr ^= 3; #endif diff --git a/hw/display/ati.c b/hw/display/ati.c index 31f22754dc..6e38e00502 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -955,7 +955,9 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp) } /* init vga bits */ - vga_common_init(vga, OBJECT(s)); + if (!vga_common_init(vga, OBJECT(s), errp)) { + return; + } vga_init(vga, OBJECT(s), pci_address_space(dev), pci_address_space_io(dev), true); vga->con = graphic_console_init(DEVICE(s), 0, s->vga.hw_ops, &s->vga); diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 4b7e78d919..2e9656ae1c 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" #include "qemu/error-report.h" diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index fdca6ca659..3bb6a58698 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -2940,27 +2940,30 @@ void cirrus_init_common(CirrusVGAState *s, Object *owner, static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) { - PCICirrusVGAState *d = PCI_CIRRUS_VGA(dev); - CirrusVGAState *s = &d->cirrus_vga; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - int16_t device_id = pc->device_id; + PCICirrusVGAState *d = PCI_CIRRUS_VGA(dev); + CirrusVGAState *s = &d->cirrus_vga; + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + int16_t device_id = pc->device_id; - /* follow real hardware, cirrus card emulated has 4 MB video memory. - Also accept 8 MB/16 MB for backward compatibility. */ - if (s->vga.vram_size_mb != 4 && s->vga.vram_size_mb != 8 && - s->vga.vram_size_mb != 16) { - error_setg(errp, "Invalid cirrus_vga ram size '%u'", - s->vga.vram_size_mb); - return; - } - /* setup VGA */ - vga_common_init(&s->vga, OBJECT(dev)); - cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), - pci_address_space_io(dev)); - s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); - - /* setup PCI */ + /* + * Follow real hardware, cirrus card emulated has 4 MB video memory. + * Also accept 8 MB/16 MB for backward compatibility. + */ + if (s->vga.vram_size_mb != 4 && s->vga.vram_size_mb != 8 && + s->vga.vram_size_mb != 16) { + error_setg(errp, "Invalid cirrus_vga ram size '%u'", + s->vga.vram_size_mb); + return; + } + /* setup VGA */ + if (!vga_common_init(&s->vga, OBJECT(dev), errp)) { + return; + } + cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), + pci_address_space_io(dev)); + s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); + /* setup PCI */ memory_region_init(&s->pci_bar, OBJECT(dev), "cirrus-pci-bar0", 0x2000000); /* XXX: add byte swapping apertures */ @@ -2968,14 +2971,14 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) memory_region_add_subregion(&s->pci_bar, 0x1000000, &s->cirrus_linear_bitblt_io); - /* setup memory space */ - /* memory #0 LFB */ - /* memory #1 memory-mapped I/O */ - /* XXX: s->vga.vram_size must be a power of two */ - pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->pci_bar); - if (device_id == CIRRUS_ID_CLGD5446) { - pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io); - } + /* setup memory space */ + /* memory #0 LFB */ + /* memory #1 memory-mapped I/O */ + /* XXX: s->vga.vram_size must be a power of two */ + pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->pci_bar); + if (device_id == CIRRUS_ID_CLGD5446) { + pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io); + } } static Property pci_vga_cirrus_properties[] = { diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c index 4f6fb1af3b..96144bd690 100644 --- a/hw/display/cirrus_vga_isa.c +++ b/hw/display/cirrus_vga_isa.c @@ -56,7 +56,9 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) return; } s->global_vmstate = true; - vga_common_init(s, OBJECT(dev)); + if (!vga_common_init(s, OBJECT(dev), errp)) { + return; + } cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, isa_address_space(isadev), isa_address_space_io(isadev)); diff --git a/hw/display/edid-generate.c b/hw/display/edid-generate.c index bccf32af69..2cb819675e 100644 --- a/hw/display/edid-generate.c +++ b/hw/display/edid-generate.c @@ -255,33 +255,31 @@ static void edid_desc_dummy(uint8_t *desc) edid_desc_type(desc, 0x10); } -static void edid_desc_timing(uint8_t *desc, uint32_t refresh_rate, +static void edid_desc_timing(uint8_t *desc, const Timings *timings, uint32_t xres, uint32_t yres, uint32_t xmm, uint32_t ymm) { - Timings timings; - generate_timings(&timings, refresh_rate, xres, yres); - stl_le_p(desc, timings.clock); + stw_le_p(desc, timings->clock); desc[2] = xres & 0xff; - desc[3] = timings.xblank & 0xff; + desc[3] = timings->xblank & 0xff; desc[4] = (((xres & 0xf00) >> 4) | - ((timings.xblank & 0xf00) >> 8)); + ((timings->xblank & 0xf00) >> 8)); desc[5] = yres & 0xff; - desc[6] = timings.yblank & 0xff; + desc[6] = timings->yblank & 0xff; desc[7] = (((yres & 0xf00) >> 4) | - ((timings.yblank & 0xf00) >> 8)); + ((timings->yblank & 0xf00) >> 8)); - desc[8] = timings.xfront & 0xff; - desc[9] = timings.xsync & 0xff; + desc[8] = timings->xfront & 0xff; + desc[9] = timings->xsync & 0xff; - desc[10] = (((timings.yfront & 0x00f) << 4) | - ((timings.ysync & 0x00f) << 0)); - desc[11] = (((timings.xfront & 0x300) >> 2) | - ((timings.xsync & 0x300) >> 4) | - ((timings.yfront & 0x030) >> 2) | - ((timings.ysync & 0x030) >> 4)); + desc[10] = (((timings->yfront & 0x00f) << 4) | + ((timings->ysync & 0x00f) << 0)); + desc[11] = (((timings->xfront & 0x300) >> 2) | + ((timings->xsync & 0x300) >> 4) | + ((timings->yfront & 0x030) >> 2) | + ((timings->ysync & 0x030) >> 4)); desc[12] = xmm & 0xff; desc[13] = ymm & 0xff; @@ -348,13 +346,10 @@ static void init_displayid(uint8_t *did) edid_checksum(did + 1, did[2] + 4); } -static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate, +static void qemu_displayid_generate(uint8_t *did, const Timings *timings, uint32_t xres, uint32_t yres, uint32_t xmm, uint32_t ymm) { - Timings timings; - generate_timings(&timings, refresh_rate, xres, yres); - did[0] = 0x70; /* display id extension */ did[1] = 0x13; /* version 1.3 */ did[2] = 23; /* length */ @@ -364,21 +359,21 @@ static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate, did[6] = 0x00; /* revision */ did[7] = 0x14; /* block length */ - did[8] = timings.clock & 0xff; - did[9] = (timings.clock & 0xff00) >> 8; - did[10] = (timings.clock & 0xff0000) >> 16; + did[8] = timings->clock & 0xff; + did[9] = (timings->clock & 0xff00) >> 8; + did[10] = (timings->clock & 0xff0000) >> 16; did[11] = 0x88; /* leave aspect ratio undefined */ stw_le_p(did + 12, 0xffff & (xres - 1)); - stw_le_p(did + 14, 0xffff & (timings.xblank - 1)); - stw_le_p(did + 16, 0xffff & (timings.xfront - 1)); - stw_le_p(did + 18, 0xffff & (timings.xsync - 1)); + stw_le_p(did + 14, 0xffff & (timings->xblank - 1)); + stw_le_p(did + 16, 0xffff & (timings->xfront - 1)); + stw_le_p(did + 18, 0xffff & (timings->xsync - 1)); stw_le_p(did + 20, 0xffff & (yres - 1)); - stw_le_p(did + 22, 0xffff & (timings.yblank - 1)); - stw_le_p(did + 24, 0xffff & (timings.yfront - 1)); - stw_le_p(did + 26, 0xffff & (timings.ysync - 1)); + stw_le_p(did + 22, 0xffff & (timings->yblank - 1)); + stw_le_p(did + 24, 0xffff & (timings->yfront - 1)); + stw_le_p(did + 26, 0xffff & (timings->ysync - 1)); edid_checksum(did + 1, did[2] + 4); } @@ -386,6 +381,7 @@ static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate, void qemu_edid_generate(uint8_t *edid, size_t size, qemu_edid_info *info) { + Timings timings; uint8_t *desc = edid + 54; uint8_t *xtra3 = NULL; uint8_t *dta = NULL; @@ -409,9 +405,6 @@ void qemu_edid_generate(uint8_t *edid, size_t size, if (!info->prefy) { info->prefy = 800; } - if (info->prefx >= 4096 || info->prefy >= 4096) { - large_screen = 1; - } if (info->width_mm && info->height_mm) { width_mm = info->width_mm; height_mm = info->height_mm; @@ -421,6 +414,11 @@ void qemu_edid_generate(uint8_t *edid, size_t size, height_mm = qemu_edid_dpi_to_mm(dpi, info->prefy); } + generate_timings(&timings, refresh_rate, info->prefx, info->prefy); + if (info->prefx >= 4096 || info->prefy >= 4096 || timings.clock >= 65536) { + large_screen = 1; + } + /* =============== extensions =============== */ if (size >= 256) { @@ -501,7 +499,7 @@ void qemu_edid_generate(uint8_t *edid, size_t size, if (!large_screen) { /* The DTD section has only 12 bits to store the resolution */ - edid_desc_timing(desc, refresh_rate, info->prefx, info->prefy, + edid_desc_timing(desc, &timings, info->prefx, info->prefy, width_mm, height_mm); desc = edid_desc_next(edid, dta, desc); } @@ -536,7 +534,7 @@ void qemu_edid_generate(uint8_t *edid, size_t size, /* =============== display id extensions =============== */ if (did && large_screen) { - qemu_displayid_generate(did, refresh_rate, info->prefx, info->prefy, + qemu_displayid_generate(did, &timings, info->prefx, info->prefy, width_mm, height_mm); } diff --git a/hw/display/macfb.c b/hw/display/macfb.c index c9b468c10e..2f8e016566 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -476,7 +476,8 @@ static void macfb_update_display(void *opaque) static void macfb_update_irq(MacfbState *s) { - uint32_t irq_state = s->irq_state & s->irq_mask; + uint32_t irq_state = s->regs[DAFB_INTR_STAT >> 2] & + s->regs[DAFB_INTR_MASK >> 2]; if (irq_state) { qemu_irq_raise(s->irq); @@ -496,7 +497,7 @@ static void macfb_vbl_timer(void *opaque) MacfbState *s = opaque; int64_t next_vbl; - s->irq_state |= DAFB_INTR_VBL; + s->regs[DAFB_INTR_STAT >> 2] |= DAFB_INTR_VBL; macfb_update_irq(s); /* 60 Hz irq */ @@ -530,14 +531,16 @@ static uint64_t macfb_ctrl_read(void *opaque, case DAFB_MODE_VADDR2: case DAFB_MODE_CTRL1: case DAFB_MODE_CTRL2: - val = s->regs[addr >> 2]; - break; case DAFB_INTR_STAT: - val = s->irq_state; + val = s->regs[addr >> 2]; break; case DAFB_MODE_SENSE: val = macfb_sense_read(s); break; + default: + if (addr < MACFB_CTRL_TOPADDR) { + val = s->regs[addr >> 2]; + } } trace_macfb_ctrl_read(addr, val, size); @@ -568,7 +571,7 @@ static void macfb_ctrl_write(void *opaque, macfb_sense_write(s, val); break; case DAFB_INTR_MASK: - s->irq_mask = val; + s->regs[addr >> 2] = val; if (val & DAFB_INTR_VBL) { next_vbl = macfb_next_vbl(); timer_mod(s->vbl_timer, next_vbl); @@ -577,12 +580,12 @@ static void macfb_ctrl_write(void *opaque, } break; case DAFB_INTR_CLEAR: - s->irq_state &= ~DAFB_INTR_VBL; + s->regs[DAFB_INTR_STAT >> 2] &= ~DAFB_INTR_VBL; macfb_update_irq(s); break; case DAFB_RESET: s->palette_current = 0; - s->irq_state &= ~DAFB_INTR_VBL; + s->regs[DAFB_INTR_STAT >> 2] &= ~DAFB_INTR_VBL; macfb_update_irq(s); break; case DAFB_LUT: @@ -593,6 +596,10 @@ static void macfb_ctrl_write(void *opaque, macfb_invalidate_display(s); } break; + default: + if (addr < MACFB_CTRL_TOPADDR) { + s->regs[addr >> 2] = val; + } } trace_macfb_ctrl_write(addr, val, size); @@ -618,9 +625,11 @@ static const VMStateDescription vmstate_macfb = { .minimum_version_id = 1, .post_load = macfb_post_load, .fields = (VMStateField[]) { + VMSTATE_UINT8(type, MacfbState), VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3), VMSTATE_UINT32(palette_current, MacfbState), VMSTATE_UINT32_ARRAY(regs, MacfbState, MACFB_NUM_REGS), + VMSTATE_TIMER_PTR(vbl_timer, MacfbState), VMSTATE_END_OF_LIST() } }; @@ -646,6 +655,14 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) return false; } + /* + * Set mode control registers to match the mode found above so that + * macfb_mode_write() does the right thing if no MacOS toolbox ROM + * is present to initialise them + */ + s->regs[DAFB_MODE_CTRL1 >> 2] = s->mode->mode_ctrl1; + s->regs[DAFB_MODE_CTRL2 >> 2] = s->mode->mode_ctrl2; + s->con = graphic_console_init(dev, 0, &macfb_ops, s); surface = qemu_console_surface(s->con); @@ -746,6 +763,16 @@ static Property macfb_sysbus_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static const VMStateDescription vmstate_macfb_sysbus = { + .name = "macfb-sysbus", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(macfb, MacfbSysBusState, 1, vmstate_macfb, MacfbState), + VMSTATE_END_OF_LIST() + } +}; + static Property macfb_nubus_properties[] = { DEFINE_PROP_UINT32("width", MacfbNubusState, macfb.width, 640), DEFINE_PROP_UINT32("height", MacfbNubusState, macfb.height, 480), @@ -755,6 +782,16 @@ static Property macfb_nubus_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static const VMStateDescription vmstate_macfb_nubus = { + .name = "macfb-nubus", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(macfb, MacfbNubusState, 1, vmstate_macfb, MacfbState), + VMSTATE_END_OF_LIST() + } +}; + static void macfb_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -762,7 +799,7 @@ static void macfb_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = macfb_sysbus_realize; dc->desc = "SysBus Macintosh framebuffer"; dc->reset = macfb_sysbus_reset; - dc->vmsd = &vmstate_macfb; + dc->vmsd = &vmstate_macfb_sysbus; device_class_set_props(dc, macfb_sysbus_properties); } @@ -777,7 +814,7 @@ static void macfb_nubus_class_init(ObjectClass *klass, void *data) &ndc->parent_unrealize); dc->desc = "Nubus Macintosh framebuffer"; dc->reset = macfb_nubus_reset; - dc->vmsd = &vmstate_macfb; + dc->vmsd = &vmstate_macfb_nubus; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); device_class_set_props(dc, macfb_nubus_properties); } diff --git a/hw/display/pl110_template.h b/hw/display/pl110_template.h index 877419aa81..0087785322 100644 --- a/hw/display/pl110_template.h +++ b/hw/display/pl110_template.h @@ -15,18 +15,18 @@ #if ORDER == 0 #define NAME glue(lblp_, BORDER) -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define SWAP_WORDS 1 #endif #elif ORDER == 1 #define NAME glue(bbbp_, BORDER) -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN #define SWAP_WORDS 1 #endif #else #define SWAP_PIXELS 1 #define NAME glue(lbbp_, BORDER) -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define SWAP_WORDS 1 #endif #endif diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c index 2887ce496b..7859c5d1cd 100644 --- a/hw/display/pxa2xx_lcd.c +++ b/hw/display/pxa2xx_lcd.c @@ -199,7 +199,7 @@ typedef struct QEMU_PACKED { SKIP_PIXEL(to); \ } while (0) -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN # define SWAP_WORDS 1 #endif @@ -1427,7 +1427,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, { PXA2xxLCDState *s; - s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState)); + s = g_new0(PXA2xxLCDState, 1); s->invalidated = 1; s->irq = irq; s->sysmem = sysmem; diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index d28849b121..ca217004bf 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -247,6 +247,13 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, size_t size; c = cursor_alloc(cursor->header.width, cursor->header.height); + + if (!c) { + qxl_set_guest_bug(qxl, "%s: cursor %ux%u alloc error", __func__, + cursor->header.width, cursor->header.height); + goto fail; + } + c->hot_x = cursor->header.hot_spot_x; c->hot_y = cursor->header.hot_spot_y; switch (cursor->header.type) { @@ -266,7 +273,7 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, } break; case SPICE_CURSOR_TYPE_ALPHA: - size = sizeof(uint32_t) * cursor->header.width * cursor->header.height; + size = sizeof(uint32_t) * c->width * c->height; qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id); if (qxl->debug > 2) { cursor_print_ascii_art(c, "qxl/alpha"); diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 1f9ad31943..2db34714fb 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -320,7 +320,7 @@ static ram_addr_t qxl_rom_size(void) #define QXL_ROM_SZ 8192 QEMU_BUILD_BUG_ON(QXL_REQUIRED_SZ > QXL_ROM_SZ); - return QEMU_ALIGN_UP(QXL_REQUIRED_SZ, qemu_real_host_page_size); + return QEMU_ALIGN_UP(QXL_REQUIRED_SZ, qemu_real_host_page_size()); } static void init_qxl_rom(PCIQXLDevice *d) @@ -2215,7 +2215,11 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) qxl_init_ramsize(qxl); vga->vbe_size = qxl->vgamem_size; vga->vram_size_mb = qxl->vga.vram_size / MiB; - vga_common_init(vga, OBJECT(dev)); + vga_common_init(vga, OBJECT(dev), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } vga_init(vga, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), false); portio_list_init(&qxl->vga_port_list, OBJECT(dev), qxl_vga_portio_list, diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 30d21f4d0b..e74de9579d 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -149,7 +149,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL) /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 1f28223c7b..c7beba453b 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -540,7 +540,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) }, }; - s = (TC6393xbState *) g_malloc0(sizeof(TC6393xbState)); + s = g_new0(TC6393xbState, 1); s->irq = irq; s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS); diff --git a/hw/display/tcx.c b/hw/display/tcx.c index d4d09d0df8..1b27b64f6d 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" #include "ui/console.h" diff --git a/hw/display/trace-events b/hw/display/trace-events index 4a687d1b8e..0c0ffcbe42 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -21,6 +21,10 @@ vmware_palette_write(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp" +vmware_verify_rect_less_than_zero(const char *name, const char *param, int x) "%s: %s was < 0 (%d)" +vmware_verify_rect_greater_than_bound(const char *name, const char *param, int bound, int x) "%s: %s was > %d (%d)" +vmware_verify_rect_surface_bound_exceeded(const char *name, const char *component, int bound, const char *param1, int value1, const char *param2, int value2) "%s: %s > %d (%s: %d, %s: %d)" +vmware_update_rect_delayed_flush(void) "display update FIFO full - forcing flush" # virtio-gpu-base.c virtio_gpu_features(bool virgl) "virgl %d" diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 90851e730b..46abbc5653 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -62,7 +62,10 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) const MemoryRegionPortio *vga_ports, *vbe_ports; s->global_vmstate = true; - vga_common_init(s, OBJECT(dev)); + if (!vga_common_init(s, OBJECT(dev), errp)) { + return; + } + s->legacy_address_space = isa_address_space(isadev); vga_io_memory = vga_init_io(s, OBJECT(dev), &vga_ports, &vbe_ports); isa_register_portio_list(isadev, &d->portio_vga, diff --git a/hw/display/vga-mmio.c b/hw/display/vga-mmio.c index 4969368081..75dfcedea5 100644 --- a/hw/display/vga-mmio.c +++ b/hw/display/vga-mmio.c @@ -102,7 +102,10 @@ static void vga_mmio_realizefn(DeviceState *dev, Error **errp) s->vga.bank_offset = 0; s->vga.global_vmstate = true; - vga_common_init(&s->vga, OBJECT(dev)); + if (!vga_common_init(&s->vga, OBJECT(dev), errp)) { + return; + } + sysbus_init_mmio(sbd, &s->vga.vram); s->vga.con = graphic_console_init(dev, 0, s->vga.hw_ops, &s->vga); } diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 62fb5c38c1..3e5bc259f7 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -239,7 +239,9 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) bool edid = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev)); + if (!vga_common_init(s, OBJECT(dev), errp)) { + return; + } vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), true); @@ -275,7 +277,9 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) bool edid = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev)); + if (!vga_common_init(s, OBJECT(dev), errp)) { + return; + } s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ diff --git a/hw/display/vga.c b/hw/display/vga.c index 9d1f66af40..5dca2d1528 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -94,19 +94,19 @@ const uint8_t gr_mask[16] = { (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define PAT(x) cbswap_32(x) #else #define PAT(x) (x) #endif -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define BIG 1 #else #define BIG 0 #endif -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff) #else #define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff) @@ -133,7 +133,7 @@ static const uint32_t mask16[16] = { #undef PAT -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define PAT(x) (x) #else #define PAT(x) cbswap_32(x) @@ -1296,7 +1296,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) if (cx > cx_max) cx_max = cx; *ch_attr_ptr = ch_attr; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN ch = ch_attr >> 8; cattr = ch_attr & 0xff; #else @@ -1477,7 +1477,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) vga_draw_line_func *vga_draw_line = NULL; bool share_surface, force_shadow = false; pixman_format_code_t format; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN bool byteswap = !s->big_endian_fb; #else bool byteswap = s->big_endian_fb; @@ -2168,9 +2168,10 @@ static inline uint32_t uint_clamp(uint32_t val, uint32_t vmin, uint32_t vmax) return val; } -void vga_common_init(VGACommonState *s, Object *obj) +bool vga_common_init(VGACommonState *s, Object *obj, Error **errp) { int i, j, v, b; + Error *local_err = NULL; for(i = 0;i < 256; i++) { v = 0; @@ -2205,8 +2206,18 @@ void vga_common_init(VGACommonState *s, Object *obj) s->vbe_size_mask = s->vbe_size - 1; s->is_vbe_vmstate = 1; + + if (s->global_vmstate && qemu_ram_block_by_name("vga.vram")) { + error_setg(errp, "Only one global VGA device can be used at a time"); + return false; + } + memory_region_init_ram_nomigrate(&s->vram, obj, "vga.vram", s->vram_size, - &error_fatal); + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return false; + } vmstate_register_ram(&s->vram, s->global_vmstate ? NULL : DEVICE(obj)); xen_register_framebuffer(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); @@ -2231,12 +2242,14 @@ void vga_common_init(VGACommonState *s, Object *obj) * into a device attribute set by the machine/platform to remove * all target endian dependencies from this file. */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN s->default_endian_fb = true; #else s->default_endian_fb = false; #endif vga_dirty_log_start(s); + + return true; } static const MemoryRegionPortio vga_portio_list[] = { diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 847e784ca6..305e700014 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -156,7 +156,7 @@ static inline int c6_to_8(int v) return (v << 2) | (b << 1) | b; } -void vga_common_init(VGACommonState *s, Object *obj); +bool vga_common_init(VGACommonState *s, Object *obj, Error **errp); void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, MemoryRegion *address_space_io, bool init_vga_ports); MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index 6cc4313b1a..0bca887703 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -108,7 +108,7 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp) { VirtIOGPU *g = VIRTIO_GPU(qdev); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN error_setg(errp, "virgl is not supported on bigendian platforms"); return; #endif diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 1597921c51..8bdf4bac6e 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu-common.h" #include "qemu/iov.h" #include "ui/console.h" #include "hw/virtio/virtio-gpu.h" diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index c6dc818988..529b5246b2 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -831,9 +831,9 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, } if (!(v % 16)) { - *iov = g_realloc(*iov, sizeof(struct iovec) * (v + 16)); + *iov = g_renew(struct iovec, *iov, v + 16); if (addr) { - *addr = g_realloc(*addr, sizeof(uint64_t) * (v + 16)); + *addr = g_renew(uint64_t, *addr, v + 16); } } (*iov)[v].iov_base = map; diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 5a2f7a4540..7b55c8d0e7 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -108,7 +108,9 @@ static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) /* init vga compat bits */ vga->vram_size_mb = 8; - vga_common_init(vga, OBJECT(vpci_dev)); + if (!vga_common_init(vga, OBJECT(vpci_dev), errp)) { + return; + } vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), pci_address_space_io(&vpci_dev->pci_dev), true); pci_register_bar(&vpci_dev->pci_dev, 0, diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index e2969a6c81..cedbbde522 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -80,7 +80,7 @@ struct vmsvga_state_s { struct vmsvga_rect_s { int x, y, w, h; } redraw_fifo[REDRAW_FIFO_LEN]; - int redraw_fifo_first, redraw_fifo_last; + int redraw_fifo_last; }; #define TYPE_VMWARE_SVGA "vmware-svga" @@ -297,46 +297,52 @@ static inline bool vmsvga_verify_rect(DisplaySurface *surface, int x, int y, int w, int h) { if (x < 0) { - fprintf(stderr, "%s: x was < 0 (%d)\n", name, x); + trace_vmware_verify_rect_less_than_zero(name, "x", x); return false; } if (x > SVGA_MAX_WIDTH) { - fprintf(stderr, "%s: x was > %d (%d)\n", name, SVGA_MAX_WIDTH, x); + trace_vmware_verify_rect_greater_than_bound(name, "x", SVGA_MAX_WIDTH, + x); return false; } if (w < 0) { - fprintf(stderr, "%s: w was < 0 (%d)\n", name, w); + trace_vmware_verify_rect_less_than_zero(name, "w", w); return false; } if (w > SVGA_MAX_WIDTH) { - fprintf(stderr, "%s: w was > %d (%d)\n", name, SVGA_MAX_WIDTH, w); + trace_vmware_verify_rect_greater_than_bound(name, "w", SVGA_MAX_WIDTH, + w); return false; } if (x + w > surface_width(surface)) { - fprintf(stderr, "%s: width was > %d (x: %d, w: %d)\n", - name, surface_width(surface), x, w); + trace_vmware_verify_rect_surface_bound_exceeded(name, "width", + surface_width(surface), + "x", x, "w", w); return false; } if (y < 0) { - fprintf(stderr, "%s: y was < 0 (%d)\n", name, y); + trace_vmware_verify_rect_less_than_zero(name, "y", y); return false; } if (y > SVGA_MAX_HEIGHT) { - fprintf(stderr, "%s: y was > %d (%d)\n", name, SVGA_MAX_HEIGHT, y); + trace_vmware_verify_rect_greater_than_bound(name, "y", SVGA_MAX_HEIGHT, + y); return false; } if (h < 0) { - fprintf(stderr, "%s: h was < 0 (%d)\n", name, h); + trace_vmware_verify_rect_less_than_zero(name, "h", h); return false; } if (h > SVGA_MAX_HEIGHT) { - fprintf(stderr, "%s: h was > %d (%d)\n", name, SVGA_MAX_HEIGHT, h); + trace_vmware_verify_rect_greater_than_bound(name, "y", SVGA_MAX_HEIGHT, + y); return false; } if (y + h > surface_height(surface)) { - fprintf(stderr, "%s: update height > %d (y: %d, h: %d)\n", - name, surface_height(surface), y, h); + trace_vmware_verify_rect_surface_bound_exceeded(name, "height", + surface_height(surface), + "y", y, "h", h); return false; } @@ -374,33 +380,39 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s, dpy_gfx_update(s->vga.con, x, y, w, h); } -static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s, - int x, int y, int w, int h) -{ - struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++]; - - s->redraw_fifo_last &= REDRAW_FIFO_LEN - 1; - rect->x = x; - rect->y = y; - rect->w = w; - rect->h = h; -} - static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s) { struct vmsvga_rect_s *rect; if (s->invalidated) { - s->redraw_fifo_first = s->redraw_fifo_last; + s->redraw_fifo_last = 0; return; } /* Overlapping region updates can be optimised out here - if someone * knows a smart algorithm to do that, please share. */ - while (s->redraw_fifo_first != s->redraw_fifo_last) { - rect = &s->redraw_fifo[s->redraw_fifo_first++]; - s->redraw_fifo_first &= REDRAW_FIFO_LEN - 1; + for (int i = 0; i < s->redraw_fifo_last; i++) { + rect = &s->redraw_fifo[i]; vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h); } + + s->redraw_fifo_last = 0; +} + +static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s, + int x, int y, int w, int h) +{ + + if (s->redraw_fifo_last >= REDRAW_FIFO_LEN) { + trace_vmware_update_rect_delayed_flush(); + vmsvga_update_rect_flush(s); + } + + struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++]; + + rect->x = x; + rect->y = y; + rect->w = w; + rect->h = h; } #ifdef HW_RECT_ACCEL @@ -509,6 +521,8 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s, int i, pixels; qc = cursor_alloc(c->width, c->height); + assert(qc != NULL); + qc->hot_x = c->hot_x; qc->hot_y = c->hot_y; switch (c->bpp) { @@ -1153,7 +1167,6 @@ static void vmsvga_reset(DeviceState *dev) s->config = 0; s->svgaid = SVGA_ID; s->cursor.on = 0; - s->redraw_fifo_first = 0; s->redraw_fifo_last = 0; s->syncing = 0; @@ -1248,7 +1261,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, &error_fatal); s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram); - vga_common_init(&s->vga, OBJECT(dev)); + vga_common_init(&s->vga, OBJECT(dev), &error_fatal); vga_init(&s->vga, OBJECT(dev), address_space, io, true); vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); s->new_depth = 32; diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 838260b6ad..cea10fe3c7 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -496,8 +496,8 @@ static int xenfb_map_fb(struct XenFB *xenfb) n_fbdirs = xenfb->fbpages * mode / 8; n_fbdirs = DIV_ROUND_UP(n_fbdirs, XC_PAGE_SIZE); - pgmfns = g_malloc0(sizeof(xen_pfn_t) * n_fbdirs); - fbmfns = g_malloc0(sizeof(xen_pfn_t) * xenfb->fbpages); + pgmfns = g_new0(xen_pfn_t, n_fbdirs); + fbmfns = g_new0(xen_pfn_t, xenfb->fbpages); xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); map = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom, diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 31ce01b7c5..08e5938ec7 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -15,7 +15,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index e4d2f1725b..aa1d323a36 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -646,8 +646,8 @@ static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n) struct rc4030DMAState *p; int i; - s = (rc4030_dma *)g_new0(rc4030_dma, n); - p = (struct rc4030DMAState *)g_new0(struct rc4030DMAState, n); + s = g_new0(rc4030_dma, n); + p = g_new0(struct rc4030DMAState, n); for (i = 0; i < n; i++) { p->opaque = opaque; p->n = i; diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 84f782fcdc..60ada3286b 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -744,6 +744,7 @@ static const TypeInfo xlnx_csu_dma_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(XlnxCSUDMA), .class_init = xlnx_csu_dma_class_init, + .class_size = sizeof(XlnxCSUDMAClass), .instance_init = xlnx_csu_dma_init, .interfaces = (InterfaceInfo[]) { { TYPE_STREAM_SINK }, diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c index 2d7eae72cd..dd66be5265 100644 --- a/hw/dma/xlnx_dpdma.c +++ b/hw/dma/xlnx_dpdma.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/dma/xlnx_dpdma.h" diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 911d21c8cf..9b736e7a9f 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -312,7 +312,7 @@ static void aspeed_gpio_set_pin_level(AspeedGPIOState *s, uint32_t set_idx, if (level) { value |= pin_mask; } else { - value &= !pin_mask; + value &= ~pin_mask; } aspeed_gpio_update(s, &s->sets[set_idx], value); @@ -571,7 +571,7 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size) qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" HWADDR_PRIx"\n", __func__, offset); return 0; - }; + } } static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 98b30e0395..f7595c0857 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -4,7 +4,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "cpu.h" #include "elf.h" diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index 3fbfe41c9e..fcf65903bd 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -11,3 +11,8 @@ config VMBUS bool default y depends on HYPERV + +config SYNDBG + bool + default y + depends on VMBUS diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index cb1074f234..4a1b59cb9d 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -27,13 +27,16 @@ struct SynICState { CPUState *cs; - bool enabled; + bool sctl_enabled; hwaddr msg_page_addr; hwaddr event_page_addr; MemoryRegion msg_page_mr; MemoryRegion event_page_mr; struct hyperv_message_page *msg_page; struct hyperv_event_flags_page *event_page; + + QemuMutex sint_routes_mutex; + QLIST_HEAD(, HvSintRoute) sint_routes; }; #define TYPE_SYNIC "hyperv-synic" @@ -51,11 +54,11 @@ static SynICState *get_synic(CPUState *cs) return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); } -static void synic_update(SynICState *synic, bool enable, +static void synic_update(SynICState *synic, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { - synic->enabled = enable; + synic->sctl_enabled = sctl_enable; if (synic->msg_page_addr != msg_page_addr) { if (synic->msg_page_addr) { memory_region_del_subregion(get_system_memory(), @@ -80,7 +83,7 @@ static void synic_update(SynICState *synic, bool enable, } } -void hyperv_synic_update(CPUState *cs, bool enable, +void hyperv_synic_update(CPUState *cs, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { SynICState *synic = get_synic(cs); @@ -89,7 +92,7 @@ void hyperv_synic_update(CPUState *cs, bool enable, return; } - synic_update(synic, enable, msg_page_addr, event_page_addr); + synic_update(synic, sctl_enable, msg_page_addr, event_page_addr); } static void synic_realize(DeviceState *dev, Error **errp) @@ -110,16 +113,20 @@ static void synic_realize(DeviceState *dev, Error **errp) sizeof(*synic->event_page), &error_abort); synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr); + qemu_mutex_init(&synic->sint_routes_mutex); + QLIST_INIT(&synic->sint_routes); g_free(msgp_name); g_free(eventp_name); } + static void synic_reset(DeviceState *dev) { SynICState *synic = SYNIC(dev); memset(synic->msg_page, 0, sizeof(*synic->msg_page)); memset(synic->event_page, 0, sizeof(*synic->event_page)); synic_update(synic, false, 0, 0); + assert(QLIST_EMPTY(&synic->sint_routes)); } static void synic_class_init(ObjectClass *klass, void *data) @@ -214,6 +221,7 @@ struct HvSintRoute { HvSintStagedMessage *staged_msg; unsigned refcount; + QLIST_ENTRY(HvSintRoute) link; }; static CPUState *hyperv_find_vcpu(uint32_t vp_index) @@ -259,7 +267,7 @@ static void cpu_post_msg(CPUState *cs, run_on_cpu_data data) assert(staged_msg->state == HV_STAGED_MSG_BUSY); - if (!synic->enabled || !synic->msg_page_addr) { + if (!synic->msg_page_addr) { staged_msg->status = -ENXIO; goto posted; } @@ -343,7 +351,7 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) if (eventno > HV_EVENT_FLAGS_COUNT) { return -EINVAL; } - if (!synic->enabled || !synic->event_page_addr) { + if (!synic->sctl_enabled || !synic->event_page_addr) { return -ENXIO; } @@ -364,11 +372,12 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, HvSintMsgCb cb, void *cb_data) { - HvSintRoute *sint_route; - EventNotifier *ack_notifier; + HvSintRoute *sint_route = NULL; + EventNotifier *ack_notifier = NULL; int r, gsi; CPUState *cs; SynICState *synic; + bool ack_event_initialized = false; cs = hyperv_find_vcpu(vp_index); if (!cs) { @@ -381,57 +390,77 @@ HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, } sint_route = g_new0(HvSintRoute, 1); - r = event_notifier_init(&sint_route->sint_set_notifier, false); - if (r) { - goto err; + if (!sint_route) { + return NULL; } + sint_route->synic = synic; + sint_route->sint = sint; + sint_route->refcount = 1; ack_notifier = cb ? &sint_route->sint_ack_notifier : NULL; if (ack_notifier) { sint_route->staged_msg = g_new0(HvSintStagedMessage, 1); + if (!sint_route->staged_msg) { + goto cleanup_err_sint; + } sint_route->staged_msg->cb = cb; sint_route->staged_msg->cb_data = cb_data; r = event_notifier_init(ack_notifier, false); if (r) { - goto err_sint_set_notifier; + goto cleanup_err_sint; } - event_notifier_set_handler(ack_notifier, sint_ack_handler); + ack_event_initialized = true; + } + + /* See if we are done or we need to setup a GSI for this SintRoute */ + if (!synic->sctl_enabled) { + goto cleanup; + } + + /* We need to setup a GSI for this SintRoute */ + r = event_notifier_init(&sint_route->sint_set_notifier, false); + if (r) { + goto cleanup_err_sint; } gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); if (gsi < 0) { - goto err_gsi; + goto cleanup_err_sint_notifier; } r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, &sint_route->sint_set_notifier, ack_notifier, gsi); if (r) { - goto err_irqfd; + goto cleanup_err_irqfd; } sint_route->gsi = gsi; - sint_route->synic = synic; - sint_route->sint = sint; - sint_route->refcount = 1; - +cleanup: + qemu_mutex_lock(&synic->sint_routes_mutex); + QLIST_INSERT_HEAD(&synic->sint_routes, sint_route, link); + qemu_mutex_unlock(&synic->sint_routes_mutex); return sint_route; -err_irqfd: +cleanup_err_irqfd: kvm_irqchip_release_virq(kvm_state, gsi); -err_gsi: + +cleanup_err_sint_notifier: + event_notifier_cleanup(&sint_route->sint_set_notifier); + +cleanup_err_sint: if (ack_notifier) { - event_notifier_set_handler(ack_notifier, NULL); - event_notifier_cleanup(ack_notifier); + if (ack_event_initialized) { + event_notifier_set_handler(ack_notifier, NULL); + event_notifier_cleanup(ack_notifier); + } + g_free(sint_route->staged_msg); } -err_sint_set_notifier: - event_notifier_cleanup(&sint_route->sint_set_notifier); -err: - g_free(sint_route); + g_free(sint_route); return NULL; } @@ -442,6 +471,8 @@ void hyperv_sint_route_ref(HvSintRoute *sint_route) void hyperv_sint_route_unref(HvSintRoute *sint_route) { + SynICState *synic; + if (!sint_route) { return; } @@ -452,21 +483,33 @@ void hyperv_sint_route_unref(HvSintRoute *sint_route) return; } - kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, - &sint_route->sint_set_notifier, - sint_route->gsi); - kvm_irqchip_release_virq(kvm_state, sint_route->gsi); + synic = sint_route->synic; + qemu_mutex_lock(&synic->sint_routes_mutex); + QLIST_REMOVE(sint_route, link); + qemu_mutex_unlock(&synic->sint_routes_mutex); + + if (sint_route->gsi) { + kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, + &sint_route->sint_set_notifier, + sint_route->gsi); + kvm_irqchip_release_virq(kvm_state, sint_route->gsi); + event_notifier_cleanup(&sint_route->sint_set_notifier); + } + if (sint_route->staged_msg) { event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL); event_notifier_cleanup(&sint_route->sint_ack_notifier); g_free(sint_route->staged_msg); } - event_notifier_cleanup(&sint_route->sint_set_notifier); g_free(sint_route); } int hyperv_sint_route_set_sint(HvSintRoute *sint_route) { + if (!sint_route->gsi) { + return 0; + } + return event_notifier_set(&sint_route->sint_set_notifier); } @@ -661,3 +704,246 @@ uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) } return HV_STATUS_INVALID_CONNECTION_ID; } + +static HvSynDbgHandler hv_syndbg_handler; +static void *hv_syndbg_context; + +void hyperv_set_syndbg_handler(HvSynDbgHandler handler, void *context) +{ + assert(!hv_syndbg_handler); + hv_syndbg_handler = handler; + hv_syndbg_context = context; +} + +uint16_t hyperv_hcall_reset_dbg_session(uint64_t outgpa) +{ + uint16_t ret; + HvSynDbgMsg msg; + struct hyperv_reset_debug_session_output *reset_dbg_session = NULL; + hwaddr len; + + if (!hv_syndbg_handler) { + ret = HV_STATUS_INVALID_HYPERCALL_CODE; + goto cleanup; + } + + len = sizeof(*reset_dbg_session); + reset_dbg_session = cpu_physical_memory_map(outgpa, &len, 1); + if (!reset_dbg_session || len < sizeof(*reset_dbg_session)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + msg.type = HV_SYNDBG_MSG_CONNECTION_INFO; + ret = hv_syndbg_handler(hv_syndbg_context, &msg); + if (ret) { + goto cleanup; + } + + reset_dbg_session->host_ip = msg.u.connection_info.host_ip; + reset_dbg_session->host_port = msg.u.connection_info.host_port; + /* The following fields are only used as validation for KDVM */ + memset(&reset_dbg_session->host_mac, 0, + sizeof(reset_dbg_session->host_mac)); + reset_dbg_session->target_ip = msg.u.connection_info.host_ip; + reset_dbg_session->target_port = msg.u.connection_info.host_port; + memset(&reset_dbg_session->target_mac, 0, + sizeof(reset_dbg_session->target_mac)); +cleanup: + if (reset_dbg_session) { + cpu_physical_memory_unmap(reset_dbg_session, + sizeof(*reset_dbg_session), 1, len); + } + + return ret; +} + +uint16_t hyperv_hcall_retreive_dbg_data(uint64_t ingpa, uint64_t outgpa, + bool fast) +{ + uint16_t ret; + struct hyperv_retrieve_debug_data_input *debug_data_in = NULL; + struct hyperv_retrieve_debug_data_output *debug_data_out = NULL; + hwaddr in_len, out_len; + HvSynDbgMsg msg; + + if (fast || !hv_syndbg_handler) { + ret = HV_STATUS_INVALID_HYPERCALL_CODE; + goto cleanup; + } + + in_len = sizeof(*debug_data_in); + debug_data_in = cpu_physical_memory_map(ingpa, &in_len, 0); + if (!debug_data_in || in_len < sizeof(*debug_data_in)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + out_len = sizeof(*debug_data_out); + debug_data_out = cpu_physical_memory_map(outgpa, &out_len, 1); + if (!debug_data_out || out_len < sizeof(*debug_data_out)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + msg.type = HV_SYNDBG_MSG_RECV; + msg.u.recv.buf_gpa = outgpa + sizeof(*debug_data_out); + msg.u.recv.count = TARGET_PAGE_SIZE - sizeof(*debug_data_out); + msg.u.recv.options = debug_data_in->options; + msg.u.recv.timeout = debug_data_in->timeout; + msg.u.recv.is_raw = true; + ret = hv_syndbg_handler(hv_syndbg_context, &msg); + if (ret == HV_STATUS_NO_DATA) { + debug_data_out->retrieved_count = 0; + debug_data_out->remaining_count = debug_data_in->count; + goto cleanup; + } else if (ret != HV_STATUS_SUCCESS) { + goto cleanup; + } + + debug_data_out->retrieved_count = msg.u.recv.retrieved_count; + debug_data_out->remaining_count = + debug_data_in->count - msg.u.recv.retrieved_count; +cleanup: + if (debug_data_out) { + cpu_physical_memory_unmap(debug_data_out, sizeof(*debug_data_out), 1, + out_len); + } + + if (debug_data_in) { + cpu_physical_memory_unmap(debug_data_in, sizeof(*debug_data_in), 0, + in_len); + } + + return ret; +} + +uint16_t hyperv_hcall_post_dbg_data(uint64_t ingpa, uint64_t outgpa, bool fast) +{ + uint16_t ret; + struct hyperv_post_debug_data_input *post_data_in = NULL; + struct hyperv_post_debug_data_output *post_data_out = NULL; + hwaddr in_len, out_len; + HvSynDbgMsg msg; + + if (fast || !hv_syndbg_handler) { + ret = HV_STATUS_INVALID_HYPERCALL_CODE; + goto cleanup; + } + + in_len = sizeof(*post_data_in); + post_data_in = cpu_physical_memory_map(ingpa, &in_len, 0); + if (!post_data_in || in_len < sizeof(*post_data_in)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + if (post_data_in->count > TARGET_PAGE_SIZE - sizeof(*post_data_in)) { + ret = HV_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + out_len = sizeof(*post_data_out); + post_data_out = cpu_physical_memory_map(outgpa, &out_len, 1); + if (!post_data_out || out_len < sizeof(*post_data_out)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + msg.type = HV_SYNDBG_MSG_SEND; + msg.u.send.buf_gpa = ingpa + sizeof(*post_data_in); + msg.u.send.count = post_data_in->count; + msg.u.send.is_raw = true; + ret = hv_syndbg_handler(hv_syndbg_context, &msg); + if (ret != HV_STATUS_SUCCESS) { + goto cleanup; + } + + post_data_out->pending_count = msg.u.send.pending_count; + ret = post_data_out->pending_count ? HV_STATUS_INSUFFICIENT_BUFFERS : + HV_STATUS_SUCCESS; +cleanup: + if (post_data_out) { + cpu_physical_memory_unmap(post_data_out, + sizeof(*post_data_out), 1, out_len); + } + + if (post_data_in) { + cpu_physical_memory_unmap(post_data_in, + sizeof(*post_data_in), 0, in_len); + } + + return ret; +} + +uint32_t hyperv_syndbg_send(uint64_t ingpa, uint32_t count) +{ + HvSynDbgMsg msg; + + if (!hv_syndbg_handler) { + return HV_SYNDBG_STATUS_INVALID; + } + + msg.type = HV_SYNDBG_MSG_SEND; + msg.u.send.buf_gpa = ingpa; + msg.u.send.count = count; + msg.u.send.is_raw = false; + if (hv_syndbg_handler(hv_syndbg_context, &msg)) { + return HV_SYNDBG_STATUS_INVALID; + } + + return HV_SYNDBG_STATUS_SEND_SUCCESS; +} + +uint32_t hyperv_syndbg_recv(uint64_t ingpa, uint32_t count) +{ + uint16_t ret; + HvSynDbgMsg msg; + + if (!hv_syndbg_handler) { + return HV_SYNDBG_STATUS_INVALID; + } + + msg.type = HV_SYNDBG_MSG_RECV; + msg.u.recv.buf_gpa = ingpa; + msg.u.recv.count = count; + msg.u.recv.options = 0; + msg.u.recv.timeout = 0; + msg.u.recv.is_raw = false; + ret = hv_syndbg_handler(hv_syndbg_context, &msg); + if (ret != HV_STATUS_SUCCESS) { + return 0; + } + + return HV_SYNDBG_STATUS_SET_SIZE(HV_SYNDBG_STATUS_RECV_SUCCESS, + msg.u.recv.retrieved_count); +} + +void hyperv_syndbg_set_pending_page(uint64_t ingpa) +{ + HvSynDbgMsg msg; + + if (!hv_syndbg_handler) { + return; + } + + msg.type = HV_SYNDBG_MSG_SET_PENDING_PAGE; + msg.u.pending_page.buf_gpa = ingpa; + hv_syndbg_handler(hv_syndbg_context, &msg); +} + +uint64_t hyperv_syndbg_query_options(void) +{ + HvSynDbgMsg msg; + + if (!hv_syndbg_handler) { + return 0; + } + + msg.type = HV_SYNDBG_MSG_QUERY_OPTIONS; + if (hv_syndbg_handler(hv_syndbg_context, &msg) != HV_STATUS_SUCCESS) { + return 0; + } + + return msg.u.query_options.options; +} diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index 1367e2994f..b43f119ea5 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -1,3 +1,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) +specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c new file mode 100644 index 0000000000..16d04cfdc6 --- /dev/null +++ b/hw/hyperv/syndbg.c @@ -0,0 +1,401 @@ +/* + * QEMU Hyper-V Synthetic Debugging device + * + * 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/ctype.h" +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/sockets.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/loader.h" +#include "cpu.h" +#include "hw/hyperv/hyperv.h" +#include "hw/hyperv/vmbus-bridge.h" +#include "hw/hyperv/hyperv-proto.h" +#include "net/net.h" +#include "net/eth.h" +#include "net/checksum.h" +#include "trace.h" + +#define TYPE_HV_SYNDBG "hv-syndbg" + +typedef struct HvSynDbg { + DeviceState parent_obj; + + char *host_ip; + uint16_t host_port; + bool use_hcalls; + + uint32_t target_ip; + struct sockaddr_in servaddr; + int socket; + bool has_data_pending; + uint64_t pending_page_gpa; +} HvSynDbg; + +#define HVSYNDBG(obj) OBJECT_CHECK(HvSynDbg, (obj), TYPE_HV_SYNDBG) + +/* returns NULL unless there is exactly one HV Synth debug device */ +static HvSynDbg *hv_syndbg_find(void) +{ + /* Returns NULL unless there is exactly one hvsd device */ + return HVSYNDBG(object_resolve_path_type("", TYPE_HV_SYNDBG, NULL)); +} + +static void set_pending_state(HvSynDbg *syndbg, bool has_pending) +{ + hwaddr out_len; + void *out_data; + + syndbg->has_data_pending = has_pending; + + if (!syndbg->pending_page_gpa) { + return; + } + + out_len = 1; + out_data = cpu_physical_memory_map(syndbg->pending_page_gpa, &out_len, 1); + if (out_data) { + *(uint8_t *)out_data = !!has_pending; + cpu_physical_memory_unmap(out_data, out_len, 1, out_len); + } +} + +static bool get_udb_pkt_data(void *p, uint32_t len, uint32_t *data_ofs, + uint32_t *src_ip) +{ + uint32_t offset, curr_len = len; + + if (curr_len < sizeof(struct eth_header) || + (be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto) != ETH_P_IP)) { + return false; + } + offset = sizeof(struct eth_header); + curr_len -= sizeof(struct eth_header); + + if (curr_len < sizeof(struct ip_header) || + PKT_GET_IP_HDR(p)->ip_p != IP_PROTO_UDP) { + return false; + } + offset += PKT_GET_IP_HDR_LEN(p); + curr_len -= PKT_GET_IP_HDR_LEN(p); + + if (curr_len < sizeof(struct udp_header)) { + return false; + } + + offset += sizeof(struct udp_header); + *data_ofs = offset; + *src_ip = PKT_GET_IP_HDR(p)->ip_src; + return true; +} + +static uint16_t handle_send_msg(HvSynDbg *syndbg, uint64_t ingpa, + uint32_t count, bool is_raw, + uint32_t *pending_count) +{ + uint16_t ret; + hwaddr data_len; + void *debug_data = NULL; + uint32_t udp_data_ofs = 0; + const void *pkt_data; + int sent_count; + + data_len = count; + debug_data = cpu_physical_memory_map(ingpa, &data_len, 0); + if (!debug_data || data_len < count) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + if (is_raw && + !get_udb_pkt_data(debug_data, count, &udp_data_ofs, + &syndbg->target_ip)) { + ret = HV_STATUS_SUCCESS; + goto cleanup; + } + + pkt_data = (const void *)((uintptr_t)debug_data + udp_data_ofs); + sent_count = sendto(syndbg->socket, pkt_data, count - udp_data_ofs, + MSG_NOSIGNAL, NULL, 0); + if (sent_count == -1) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + *pending_count = count - (sent_count + udp_data_ofs); + ret = HV_STATUS_SUCCESS; +cleanup: + if (debug_data) { + cpu_physical_memory_unmap(debug_data, count, 0, data_len); + } + + return ret; +} + +#define UDP_PKT_HEADER_SIZE \ + (sizeof(struct eth_header) + sizeof(struct ip_header) +\ + sizeof(struct udp_header)) + +static bool create_udp_pkt(HvSynDbg *syndbg, void *pkt, uint32_t pkt_len, + void *udp_data, uint32_t udp_data_len) +{ + struct udp_header *udp_part; + + if (pkt_len < (UDP_PKT_HEADER_SIZE + udp_data_len)) { + return false; + } + + /* Setup the eth */ + memset(&PKT_GET_ETH_HDR(pkt)->h_source, 0, ETH_ALEN); + memset(&PKT_GET_ETH_HDR(pkt)->h_dest, 0, ETH_ALEN); + PKT_GET_ETH_HDR(pkt)->h_proto = cpu_to_be16(ETH_P_IP); + + /* Setup the ip */ + PKT_GET_IP_HDR(pkt)->ip_ver_len = + (4 << 4) | (sizeof(struct ip_header) >> 2); + PKT_GET_IP_HDR(pkt)->ip_tos = 0; + PKT_GET_IP_HDR(pkt)->ip_id = 0; + PKT_GET_IP_HDR(pkt)->ip_off = 0; + PKT_GET_IP_HDR(pkt)->ip_ttl = 64; /* IPDEFTTL */ + PKT_GET_IP_HDR(pkt)->ip_p = IP_PROTO_UDP; + PKT_GET_IP_HDR(pkt)->ip_src = syndbg->servaddr.sin_addr.s_addr; + PKT_GET_IP_HDR(pkt)->ip_dst = syndbg->target_ip; + PKT_GET_IP_HDR(pkt)->ip_len = + cpu_to_be16(sizeof(struct ip_header) + sizeof(struct udp_header) + + udp_data_len); + eth_fix_ip4_checksum(PKT_GET_IP_HDR(pkt), PKT_GET_IP_HDR_LEN(pkt)); + + udp_part = (struct udp_header *)((uintptr_t)pkt + + sizeof(struct eth_header) + + PKT_GET_IP_HDR_LEN(pkt)); + udp_part->uh_sport = syndbg->servaddr.sin_port; + udp_part->uh_dport = syndbg->servaddr.sin_port; + udp_part->uh_ulen = cpu_to_be16(sizeof(struct udp_header) + udp_data_len); + memcpy(udp_part + 1, udp_data, udp_data_len); + net_checksum_calculate(pkt, UDP_PKT_HEADER_SIZE + udp_data_len, CSUM_ALL); + return true; +} + +static uint16_t handle_recv_msg(HvSynDbg *syndbg, uint64_t outgpa, + uint32_t count, bool is_raw, uint32_t options, + uint64_t timeout, uint32_t *retrieved_count) +{ + uint16_t ret; + uint8_t data_buf[TARGET_PAGE_SIZE - UDP_PKT_HEADER_SIZE]; + hwaddr out_len; + void *out_data; + ssize_t recv_byte_count; + + /* TODO: Handle options and timeout */ + (void)options; + (void)timeout; + + if (!syndbg->has_data_pending) { + recv_byte_count = 0; + } else { + recv_byte_count = recv(syndbg->socket, data_buf, + MIN(sizeof(data_buf), count), MSG_WAITALL); + if (recv_byte_count == -1) { + return HV_STATUS_INVALID_PARAMETER; + } + } + + if (!recv_byte_count) { + *retrieved_count = 0; + return HV_STATUS_NO_DATA; + } + + set_pending_state(syndbg, false); + + out_len = recv_byte_count; + if (is_raw) { + out_len += UDP_PKT_HEADER_SIZE; + } + out_data = cpu_physical_memory_map(outgpa, &out_len, 1); + if (!out_data) { + return HV_STATUS_INSUFFICIENT_MEMORY; + } + + if (is_raw && + !create_udp_pkt(syndbg, out_data, + recv_byte_count + UDP_PKT_HEADER_SIZE, + data_buf, recv_byte_count)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup_out_data; + } else if (!is_raw) { + memcpy(out_data, data_buf, recv_byte_count); + } + + *retrieved_count = recv_byte_count; + if (is_raw) { + *retrieved_count += UDP_PKT_HEADER_SIZE; + } + ret = HV_STATUS_SUCCESS; + +cleanup_out_data: + cpu_physical_memory_unmap(out_data, out_len, 1, out_len); + return ret; +} + +static uint16_t hv_syndbg_handler(void *context, HvSynDbgMsg *msg) +{ + HvSynDbg *syndbg = context; + uint16_t ret = HV_STATUS_INVALID_HYPERCALL_CODE; + + switch (msg->type) { + case HV_SYNDBG_MSG_CONNECTION_INFO: + msg->u.connection_info.host_ip = + ntohl(syndbg->servaddr.sin_addr.s_addr); + msg->u.connection_info.host_port = + ntohs(syndbg->servaddr.sin_port); + ret = HV_STATUS_SUCCESS; + break; + case HV_SYNDBG_MSG_SEND: + ret = handle_send_msg(syndbg, msg->u.send.buf_gpa, msg->u.send.count, + msg->u.send.is_raw, &msg->u.send.pending_count); + break; + case HV_SYNDBG_MSG_RECV: + ret = handle_recv_msg(syndbg, msg->u.recv.buf_gpa, msg->u.recv.count, + msg->u.recv.is_raw, msg->u.recv.options, + msg->u.recv.timeout, + &msg->u.recv.retrieved_count); + break; + case HV_SYNDBG_MSG_SET_PENDING_PAGE: + syndbg->pending_page_gpa = msg->u.pending_page.buf_gpa; + ret = HV_STATUS_SUCCESS; + break; + case HV_SYNDBG_MSG_QUERY_OPTIONS: + msg->u.query_options.options = 0; + if (syndbg->use_hcalls) { + msg->u.query_options.options = HV_X64_SYNDBG_OPTION_USE_HCALLS; + } + ret = HV_STATUS_SUCCESS; + break; + default: + break; + } + + return ret; +} + +static void hv_syndbg_recv_event(void *opaque) +{ + HvSynDbg *syndbg = opaque; + struct timeval tv; + fd_set rfds; + + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(syndbg->socket, &rfds); + if (select(syndbg->socket + 1, &rfds, NULL, NULL, &tv) > 0) { + set_pending_state(syndbg, true); + } +} + +static void hv_syndbg_realize(DeviceState *dev, Error **errp) +{ + HvSynDbg *syndbg = HVSYNDBG(dev); + + if (!hv_syndbg_find()) { + error_setg(errp, "at most one %s device is permitted", TYPE_HV_SYNDBG); + return; + } + + if (!vmbus_bridge_find()) { + error_setg(errp, "%s device requires vmbus-bridge device", + TYPE_HV_SYNDBG); + return; + } + + /* Parse and host_ip */ + if (qemu_isdigit(syndbg->host_ip[0])) { + syndbg->servaddr.sin_addr.s_addr = inet_addr(syndbg->host_ip); + } else { + struct hostent *he = gethostbyname(syndbg->host_ip); + if (!he) { + error_setg(errp, "%s failed to resolve host name %s", + TYPE_HV_SYNDBG, syndbg->host_ip); + return; + } + syndbg->servaddr.sin_addr = *(struct in_addr *)he->h_addr; + } + + syndbg->socket = socket(AF_INET, SOCK_DGRAM, 0); + if (syndbg->socket < 0) { + error_setg(errp, "%s failed to create socket", TYPE_HV_SYNDBG); + return; + } + + qemu_socket_set_nonblock(syndbg->socket); + + syndbg->servaddr.sin_port = htons(syndbg->host_port); + syndbg->servaddr.sin_family = AF_INET; + if (connect(syndbg->socket, (struct sockaddr *)&syndbg->servaddr, + sizeof(syndbg->servaddr)) < 0) { + closesocket(syndbg->socket); + error_setg(errp, "%s failed to connect to socket", TYPE_HV_SYNDBG); + return; + } + + syndbg->pending_page_gpa = 0; + syndbg->has_data_pending = false; + hyperv_set_syndbg_handler(hv_syndbg_handler, syndbg); + qemu_set_fd_handler(syndbg->socket, hv_syndbg_recv_event, NULL, syndbg); +} + +static void hv_syndbg_unrealize(DeviceState *dev) +{ + HvSynDbg *syndbg = HVSYNDBG(dev); + + if (syndbg->socket > 0) { + qemu_set_fd_handler(syndbg->socket, NULL, NULL, NULL); + closesocket(syndbg->socket); + } +} + +static const VMStateDescription vmstate_hv_syndbg = { + .name = TYPE_HV_SYNDBG, + .unmigratable = 1, +}; + +static Property hv_syndbg_properties[] = { + DEFINE_PROP_STRING("host_ip", HvSynDbg, host_ip), + DEFINE_PROP_UINT16("host_port", HvSynDbg, host_port, 50000), + DEFINE_PROP_BOOL("use_hcalls", HvSynDbg, use_hcalls, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void hv_syndbg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, hv_syndbg_properties); + dc->fw_name = TYPE_HV_SYNDBG; + dc->vmsd = &vmstate_hv_syndbg; + dc->realize = hv_syndbg_realize; + dc->unrealize = hv_syndbg_unrealize; + dc->user_creatable = true; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo hv_syndbg_type_info = { + .name = TYPE_HV_SYNDBG, + .parent = TYPE_DEVICE, + .instance_size = sizeof(HvSynDbg), + .class_init = hv_syndbg_class_init, +}; + +static void hv_syndbg_register_types(void) +{ + type_register_static(&hv_syndbg_type_info); +} + +type_init(hv_syndbg_register_types) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 0e7d2763b9..d0cb2d32fa 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -274,7 +274,7 @@ static int i2c_slave_post_load(void *opaque, int version_id) bus = I2C_BUS(qdev_get_parent_bus(DEVICE(dev))); if ((bus->saved_address == dev->address) || (bus->saved_address == I2C_BROADCAST)) { - node = g_malloc(sizeof(struct I2CNode)); + node = g_new(struct I2CNode, 1); node->elt = dev; QLIST_INSERT_HEAD(&bus->current_devs, node, next); } @@ -319,7 +319,7 @@ static bool i2c_slave_match(I2CSlave *candidate, uint8_t address, bool broadcast, I2CNodeList *current_devs) { if ((candidate->address == address) || (broadcast)) { - I2CNode *node = g_malloc(sizeof(struct I2CNode)); + I2CNode *node = g_new(struct I2CNode, 1); node->elt = candidate; QLIST_INSERT_HEAD(current_devs, node, next); return true; diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index 847c59921c..3945de795c 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -30,24 +30,6 @@ #define PCA9548_CHANNEL_COUNT 8 #define PCA9546_CHANNEL_COUNT 4 -/* - * struct Pca954xChannel - The i2c mux device will have N of these states - * that own the i2c channel bus. - * @bus: The owned channel bus. - * @enabled: Is this channel active? - */ -typedef struct Pca954xChannel { - SysBusDevice parent; - - I2CBus *bus; - - bool enabled; -} Pca954xChannel; - -#define TYPE_PCA954X_CHANNEL "pca954x-channel" -#define PCA954X_CHANNEL(obj) \ - OBJECT_CHECK(Pca954xChannel, (obj), TYPE_PCA954X_CHANNEL) - /* * struct Pca954xState - The pca954x state object. * @control: The value written to the mux control. @@ -59,8 +41,8 @@ typedef struct Pca954xState { uint8_t control; - /* The channel i2c buses. */ - Pca954xChannel channel[PCA9548_CHANNEL_COUNT]; + bool enabled[PCA9548_CHANNEL_COUNT]; + I2CBus *bus[PCA9548_CHANNEL_COUNT]; } Pca954xState; /* @@ -89,7 +71,7 @@ static bool pca954x_match(I2CSlave *candidate, uint8_t address, /* They are talking to the mux itself (or all devices enabled). */ if ((candidate->address == address) || broadcast) { - I2CNode *node = g_malloc(sizeof(struct I2CNode)); + I2CNode *node = g_new(struct I2CNode, 1); node->elt = candidate; QLIST_INSERT_HEAD(current_devs, node, next); if (!broadcast) { @@ -98,11 +80,11 @@ static bool pca954x_match(I2CSlave *candidate, uint8_t address, } for (i = 0; i < mc->nchans; i++) { - if (!mux->channel[i].enabled) { + if (!mux->enabled[i]) { continue; } - if (i2c_scan_bus(mux->channel[i].bus, address, broadcast, + if (i2c_scan_bus(mux->bus[i], address, broadcast, current_devs)) { if (!broadcast) { return true; @@ -125,9 +107,9 @@ static void pca954x_enable_channel(Pca954xState *s, uint8_t enable_mask) */ for (i = 0; i < mc->nchans; i++) { if (enable_mask & (1 << i)) { - s->channel[i].enabled = true; + s->enabled[i] = true; } else { - s->channel[i].enabled = false; + s->enabled[i] = false; } } } @@ -184,23 +166,7 @@ I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel) Pca954xState *pca954x = PCA954X(mux); g_assert(channel < pc->nchans); - return I2C_BUS(qdev_get_child_bus(DEVICE(&pca954x->channel[channel]), - "i2c-bus")); -} - -static void pca954x_channel_init(Object *obj) -{ - Pca954xChannel *s = PCA954X_CHANNEL(obj); - s->bus = i2c_init_bus(DEVICE(s), "i2c-bus"); - - /* Start all channels as disabled. */ - s->enabled = false; -} - -static void pca954x_channel_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->desc = "Pca954x Channel"; + return pca954x->bus[channel]; } static void pca9546_class_init(ObjectClass *klass, void *data) @@ -215,28 +181,19 @@ static void pca9548_class_init(ObjectClass *klass, void *data) s->nchans = PCA9548_CHANNEL_COUNT; } -static void pca954x_realize(DeviceState *dev, Error **errp) -{ - Pca954xState *s = PCA954X(dev); - Pca954xClass *c = PCA954X_GET_CLASS(s); - int i; - - /* SMBus modules. Cannot fail. */ - for (i = 0; i < c->nchans; i++) { - sysbus_realize(SYS_BUS_DEVICE(&s->channel[i]), &error_abort); - } -} - static void pca954x_init(Object *obj) { Pca954xState *s = PCA954X(obj); Pca954xClass *c = PCA954X_GET_CLASS(obj); int i; - /* Only initialize the children we expect. */ + /* SMBus modules. Cannot fail. */ for (i = 0; i < c->nchans; i++) { - object_initialize_child(obj, "channel[*]", &s->channel[i], - TYPE_PCA954X_CHANNEL); + g_autofree gchar *bus_name = g_strdup_printf("i2c.%d", i); + + /* start all channels as disabled. */ + s->enabled[i] = false; + s->bus[i] = i2c_init_bus(DEVICE(s), bus_name); } } @@ -252,7 +209,6 @@ static void pca954x_class_init(ObjectClass *klass, void *data) rc->phases.enter = pca954x_enter_reset; dc->desc = "Pca954x i2c-mux"; - dc->realize = pca954x_realize; k->write_data = pca954x_write_data; k->receive_byte = pca954x_read_byte; @@ -278,13 +234,6 @@ static const TypeInfo pca954x_info[] = { .parent = TYPE_PCA954X, .class_init = pca9548_class_init, }, - { - .name = TYPE_PCA954X_CHANNEL, - .parent = TYPE_SYS_BUS_DEVICE, - .class_init = pca954x_channel_class_init, - .instance_size = sizeof(Pca954xChannel), - .instance_init = pca954x_channel_init, - } }; DEFINE_TYPES(pca954x_info) diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index 24f8f522d9..62885fa6a1 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -28,6 +28,24 @@ uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value) return x; } +uint16_t pmbus_data2linear_mode(uint16_t value, int exp) +{ + /* L = D * 2^(-e) */ + if (exp < 0) { + return value << (-exp); + } + return value >> exp; +} + +uint16_t pmbus_linear_mode2data(uint16_t value, int exp) +{ + /* D = L * 2^e */ + if (exp < 0) { + return value >> (-exp); + } + return value << exp; +} + void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len) { if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) { @@ -89,16 +107,16 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data) } -static uint64_t pmbus_receive_uint(const uint8_t *buf, uint8_t len) +static uint64_t pmbus_receive_uint(PMBusDevice *pmdev) { uint64_t ret = 0; /* Exclude command code from return value */ - buf++; - len--; + pmdev->in_buf++; + pmdev->in_buf_len--; - for (int i = len - 1; i >= 0; i--) { - ret = ret << 8 | buf[i]; + for (int i = pmdev->in_buf_len - 1; i >= 0; i--) { + ret = ret << 8 | pmdev->in_buf[i]; } return ret; } @@ -110,7 +128,7 @@ uint8_t pmbus_receive8(PMBusDevice *pmdev) "%s: length mismatch. Expected 1 byte, got %d bytes\n", __func__, pmdev->in_buf_len - 1); } - return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); + return pmbus_receive_uint(pmdev); } uint16_t pmbus_receive16(PMBusDevice *pmdev) @@ -120,7 +138,7 @@ uint16_t pmbus_receive16(PMBusDevice *pmdev) "%s: length mismatch. Expected 2 bytes, got %d bytes\n", __func__, pmdev->in_buf_len - 1); } - return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); + return pmbus_receive_uint(pmdev); } uint32_t pmbus_receive32(PMBusDevice *pmdev) @@ -130,7 +148,7 @@ uint32_t pmbus_receive32(PMBusDevice *pmdev) "%s: length mismatch. Expected 4 bytes, got %d bytes\n", __func__, pmdev->in_buf_len - 1); } - return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); + return pmbus_receive_uint(pmdev); } uint64_t pmbus_receive64(PMBusDevice *pmdev) @@ -140,7 +158,7 @@ uint64_t pmbus_receive64(PMBusDevice *pmdev) "%s: length mismatch. Expected 8 bytes, got %d bytes\n", __func__, pmdev->in_buf_len - 1); } - return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); + return pmbus_receive_uint(pmdev); } static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev) @@ -149,7 +167,7 @@ static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev) qemu_log_mask(LOG_GUEST_ERROR, "%s: tried to read from empty buffer", __func__); - return 0xFF; + return PMBUS_ERR_BYTE; } uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1]; pmdev->out_buf_len--; @@ -243,18 +261,47 @@ void pmbus_check_limits(PMBusDevice *pmdev) } } +/* assert the status_cml error upon receipt of malformed command */ +static void pmbus_cml_error(PMBusDevice *pmdev) +{ + for (int i = 0; i < pmdev->num_pages; i++) { + pmdev->pages[i].status_word |= PMBUS_STATUS_CML; + pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD; + } +} + static uint8_t pmbus_receive_byte(SMBusDevice *smd) { PMBusDevice *pmdev = PMBUS_DEVICE(smd); PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); - uint8_t ret = 0xFF; - uint8_t index = pmdev->page; + uint8_t ret = PMBUS_ERR_BYTE; + uint8_t index; if (pmdev->out_buf_len != 0) { ret = pmbus_out_buf_pop(pmdev); return ret; } + /* + * Reading from all pages will return the value from page 0, + * this is unspecified behaviour in general. + */ + if (pmdev->page == PB_ALL_PAGES) { + index = 0; + qemu_log_mask(LOG_GUEST_ERROR, + "%s: tried to read from all pages\n", + __func__); + pmbus_cml_error(pmdev); + } else if (pmdev->page > pmdev->num_pages - 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: page %d is out of range\n", + __func__, pmdev->page); + pmbus_cml_error(pmdev); + return PMBUS_ERR_BYTE; + } else { + index = pmdev->page; + } + switch (pmdev->code) { case PMBUS_PAGE: pmbus_send8(pmdev, pmdev->page); @@ -278,6 +325,11 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) case PMBUS_CAPABILITY: pmbus_send8(pmdev, pmdev->capability); + if (pmdev->capability & BIT(7)) { + qemu_log_mask(LOG_UNIMP, + "%s: PEC is enabled but not yet supported.\n", + __func__); + } break; case PMBUS_VOUT_MODE: /* R/W byte */ @@ -368,6 +420,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_VOUT_MIN: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].vout_min); + } else { + goto passthough; + } + break; + /* TODO: implement coefficients support */ case PMBUS_POUT_MAX: /* R/W word */ @@ -708,6 +768,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) pmbus_send8(pmdev, pmdev->pages[index].status_other); break; + case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific); + break; + case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ if (pmdev->pages[index].page_flags & PB_HAS_EIN) { pmbus_send(pmdev, pmdev->pages[index].read_ein, 5); @@ -1007,7 +1071,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) if (len == 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); - return -1; + return PMBUS_ERR_BYTE; } if (!pmdev->pages) { /* allocate memory for pages on first use */ @@ -1026,6 +1090,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) pmdev->page = pmbus_receive8(pmdev); return 0; } + /* loop through all the pages when 0xFF is received */ if (pmdev->page == PB_ALL_PAGES) { for (int i = 0; i < pmdev->num_pages; i++) { @@ -1036,6 +1101,15 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) return 0; } + if (pmdev->page > pmdev->num_pages - 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: page %u is out of range\n", + __func__, pmdev->page); + pmdev->page = 0; /* undefined behaviour - reset to page 0 */ + pmbus_cml_error(pmdev); + return PMBUS_ERR_BYTE; + } + index = pmdev->page; switch (pmdev->code) { @@ -1149,6 +1223,14 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) } break; + case PMBUS_VOUT_MIN: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { + pmdev->pages[index].vout_min = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + case PMBUS_POUT_MAX: /* R/W word */ if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { pmdev->pages[index].pout_max = pmbus_receive16(pmdev); @@ -1482,6 +1564,10 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) pmdev->pages[index].status_other = pmbus_receive8(pmdev); break; + case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */ + pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev); + break; + case PMBUS_PAGE_PLUS_READ: /* Block Read-only */ case PMBUS_CAPABILITY: /* Read-Only byte */ case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */ diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index ebd47aa26f..dcf6ece3d0 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -38,6 +38,7 @@ #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/isa/isa.h" +#include "hw/input/i8042.h" #include "hw/block/fdc.h" #include "hw/acpi/memory_hotplug.h" #include "sysemu/tpm.h" @@ -192,6 +193,13 @@ static void init_common_fadt_data(MachineState *ms, Object *o, .address = object_property_get_uint(o, ACPI_PM_PROP_GPE0_BLK, NULL) }, }; + + /* + * ACPI v2, Table 5-10 - Fixed ACPI Description Table Boot Architecture + * Flags, bit offset 1 - 8042. + */ + fadt.iapc_boot_arch = iapc_boot_arch_8042(); + *data = fadt; } @@ -575,7 +583,7 @@ Aml *aml_pci_device_dsm(void) Aml *bnum = aml_arg(4); Aml *func = aml_arg(2); Aml *rev = aml_arg(1); - Aml *sun = aml_arg(5); + Aml *sunum = aml_arg(5); method = aml_method("PDSM", 6, AML_SERIALIZED); @@ -586,7 +594,7 @@ Aml *aml_pci_device_dsm(void) UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); ifctx = aml_if(aml_equal(aml_arg(0), UUID)); { - aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sun), acpi_index)); + aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); ifctx1 = aml_if(aml_equal(func, zero)); { uint8_t byte_list[1]; diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index 68ca7e7fc2..fb09185cbd 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -37,6 +37,7 @@ #include "hw/pci/pcie_host.h" #include "hw/usb/xhci.h" #include "hw/virtio/virtio-mmio.h" +#include "hw/input/i8042.h" #include "acpi-common.h" #include "acpi-microvm.h" @@ -187,6 +188,11 @@ static void acpi_build_microvm(AcpiBuildTables *tables, .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_RESET, }, .reset_val = ACPI_GED_RESET_VALUE, + /* + * ACPI v2, Table 5-10 - Fixed ACPI Description Table Boot Architecture + * Flags, bit offset 1 - 8042. + */ + .iapc_boot_arch = iapc_boot_arch_8042(), }; table_offsets = g_array_new(false, true /* clear */, diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 4d13d8e697..ea8eaeb330 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -913,7 +913,7 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, } /* we are at the leaf page table or page table encodes a huge page */ - while (level > 0) { + do { pte_perms = amdvi_get_perms(pte); present = pte & 1; if (!present || perms != (perms & pte_perms)) { @@ -932,10 +932,7 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, } oldlevel = level; level = get_pte_translation_mode(pte); - if (level == 0x7) { - break; - } - } + } while (level > 0 && level < 7); if (level == 0x7) { page_mask = pte_override_page_mask(pte); @@ -1405,7 +1402,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) /* allocate memory during the first run */ if (!iommu_as) { - iommu_as = g_malloc0(sizeof(AMDVIAddressSpace *) * PCI_DEVFN_MAX); + iommu_as = g_new0(AMDVIAddressSpace *, PCI_DEVFN_MAX); s->address_spaces[bus_num] = iommu_as; } @@ -1413,7 +1410,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) if (!iommu_as[devfn]) { snprintf(name, sizeof(name), "amd_iommu_devfn_%d", devfn); - iommu_as[devfn] = g_malloc0(sizeof(AMDVIAddressSpace)); + iommu_as[devfn] = g_new0(AMDVIAddressSpace, 1); iommu_as[devfn]->bus_num = (uint8_t)bus_num; iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->iommu_state = s; diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 4c6c016388..c64aa81a83 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3030,6 +3030,13 @@ static int vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; + /* TODO: add support for VFIO and vhost users */ + if (s->snoop_control) { + error_setg_errno(errp, -ENOTSUP, + "Snoop Control with vhost or VFIO is not supported"); + return -ENOTSUP; + } + /* Update per-address-space notifier flags */ vtd_as->notifier_flags = new; @@ -3113,6 +3120,7 @@ static Property vtd_properties[] = { VTD_HOST_ADDRESS_WIDTH), DEFINE_PROP_BOOL("caching-mode", IntelIOMMUState, caching_mode, FALSE), DEFINE_PROP_BOOL("x-scalable-mode", IntelIOMMUState, scalable_mode, FALSE), + DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false), DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), DEFINE_PROP_END_OF_LIST(), }; @@ -3408,7 +3416,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) if (!vtd_dev_as) { snprintf(name, sizeof(name), "vtd-%02x.%x", PCI_SLOT(devfn), PCI_FUNC(devfn)); - vtd_bus->dev_as[devfn] = vtd_dev_as = g_malloc0(sizeof(VTDAddressSpace)); + vtd_bus->dev_as[devfn] = vtd_dev_as = g_new0(VTDAddressSpace, 1); vtd_dev_as->bus = bus; vtd_dev_as->devfn = (uint8_t)devfn; @@ -3643,7 +3651,7 @@ static void vtd_init(IntelIOMMUState *s) vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits, x86_iommu->dt_supported); - if (s->scalable_mode) { + if (s->scalable_mode || s->snoop_control) { vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP; vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP; vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP; @@ -3674,6 +3682,10 @@ static void vtd_init(IntelIOMMUState *s) s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_SRS | VTD_ECAP_SLTS; } + if (s->snoop_control) { + s->ecap |= VTD_ECAP_SC; + } + vtd_reset_caches(s); /* Define registers with default values and bit semantics */ diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index a6c788049b..1ff13b40f9 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -188,6 +188,7 @@ #define VTD_ECAP_IR (1ULL << 3) #define VTD_ECAP_EIM (1ULL << 4) #define VTD_ECAP_PT (1ULL << 6) +#define VTD_ECAP_SC (1ULL << 7) #define VTD_ECAP_MHMV (15ULL << 20) #define VTD_ECAP_SRS (1ULL << 31) #define VTD_ECAP_SMTS (1ULL << 43) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index c8696ac01e..23bba9d82c 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -95,6 +95,9 @@ #include "trace.h" #include CONFIG_DEVICES +GlobalProperty pc_compat_7_0[] = {}; +const size_t pc_compat_7_0_len = G_N_ELEMENTS(pc_compat_7_0); + GlobalProperty pc_compat_6_2[] = { { "virtio-mem", "unplugged-inaccessible", "off" }, }; @@ -318,8 +321,6 @@ GlobalProperty pc_compat_2_0[] = { { "pci-serial-4x", "prog_if", "0" }, { "virtio-net-pci", "guest_announce", "off" }, { "ICH9-LPC", "memory-hotplug-support", "off" }, - { "xio3130-downstream", COMPAT_PROP_PCP, "off" }, - { "ioh3420", COMPAT_PROP_PCP, "off" }, }; const size_t pc_compat_2_0_len = G_N_ELEMENTS(pc_compat_2_0); @@ -1014,7 +1015,8 @@ static const MemoryRegionOps ioportF0_io_ops = { }, }; -static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) +static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, + bool create_i8042, bool no_vmport) { int i; DriveInfo *fd[MAX_FD]; @@ -1036,6 +1038,10 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) } } + if (!create_i8042) { + return; + } + i8042 = isa_create_simple(isa_bus, "i8042"); if (!no_vmport) { isa_create_simple(isa_bus, TYPE_VMPORT); @@ -1131,7 +1137,8 @@ void pc_basic_device_init(struct PCMachineState *pcms, i8257_dma_init(isa_bus, 0); /* Super I/O */ - pc_superio_init(isa_bus, create_fdctrl, pcms->vmport != ON_OFF_AUTO_ON); + pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled, + pcms->vmport != ON_OFF_AUTO_ON); } void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) @@ -1512,6 +1519,20 @@ static void pc_machine_set_hpet(Object *obj, bool value, Error **errp) pcms->hpet_enabled = value; } +static bool pc_machine_get_i8042(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + return pcms->i8042_enabled; +} + +static void pc_machine_set_i8042(Object *obj, bool value, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + pcms->i8042_enabled = value; +} + static bool pc_machine_get_default_bus_bypass_iommu(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); @@ -1641,6 +1662,7 @@ static void pc_machine_initfn(Object *obj) pcms->smbus_enabled = true; pcms->sata_enabled = true; pcms->pit_enabled = true; + pcms->i8042_enabled = true; pcms->max_fw_size = 8 * MiB; #ifdef CONFIG_HPET pcms->hpet_enabled = true; @@ -1777,6 +1799,9 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "hpet", "Enable/disable high precision event timer emulation"); + object_class_property_add_bool(oc, PC_MACHINE_I8042, + pc_machine_get_i8042, pc_machine_set_i8042); + object_class_property_add_bool(oc, "default-bus-bypass-iommu", pc_machine_get_default_bus_bypass_iommu, pc_machine_set_default_bus_bypass_iommu); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8d33cf689d..4c185c72d0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -415,7 +415,7 @@ static void pc_i440fx_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); } -static void pc_i440fx_7_0_machine_options(MachineClass *m) +static void pc_i440fx_7_1_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_machine_options(m); @@ -424,6 +424,18 @@ static void pc_i440fx_7_0_machine_options(MachineClass *m) pcmc->default_cpu_version = 1; } +DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", NULL, + pc_i440fx_7_1_machine_options); + +static void pc_i440fx_7_0_machine_options(MachineClass *m) +{ + pc_i440fx_7_1_machine_options(m); + m->alias = NULL; + m->is_default = false; + compat_props_add(m->compat_props, hw_compat_7_0, hw_compat_7_0_len); + compat_props_add(m->compat_props, pc_compat_7_0, pc_compat_7_0_len); +} + DEFINE_I440FX_MACHINE(v7_0, "pc-i440fx-7.0", NULL, pc_i440fx_7_0_machine_options); @@ -757,6 +769,7 @@ static void pc_i440fx_1_7_machine_options(MachineClass *m) m->hw_version = "1.7.0"; m->default_machine_opts = NULL; m->option_rom_has_mr = true; + m->deprecation_reason = "old and unattended - use a newer version instead"; compat_props_add(m->compat_props, pc_compat_1_7, pc_compat_1_7_len); pcmc->smbios_defaults = false; pcmc->gigabyte_align = false; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 1780f79bc1..302288342a 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -360,7 +360,7 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_7_0_machine_options(MachineClass *m) +static void pc_q35_7_1_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_options(m); @@ -368,6 +368,17 @@ static void pc_q35_7_0_machine_options(MachineClass *m) pcmc->default_cpu_version = 1; } +DEFINE_Q35_MACHINE(v7_1, "pc-q35-7.1", NULL, + pc_q35_7_1_machine_options); + +static void pc_q35_7_0_machine_options(MachineClass *m) +{ + pc_q35_7_1_machine_options(m); + m->alias = NULL; + compat_props_add(m->compat_props, hw_compat_7_0, hw_compat_7_0_len); + compat_props_add(m->compat_props, pc_compat_7_0, pc_compat_7_0_len); +} + DEFINE_Q35_MACHINE(v7_0, "pc-q35-7.0", NULL, pc_q35_7_0_machine_options); diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index c8b17af953..c8d9e71b88 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "sysemu/block-backend.h" #include "qemu/error-report.h" @@ -148,7 +147,6 @@ static void pc_system_flash_map(PCMachineState *pcms, MemoryRegion *flash_mem; void *flash_ptr; int flash_size; - int ret; assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); @@ -196,19 +194,7 @@ static void pc_system_flash_map(PCMachineState *pcms, if (sev_enabled()) { flash_ptr = memory_region_get_ram_ptr(flash_mem); flash_size = memory_region_size(flash_mem); - /* - * OVMF places a GUIDed structures in the flash, so - * search for them - */ - pc_system_parse_ovmf_flash(flash_ptr, flash_size); - - ret = sev_es_save_reset_vector(flash_ptr, flash_size); - if (ret) { - error_report("failed to locate and/or save reset vector"); - exit(1); - } - - sev_encrypt_flash(flash_ptr, flash_size, &error_fatal); + x86_firmware_configure(flash_ptr, flash_size); } } } @@ -260,3 +246,24 @@ void pc_system_firmware_init(PCMachineState *pcms, pc_system_flash_cleanup_unused(pcms); } + +void x86_firmware_configure(void *ptr, int size) +{ + int ret; + + /* + * OVMF places a GUIDed structures in the flash, so + * search for them + */ + pc_system_parse_ovmf_flash(ptr, size); + + if (sev_enabled()) { + ret = sev_es_save_reset_vector(ptr, size); + if (ret) { + error_report("failed to locate and/or save reset vector"); + exit(1); + } + + sev_encrypt_flash(ptr, size, &error_fatal); + } +} diff --git a/hw/i386/pc_sysfw_ovmf.c b/hw/i386/pc_sysfw_ovmf.c index f4dd92c588..07a4c267fa 100644 --- a/hw/i386/pc_sysfw_ovmf.c +++ b/hw/i386/pc_sysfw_ovmf.c @@ -24,11 +24,14 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/i386/pc.h" #include "cpu.h" #define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d" +static const int bytes_after_table_footer = 32; + static bool ovmf_flash_parsed; static uint8_t *ovmf_table; static int ovmf_table_len; @@ -52,12 +55,13 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size) /* * if this is OVMF there will be a table footer - * guid 48 bytes before the end of the flash file. If it's - * not found, silently abort the flash parsing. + * guid 48 bytes before the end of the flash file + * (= 32 bytes after the table + 16 bytes the GUID itself). + * If it's not found, silently abort the flash parsing. */ qemu_uuid_parse(OVMF_TABLE_FOOTER_GUID, &guid); guid = qemu_uuid_bswap(guid); /* guids are LE */ - ptr = flash_ptr + flash_size - 48; + ptr = flash_ptr + flash_size - (bytes_after_table_footer + sizeof(guid)); if (!qemu_uuid_is_equal((QemuUUID *)ptr, &guid)) { return; } @@ -66,7 +70,13 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size) ptr -= sizeof(uint16_t); tot_len = le16_to_cpu(*(uint16_t *)ptr) - sizeof(guid) - sizeof(uint16_t); - if (tot_len <= 0) { + if (tot_len < 0 || tot_len > (ptr - flash_ptr)) { + error_report("OVMF table has invalid size %d", tot_len); + return; + } + + if (tot_len == 0) { + /* no entries in the OVMF table */ return; } diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index a2b318dd93..a44d66ba2a 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -315,7 +315,7 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) } if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) { - error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap", + error_report("Size of all 'sgx-epc' =0x%"PRIx64" causes EPC to wrap", sgx_epc->size); exit(EXIT_FAILURE); } diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 5bf7e52bf5..e49814dd64 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -33,15 +33,15 @@ vtd_fault_disabled(void) "Fault processing disabled for context entry" vtd_replay_ce_valid(const char *mode, uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "%s: replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 -vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIx16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" vtd_page_walk_one_skip_map(uint64_t iova, uint64_t mask, uint64_t translated) "iova 0x%"PRIx64" mask 0x%"PRIx64" translated 0x%"PRIx64 vtd_page_walk_one_skip_unmap(uint64_t iova, uint64_t mask) "iova 0x%"PRIx64" mask 0x%"PRIx64 vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 -vtd_translate_pt(uint16_t sid, uint64_t addr) "source id 0x%"PRIu16", iova 0x%"PRIx64 -vtd_pt_enable_fast_path(uint16_t sid, bool success) "sid 0x%"PRIu16" %d" +vtd_translate_pt(uint16_t sid, uint64_t addr) "source id 0x%"PRIx16", iova 0x%"PRIx64 +vtd_pt_enable_fast_path(uint16_t sid, bool success) "sid 0x%"PRIx16" %d" vtd_irq_generate(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PRIx64 vtd_reg_read(uint64_t addr, uint64_t size) "addr 0x%"PRIx64" size 0x%"PRIx64 vtd_reg_write(uint64_t addr, uint64_t size, uint64_t val) "addr 0x%"PRIx64" size 0x%"PRIx64" value 0x%"PRIx64 diff --git a/hw/i386/x86.c b/hw/i386/x86.c index b84840a1bb..79ebdface6 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -25,7 +25,6 @@ #include "qemu/option.h" #include "qemu/cutils.h" #include "qemu/units.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" @@ -83,24 +82,11 @@ inline void init_topo_info(X86CPUTopoInfo *topo_info, uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, unsigned int cpu_index) { - X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms); X86CPUTopoInfo topo_info; - uint32_t correct_id; - static bool warned; init_topo_info(&topo_info, x86ms); - correct_id = x86_apicid_from_cpu_idx(&topo_info, cpu_index); - if (x86mc->compat_apic_id_mode) { - if (cpu_index != correct_id && !warned && !qtest_enabled()) { - error_report("APIC IDs set in compatibility mode, " - "CPU topology won't match the configuration"); - warned = true; - } - return cpu_index; - } else { - return correct_id; - } + return x86_apicid_from_cpu_idx(&topo_info, cpu_index); } @@ -1129,14 +1115,25 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware, } bios = g_malloc(sizeof(*bios)); memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); - if (!isapc_ram_fw) { - memory_region_set_readonly(bios, true); - } - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); - if (ret != 0) { - bios_error: - fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); - exit(1); + if (sev_enabled()) { + /* + * The concept of a "reset" simply doesn't exist for + * confidential computing guests, we have to destroy and + * re-launch them instead. So there is no need to register + * the firmware as rom to properly re-initialize on reset. + * Just go for a straight file load instead. + */ + void *ptr = memory_region_get_ram_ptr(bios); + load_image_size(filename, ptr, bios_size); + x86_firmware_configure(ptr, bios_size); + } else { + if (!isapc_ram_fw) { + memory_region_set_readonly(bios, true); + } + ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); + if (ret != 0) { + goto bios_error; + } } g_free(filename); @@ -1157,6 +1154,11 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware, memory_region_add_subregion(rom_memory, (uint32_t)(-bios_size), bios); + return; + +bios_error: + fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); + exit(1); } bool x86_machine_is_smm_enabled(const X86MachineState *x86ms) @@ -1330,7 +1332,6 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = x86_cpu_index_to_props; mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; - x86mc->compat_apic_id_mode = false; x86mc->save_tsc_khz = true; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index cf8e500514..0731f70410 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -396,7 +396,7 @@ go_physmap: mr_name = memory_region_name(mr); - physmap = g_malloc(sizeof(XenPhysmap)); + physmap = g_new(XenPhysmap, 1); physmap->start_addr = start_addr; physmap->size = size; @@ -1281,7 +1281,7 @@ static void xen_read_physmap(XenIOState *state) return; for (i = 0; i < num; i++) { - physmap = g_malloc(sizeof (XenPhysmap)); + physmap = g_new(XenPhysmap, 1); physmap->phys_offset = strtoull(entries[i], NULL, 16); snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/physmap/%s/start_addr", @@ -1410,7 +1410,7 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) xen_pfn_t ioreq_pfn; XenIOState *state; - state = g_malloc0(sizeof (XenIOState)); + state = g_new0(XenIOState, 1); state->xce_handle = xenevtchn_open(NULL, 0); if (state->xce_handle == NULL) { @@ -1463,7 +1463,7 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) } /* Note: cpus is empty at this point in init */ - state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); + state->cpu_by_vcpu_id = g_new0(CPUState *, max_cpus); rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true); if (rc < 0) { @@ -1472,7 +1472,7 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - state->ioreq_local_port = g_malloc0(max_cpus * sizeof (evtchn_port_t)); + state->ioreq_local_port = g_new0(evtchn_port_t, max_cpus); /* FIXME: how about if we overflow the page here? */ for (i = 0; i < max_cpus; i++) { diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index f2ef977963..a2f93096e7 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -108,7 +108,7 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) unsigned long size; struct rlimit rlimit_as; - mapcache = g_malloc0(sizeof (MapCache)); + mapcache = g_new0(MapCache, 1); mapcache->phys_offset_to_gaddr = f; mapcache->opaque = opaque; @@ -164,8 +164,8 @@ static void xen_remap_bucket(MapCacheEntry *entry, trace_xen_remap_bucket(address_index); - pfns = g_malloc0(nb_pfn * sizeof (xen_pfn_t)); - err = g_malloc0(nb_pfn * sizeof (int)); + pfns = g_new0(xen_pfn_t, nb_pfn); + err = g_new0(int, nb_pfn); if (entry->vaddr_base != NULL) { if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { @@ -231,8 +231,8 @@ static void xen_remap_bucket(MapCacheEntry *entry, entry->vaddr_base = vaddr_base; entry->paddr_index = address_index; entry->size = size; - entry->valid_mapping = (unsigned long *) g_malloc0(sizeof(unsigned long) * - BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); + entry->valid_mapping = g_new0(unsigned long, + BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); if (dummy) { entry->flags |= XEN_MAPCACHE_ENTRY_DUMMY; @@ -319,7 +319,7 @@ tryagain: pentry = free_pentry; } if (!entry) { - entry = g_malloc0(sizeof (MapCacheEntry)); + entry = g_new0(MapCacheEntry, 1); pentry->next = entry; xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); } else if (!entry->lock) { @@ -353,7 +353,7 @@ tryagain: mapcache->last_entry = entry; if (lock) { - MapCacheRev *reventry = g_malloc0(sizeof(MapCacheRev)); + MapCacheRev *reventry = g_new0(MapCacheRev, 1); entry->lock++; if (entry->lock == 0) { fprintf(stderr, diff --git a/hw/ide/core.c b/hw/ide/core.c index 33463d9b8f..3a5afff5d7 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -30,6 +30,7 @@ #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/hw-version.h" +#include "qemu/memalign.h" #include "sysemu/sysemu.h" #include "sysemu/blockdev.h" #include "sysemu/dma.h" @@ -434,12 +435,16 @@ static const AIOCBInfo trim_aiocb_info = { static void ide_trim_bh_cb(void *opaque) { TrimAIOCB *iocb = opaque; + BlockBackend *blk = iocb->s->blk; iocb->common.cb(iocb->common.opaque, iocb->ret); qemu_bh_delete(iocb->bh); iocb->bh = NULL; qemu_aio_unref(iocb); + + /* Paired with an increment in ide_issue_trim() */ + blk_dec_in_flight(blk); } static void ide_issue_trim_cb(void *opaque, int ret) @@ -509,6 +514,9 @@ BlockAIOCB *ide_issue_trim( IDEState *s = opaque; TrimAIOCB *iocb; + /* Paired with a decrement in ide_trim_bh_cb() */ + blk_inc_in_flight(s->blk); + iocb = blk_aio_get(&trim_aiocb_info, s->blk, cb, cb_opaque); iocb->s = s; iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 24bbde24c2..8bedbd13f1 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -75,7 +75,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); - isa_init_irq(isadev, &s->irq, s->isairq); + s->irq = isa_get_irq(isadev, s->isairq); ide_init2(&s->bus, s->irq); vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); ide_register_restart_cb(&s->bus); diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c index 68d741d342..94f18be4cd 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -266,7 +266,7 @@ void lasips2_init(MemoryRegion *address_space, { LASIPS2State *s; - s = g_malloc0(sizeof(LASIPS2State)); + s = g_new0(LASIPS2State, 1); s->irq = irq; s->mouse.id = 1; diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index baba62f357..4efdf75620 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -26,6 +26,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/timer.h" +#include "qapi/error.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" #include "hw/acpi/aml-build.h" @@ -648,7 +649,7 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, MemoryRegion *region, ram_addr_t size, hwaddr mask) { - KBDState *s = g_malloc0(sizeof(KBDState)); + KBDState *s = g_new0(KBDState, 1); s->irq_kbd = kbd_irq; s->irq_mouse = mouse_irq; @@ -671,6 +672,8 @@ struct ISAKBDState { KBDState kbd; bool kbd_throttle; MemoryRegion io[2]; + uint8_t kbd_irq; + uint8_t mouse_irq; }; void i8042_isa_mouse_fake_event(ISAKBDState *isa) @@ -734,8 +737,20 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) ISAKBDState *isa_s = I8042(dev); KBDState *s = &isa_s->kbd; - isa_init_irq(isadev, &s->irq_kbd, 1); - isa_init_irq(isadev, &s->irq_mouse, 12); + if (isa_s->kbd_irq >= ISA_NUM_IRQS) { + error_setg(errp, "Maximum value for \"kbd-irq\" is: %u", + ISA_NUM_IRQS - 1); + return; + } + + if (isa_s->mouse_irq >= ISA_NUM_IRQS) { + error_setg(errp, "Maximum value for \"mouse-irq\" is: %u", + ISA_NUM_IRQS - 1); + return; + } + + s->irq_kbd = isa_get_irq(isadev, isa_s->kbd_irq); + s->irq_mouse = isa_get_irq(isadev, isa_s->mouse_irq); isa_register_ioport(isadev, isa_s->io + 0, 0x60); isa_register_ioport(isadev, isa_s->io + 1, 0x64); @@ -754,6 +769,7 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) static void i8042_build_aml(ISADevice *isadev, Aml *scope) { + ISAKBDState *isa_s = I8042(isadev); Aml *kbd; Aml *mou; Aml *crs; @@ -761,7 +777,7 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope) crs = aml_resource_template(); aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01)); aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01)); - aml_append(crs, aml_irq_no_flags(1)); + aml_append(crs, aml_irq_no_flags(isa_s->kbd_irq)); kbd = aml_device("KBD"); aml_append(kbd, aml_name_decl("_HID", aml_eisaid("PNP0303"))); @@ -769,7 +785,7 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope) aml_append(kbd, aml_name_decl("_CRS", crs)); crs = aml_resource_template(); - aml_append(crs, aml_irq_no_flags(12)); + aml_append(crs, aml_irq_no_flags(isa_s->mouse_irq)); mou = aml_device("MOU"); aml_append(mou, aml_name_decl("_HID", aml_eisaid("PNP0F13"))); @@ -783,6 +799,8 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope) static Property i8042_properties[] = { DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true), DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false), + DEFINE_PROP_UINT8("kbd-irq", ISAKBDState, kbd_irq, 1), + DEFINE_PROP_UINT8("mouse-irq", ISAKBDState, mouse_irq, 12), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 6236711e1b..c16df1de7a 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -1226,7 +1226,7 @@ static QemuInputHandler ps2_keyboard_handler = { void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg) { - PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState)); + PS2KbdState *s = g_new0(PS2KbdState, 1); trace_ps2_kbd_init(s); s->common.update_irq = update_irq; @@ -1248,7 +1248,7 @@ static QemuInputHandler ps2_mouse_handler = { void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) { - PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState)); + PS2MouseState *s = g_new0(PS2MouseState, 1); trace_ps2_mouse_init(s); s->common.update_irq = update_irq; diff --git a/hw/input/pxa2xx_keypad.c b/hw/input/pxa2xx_keypad.c index 7f2f739fb3..3dd03e8c9f 100644 --- a/hw/input/pxa2xx_keypad.c +++ b/hw/input/pxa2xx_keypad.c @@ -306,7 +306,7 @@ PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem, { PXA2xxKeyPadState *s; - s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState)); + s = g_new0(PXA2xxKeyPadState, 1); s->irq = irq; memory_region_init_io(&s->iomem, NULL, &pxa2xx_keypad_ops, s, diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c index 55d61cc843..14698ce109 100644 --- a/hw/input/tsc2005.c +++ b/hw/input/tsc2005.c @@ -489,8 +489,7 @@ void *tsc2005_init(qemu_irq pintdav) { TSC2005State *s; - s = (TSC2005State *) - g_malloc0(sizeof(TSC2005State)); + s = g_new0(TSC2005State, 1); s->x = 400; s->y = 240; s->pressure = false; diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c index b0d5c2dd74..df7313db5d 100644 --- a/hw/input/tsc210x.c +++ b/hw/input/tsc210x.c @@ -24,6 +24,7 @@ #include "hw/hw.h" #include "audio/audio.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "sysemu/reset.h" #include "ui/console.h" #include "hw/arm/omap.h" /* For I2SCodec */ @@ -910,8 +911,11 @@ uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len) TSC210xState *s = opaque; uint32_t ret = 0; - if (len != 16) - hw_error("%s: FIXME: bad SPI word width %i\n", __func__, len); + if (len != 16) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad SPI word width %i\n", __func__, len); + return 0; + } /* TODO: sequential reads etc - how do we make sure the host doesn't * unintentionally read out a conversion result from a register while diff --git a/hw/input/vhost-user-input.c b/hw/input/vhost-user-input.c index 273e96a7b1..aeb0624fe5 100644 --- a/hw/input/vhost-user-input.c +++ b/hw/input/vhost-user-input.c @@ -7,7 +7,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qemu-common.h" #include "hw/virtio/virtio-input.h" diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index 137efba57b..fea7139382 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -114,7 +114,10 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp) error_setg_file_open(errp, errno, vih->evdev); return; } - qemu_set_nonblock(vih->fd); + if (!g_unix_set_fd_nonblocking(vih->fd, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + goto err_close; + } rc = ioctl(vih->fd, EVIOCGVERSION, &ver); if (rc < 0) { diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 528e77b4a6..eded1b557e 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -25,7 +25,7 @@ config APIC select MSI_NONBROKEN select I8259 -config ARM_GIC_TCG +config ARM_GICV3_TCG bool default y depends on ARM_GIC && TCG @@ -73,6 +73,9 @@ config RISCV_ACLINT config RISCV_APLIC bool +config RISCV_IMSIC + bool + config SIFIVE_PLIC bool @@ -81,3 +84,6 @@ config GOLDFISH_PIC config M68K_IRQC bool + +config NIOS2_VIC + bool diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 6d3c8ee231..0b8f79a122 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -369,11 +369,19 @@ static const MemoryRegionOps gic_ops[] = { .read_with_attrs = gicv3_dist_read, .write_with_attrs = gicv3_dist_write, .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, }, { .read_with_attrs = gicv3_redist_read, .write_with_attrs = gicv3_redist_write, .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, } }; diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 4ca5ae9bc5..5634c6fc78 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -144,6 +144,25 @@ const VMStateDescription vmstate_gicv3_cpu_sre_el1 = { } }; +static bool gicv4_needed(void *opaque) +{ + GICv3CPUState *cs = opaque; + + return cs->gic->revision > 3; +} + +const VMStateDescription vmstate_gicv3_gicv4 = { + .name = "arm_gicv3_cpu/gicv4", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv4_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(gicr_vpropbaser, GICv3CPUState), + VMSTATE_UINT64(gicr_vpendbaser, GICv3CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3_cpu = { .name = "arm_gicv3_cpu", .version_id = 1, @@ -175,6 +194,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { .subsections = (const VMStateDescription * []) { &vmstate_gicv3_cpu_virt, &vmstate_gicv3_cpu_sre_el1, + &vmstate_gicv3_gicv4, NULL } }; @@ -295,7 +315,7 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, memory_region_init_io(®ion->iomem, OBJECT(s), ops ? &ops[1] : NULL, region, name, - s->redist_region_count[i] * GICV3_REDIST_SIZE); + s->redist_region_count[i] * gicv3_redist_size(s)); sysbus_init_mmio(sbd, ®ion->iomem); g_free(name); } @@ -306,12 +326,14 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) GICv3State *s = ARM_GICV3_COMMON(dev); int i, rdist_capacity, cpuidx; - /* revision property is actually reserved and currently used only in order - * to keep the interface compatible with GICv2 code, avoiding extra - * conditions. However, in future it could be used, for example, if we - * implement GICv4. + /* + * This GIC device supports only revisions 3 and 4. The GICv1/v2 + * is a separate device. + * Note that subclasses of this device may impose further restrictions + * on the GIC revision: notably, the in-kernel KVM GIC doesn't + * support GICv4. */ - if (s->revision != 3) { + if (s->revision != 3 && s->revision != 4) { error_setg(errp, "unsupported GIC revision %d", s->revision); return; } @@ -328,6 +350,10 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) s->num_irq, GIC_INTERNAL); return; } + if (s->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } /* ITLinesNumber is represented as (N / 32) - 1, so this is an * implementation imposed restriction, not an architectural one, @@ -350,9 +376,9 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->nb_redist_regions; i++) { rdist_capacity += s->redist_region_count[i]; } - if (rdist_capacity < s->num_cpu) { + if (rdist_capacity != s->num_cpu) { error_setg(errp, "Capacity of the redist regions(%d) " - "is less than number of vcpus(%d)", + "does not match the number of vcpus(%d)", rdist_capacity, s->num_cpu); return; } @@ -382,8 +408,8 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) * Last == 1 if this is the last redistributor in a series of * contiguous redistributor pages * DirectLPI == 0 (direct injection of LPIs not supported) - * VLPIS == 0 (virtual LPIs not supported) - * PLPIS == 0 (physical LPIs not supported) + * VLPIS == 1 if vLPIs supported (GICv4 and up) + * PLPIS == 1 if LPIs supported */ cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL); @@ -398,6 +424,9 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) if (s->lpi_enable) { s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS; + if (s->revision > 3) { + s->cpu[i].gicr_typer |= GICR_TYPER_VLPIS; + } } } @@ -410,6 +439,8 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) cpuidx += s->redist_region_count[i]; s->cpu[cpuidx - 1].gicr_typer |= GICR_TYPER_LAST; } + + s->itslist = g_ptr_array_new(); } static void arm_gicv3_finalize(Object *obj) @@ -438,6 +469,8 @@ static void arm_gicv3_common_reset(DeviceState *dev) cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep; cs->gicr_propbaser = 0; cs->gicr_pendbaser = 0; + cs->gicr_vpropbaser = 0; + cs->gicr_vpendbaser = 0; /* If we're resetting a TZ-aware GIC as if secure firmware * had set it up ready to start a kernel in non-secure, we * need to set interrupts to group 1 so the kernel can use them. @@ -459,6 +492,7 @@ static void arm_gicv3_common_reset(DeviceState *dev) cs->hppi.prio = 0xff; cs->hpplpi.prio = 0xff; + cs->hppvlpi.prio = 0xff; /* State in the CPU interface must *not* be reset here, because it * is part of the CPU's reset domain, not the GIC device's. diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index d7e03d0cab..8404f46ee0 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -21,6 +21,12 @@ #include "hw/irq.h" #include "cpu.h" +/* + * Special case return value from hppvi_index(); must be larger than + * the architecturally maximum possible list register index (which is 15) + */ +#define HPPVI_INDEX_VLPI 16 + static GICv3CPUState *icc_cs_from_env(CPUARMState *env) { return env->gicv3state; @@ -157,10 +163,18 @@ static int ich_highest_active_virt_prio(GICv3CPUState *cs) static int hppvi_index(GICv3CPUState *cs) { - /* Return the list register index of the highest priority pending + /* + * Return the list register index of the highest priority pending * virtual interrupt, as per the HighestPriorityVirtualInterrupt * pseudocode. If no pending virtual interrupts, return -1. + * If the highest priority pending virtual interrupt is a vLPI, + * return HPPVI_INDEX_VLPI. + * (The pseudocode handles checking whether the vLPI is higher + * priority than the highest priority list register at every + * callsite of HighestPriorityVirtualInterrupt; we check it here.) */ + ARMCPU *cpu = ARM_CPU(cs->cpu); + CPUARMState *env = &cpu->env; int idx = -1; int i; /* Note that a list register entry with a priority of 0xff will @@ -202,6 +216,23 @@ static int hppvi_index(GICv3CPUState *cs) } } + /* + * "no pending vLPI" is indicated with prio = 0xff, which always + * fails the priority check here. vLPIs are only considered + * when we are in Non-Secure state. + */ + if (cs->hppvlpi.prio < prio && !arm_is_secure(env)) { + if (cs->hppvlpi.grp == GICV3_G0) { + if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG0) { + return HPPVI_INDEX_VLPI; + } + } else { + if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1) { + return HPPVI_INDEX_VLPI; + } + } + } + return idx; } @@ -289,6 +320,47 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) return false; } +static bool icv_hppvlpi_can_preempt(GICv3CPUState *cs) +{ + /* + * Return true if we can signal the highest priority pending vLPI. + * We can assume we're Non-secure because hppvi_index() already + * tested for that. + */ + uint32_t mask, rprio, vpmr; + + if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) { + /* Virtual interface disabled */ + return false; + } + + vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, + ICH_VMCR_EL2_VPMR_LENGTH); + + if (cs->hppvlpi.prio >= vpmr) { + /* Priority mask masks this interrupt */ + return false; + } + + rprio = ich_highest_active_virt_prio(cs); + if (rprio == 0xff) { + /* No running interrupt so we can preempt */ + return true; + } + + mask = icv_gprio_mask(cs, cs->hppvlpi.grp); + + /* + * We only preempt a running interrupt if the pending interrupt's + * group priority is sufficient (the subpriorities are not considered). + */ + if ((cs->hppvlpi.prio & mask) < (rprio & mask)) { + return true; + } + + return false; +} + static uint32_t eoi_maintenance_interrupt_state(GICv3CPUState *cs, uint32_t *misr) { @@ -370,9 +442,55 @@ static uint32_t maintenance_interrupt_state(GICv3CPUState *cs) return value; } +void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) +{ + /* + * Tell the CPU about any pending virtual interrupts. + * This should only be called for changes that affect the + * vIRQ and vFIQ status and do not change the maintenance + * interrupt status. This means that unlike gicv3_cpuif_virt_update() + * this function won't recursively call back into the GIC code. + * The main use of this is when the redistributor has changed the + * highest priority pending virtual LPI. + */ + int idx; + int irqlevel = 0; + int fiqlevel = 0; + + idx = hppvi_index(cs); + trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx, + cs->hppvlpi.irq, cs->hppvlpi.grp, + cs->hppvlpi.prio); + if (idx == HPPVI_INDEX_VLPI) { + if (icv_hppvlpi_can_preempt(cs)) { + if (cs->hppvlpi.grp == GICV3_G0) { + fiqlevel = 1; + } else { + irqlevel = 1; + } + } + } else if (idx >= 0) { + uint64_t lr = cs->ich_lr_el2[idx]; + + if (icv_hppi_can_preempt(cs, lr)) { + /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */ + if (lr & ICH_LR_EL2_GROUP) { + irqlevel = 1; + } else { + fiqlevel = 1; + } + } + } + + trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, irqlevel); + qemu_set_irq(cs->parent_vfiq, fiqlevel); + qemu_set_irq(cs->parent_virq, irqlevel); +} + static void gicv3_cpuif_virt_update(GICv3CPUState *cs) { - /* Tell the CPU about any pending virtual interrupts or + /* + * Tell the CPU about any pending virtual interrupts or * maintenance interrupts, following a change to the state * of the CPU interface relevant to virtual interrupts. * @@ -389,37 +507,17 @@ static void gicv3_cpuif_virt_update(GICv3CPUState *cs) * naturally as a result of there being no architectural * linkage between the physical and virtual GIC logic. */ - int idx; - int irqlevel = 0; - int fiqlevel = 0; - int maintlevel = 0; ARMCPU *cpu = ARM_CPU(cs->cpu); + int maintlevel = 0; - idx = hppvi_index(cs); - trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx); - if (idx >= 0) { - uint64_t lr = cs->ich_lr_el2[idx]; - - if (icv_hppi_can_preempt(cs, lr)) { - /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */ - if (lr & ICH_LR_EL2_GROUP) { - irqlevel = 1; - } else { - fiqlevel = 1; - } - } - } + gicv3_cpuif_virt_irq_fiq_update(cs); if ((cs->ich_hcr_el2 & ICH_HCR_EL2_EN) && maintenance_interrupt_state(cs) != 0) { maintlevel = 1; } - trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, - irqlevel, maintlevel); - - qemu_set_irq(cs->parent_vfiq, fiqlevel); - qemu_set_irq(cs->parent_virq, irqlevel); + trace_gicv3_cpuif_virt_set_maint_irq(gicv3_redist_affid(cs), maintlevel); qemu_set_irq(cpu->gicv3_maintenance_interrupt, maintlevel); } @@ -445,7 +543,7 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; - gicv3_cpuif_virt_update(cs); + gicv3_cpuif_virt_irq_fiq_update(cs); return; } @@ -490,7 +588,7 @@ static void icv_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, write_vbpr(cs, grp, value); - gicv3_cpuif_virt_update(cs); + gicv3_cpuif_virt_irq_fiq_update(cs); } static uint64_t icv_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -517,7 +615,7 @@ static void icv_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri, cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, ICH_VMCR_EL2_VPMR_LENGTH, value); - gicv3_cpuif_virt_update(cs); + gicv3_cpuif_virt_irq_fiq_update(cs); } static uint64_t icv_igrpen_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -584,7 +682,7 @@ static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VEOIM_SHIFT, 1, value & ICC_CTLR_EL1_EOIMODE ? 1 : 0); - gicv3_cpuif_virt_update(cs); + gicv3_cpuif_virt_irq_fiq_update(cs); } static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -603,7 +701,11 @@ static uint64_t icv_hppir_read(CPUARMState *env, const ARMCPRegInfo *ri) int idx = hppvi_index(cs); uint64_t value = INTID_SPURIOUS; - if (idx >= 0) { + if (idx == HPPVI_INDEX_VLPI) { + if (cs->hppvlpi.grp == grp) { + value = cs->hppvlpi.irq; + } + } else if (idx >= 0) { uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; @@ -612,7 +714,8 @@ static uint64_t icv_hppir_read(CPUARMState *env, const ARMCPRegInfo *ri) } } - trace_gicv3_icv_hppir_read(grp, gicv3_redist_affid(cs), value); + trace_gicv3_icv_hppir_read(ri->crm == 8 ? 0 : 1, + gicv3_redist_affid(cs), value); return value; } @@ -633,6 +736,18 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp) cs->ich_apr[grp][regno] |= (1 << regbit); } +static void icv_activate_vlpi(GICv3CPUState *cs) +{ + uint32_t mask = icv_gprio_mask(cs, cs->hppvlpi.grp); + int prio = cs->hppvlpi.prio & mask; + int aprbit = prio >> (8 - cs->vprebits); + int regno = aprbit / 32; + int regbit = aprbit % 32; + + cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit); + gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0); +} + static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); @@ -640,7 +755,12 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) int idx = hppvi_index(cs); uint64_t intid = INTID_SPURIOUS; - if (idx >= 0) { + if (idx == HPPVI_INDEX_VLPI) { + if (cs->hppvlpi.grp == grp && icv_hppvlpi_can_preempt(cs)) { + intid = cs->hppvlpi.irq; + icv_activate_vlpi(cs); + } + } else if (idx >= 0) { uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; @@ -2332,7 +2452,7 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; - gicv3_cpuif_virt_update(cs); + gicv3_cpuif_virt_irq_fiq_update(cs); } static uint64_t ich_hcr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -2458,11 +2578,15 @@ static uint64_t ich_vtr_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t value; value = ((cs->num_list_regs - 1) << ICH_VTR_EL2_LISTREGS_SHIFT) - | ICH_VTR_EL2_TDS | ICH_VTR_EL2_NV4 | ICH_VTR_EL2_A3V + | ICH_VTR_EL2_TDS | ICH_VTR_EL2_A3V | (1 << ICH_VTR_EL2_IDBITS_SHIFT) | ((cs->vprebits - 1) << ICH_VTR_EL2_PREBITS_SHIFT) | ((cs->vpribits - 1) << ICH_VTR_EL2_PRIBITS_SHIFT); + if (cs->gic->revision < 4) { + value |= ICH_VTR_EL2_NV4; + } + trace_gicv3_ich_vtr_read(gicv3_redist_affid(cs), value); return value; } @@ -2615,6 +2739,12 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque) GICv3CPUState *cs = opaque; gicv3_cpuif_update(cs); + /* + * Because vLPIs are only pending in NonSecure state, + * an EL change can change the VIRQ/VFIQ status (but + * cannot affect the maintenance interrupt state) + */ + gicv3_cpuif_virt_irq_fiq_update(cs); } void gicv3_init_cpuif(GICv3State *s) diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 4164500ea9..b9ed955e36 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -383,7 +383,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, * No1N == 1 (1-of-N SPI interrupts not supported) * A3V == 1 (non-zero values of Affinity level 3 supported) * IDbits == 0xf (we support 16-bit interrupt identifiers) - * DVIS == 0 (Direct virtual LPI injection not supported) + * DVIS == 1 (Direct virtual LPI injection supported) if GICv4 * LPIS == 1 (LPIs are supported if affinity routing is enabled) * num_LPIs == 0b00000 (bits [15:11],Number of LPIs as indicated * by GICD_TYPER.IDbits) @@ -399,8 +399,9 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, * so we only need to check the DS bit. */ bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS); + bool dvis = s->revision >= 4; - *data = (1 << 25) | (1 << 24) | (sec_extn << 10) | + *data = (1 << 25) | (1 << 24) | (dvis << 18) | (sec_extn << 10) | (s->lpi_enable << GICD_TYPER_LPIS_SHIFT) | (0xf << 19) | itlinesnumber; return true; @@ -557,7 +558,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, } case GICD_IDREGS ... GICD_IDREGS + 0x2f: /* ID registers */ - *data = gicv3_idreg(offset - GICD_IDREGS); + *data = gicv3_idreg(s, offset - GICD_IDREGS, GICV3_PIDR0_DIST); return true; case GICD_SGIR: /* WO registers, return unknown value */ @@ -838,7 +839,7 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data, if (!r) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_dist_badread(offset, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; * so use MEMTX_ERROR returns from leaf functions as a way to @@ -879,7 +880,7 @@ MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data, if (!r) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_dist_badwrite(offset, data, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; * so use MEMTX_ERROR returns from leaf functions as a way to diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 4f598d3c14..2ff21ed6bb 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -61,6 +61,12 @@ typedef struct ITEntry { uint32_t vpeid; } ITEntry; +typedef struct VTEntry { + bool valid; + unsigned vptsize; + uint32_t rdbase; + uint64_t vptaddr; +} VTEntry; /* * The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options @@ -72,13 +78,33 @@ typedef struct ITEntry { * and continue processing. * The process_* functions which handle individual ITS commands all * return an ItsCmdResult which tells process_cmdq() whether it should - * stall or keep going. + * stall, keep going because of an error, or keep going because the + * command was a success. */ typedef enum ItsCmdResult { CMD_STALL = 0, CMD_CONTINUE = 1, + CMD_CONTINUE_OK = 2, } ItsCmdResult; +/* True if the ITS supports the GICv4 virtual LPI feature */ +static bool its_feature_virtual(GICv3ITSState *s) +{ + return s->typer & R_GITS_TYPER_VIRTUAL_MASK; +} + +static inline bool intid_in_lpi_range(uint32_t id) +{ + return id >= GICV3_LPI_INTID_START && + id < (1 << (GICD_TYPER_IDBITS + 1)); +} + +static inline bool valid_doorbell(uint32_t id) +{ + /* Doorbell fields may be an LPI, or 1023 to mean "no doorbell" */ + return id == INTID_SPURIOUS || intid_in_lpi_range(id); +} + static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz) { uint64_t result = 0; @@ -161,16 +187,22 @@ static MemTxResult get_cte(GICv3ITSState *s, uint16_t icid, CTEntry *cte) if (entry_addr == -1) { /* No L2 table entry, i.e. no valid CTE, or a memory error */ cte->valid = false; - return res; + goto out; } cteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res); if (res != MEMTX_OK) { - return res; + goto out; } cte->valid = FIELD_EX64(cteval, CTE, VALID); cte->rdbase = FIELD_EX64(cteval, CTE, RDBASE); - return MEMTX_OK; +out: + if (res != MEMTX_OK) { + trace_gicv3_its_cte_read_fault(icid); + } else { + trace_gicv3_its_cte_read(icid, cte->valid, cte->rdbase); + } + return res; } /* @@ -187,6 +219,10 @@ static bool update_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, uint64_t itel = 0; uint32_t iteh = 0; + trace_gicv3_its_ite_write(dte->ittaddr, eventid, ite->valid, + ite->inttype, ite->intid, ite->icid, + ite->vpeid, ite->doorbell); + if (ite->valid) { itel = FIELD_DP64(itel, ITE_L, VALID, 1); itel = FIELD_DP64(itel, ITE_L, INTTYPE, ite->inttype); @@ -221,11 +257,13 @@ static MemTxResult get_ite(GICv3ITSState *s, uint32_t eventid, itel = address_space_ldq_le(as, iteaddr, MEMTXATTRS_UNSPECIFIED, &res); if (res != MEMTX_OK) { + trace_gicv3_its_ite_read_fault(dte->ittaddr, eventid); return res; } iteh = address_space_ldl_le(as, iteaddr + 8, MEMTXATTRS_UNSPECIFIED, &res); if (res != MEMTX_OK) { + trace_gicv3_its_ite_read_fault(dte->ittaddr, eventid); return res; } @@ -235,6 +273,9 @@ static MemTxResult get_ite(GICv3ITSState *s, uint32_t eventid, ite->icid = FIELD_EX64(itel, ITE_L, ICID); ite->vpeid = FIELD_EX64(itel, ITE_L, VPEID); ite->doorbell = FIELD_EX64(iteh, ITE_H, DOORBELL); + trace_gicv3_its_ite_read(dte->ittaddr, eventid, ite->valid, + ite->inttype, ite->intid, ite->icid, + ite->vpeid, ite->doorbell); return MEMTX_OK; } @@ -254,17 +295,215 @@ static MemTxResult get_dte(GICv3ITSState *s, uint32_t devid, DTEntry *dte) if (entry_addr == -1) { /* No L2 table entry, i.e. no valid DTE, or a memory error */ dte->valid = false; - return res; + goto out; } dteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res); if (res != MEMTX_OK) { - return res; + goto out; } dte->valid = FIELD_EX64(dteval, DTE, VALID); dte->size = FIELD_EX64(dteval, DTE, SIZE); /* DTE word field stores bits [51:8] of the ITT address */ dte->ittaddr = FIELD_EX64(dteval, DTE, ITTADDR) << ITTADDR_SHIFT; - return MEMTX_OK; +out: + if (res != MEMTX_OK) { + trace_gicv3_its_dte_read_fault(devid); + } else { + trace_gicv3_its_dte_read(devid, dte->valid, dte->size, dte->ittaddr); + } + return res; +} + +/* + * Read the vPE Table entry at index @vpeid. On success (including + * successfully determining that there is no valid entry for this index), + * we return MEMTX_OK and populate the VTEntry struct accordingly. + * If there is an error reading memory then we return the error code. + */ +static MemTxResult get_vte(GICv3ITSState *s, uint32_t vpeid, VTEntry *vte) +{ + MemTxResult res = MEMTX_OK; + AddressSpace *as = &s->gicv3->dma_as; + uint64_t entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res); + uint64_t vteval; + + if (entry_addr == -1) { + /* No L2 table entry, i.e. no valid VTE, or a memory error */ + vte->valid = false; + goto out; + } + vteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + goto out; + } + vte->valid = FIELD_EX64(vteval, VTE, VALID); + vte->vptsize = FIELD_EX64(vteval, VTE, VPTSIZE); + vte->vptaddr = FIELD_EX64(vteval, VTE, VPTADDR); + vte->rdbase = FIELD_EX64(vteval, VTE, RDBASE); +out: + if (res != MEMTX_OK) { + trace_gicv3_its_vte_read_fault(vpeid); + } else { + trace_gicv3_its_vte_read(vpeid, vte->valid, vte->vptsize, + vte->vptaddr, vte->rdbase); + } + return res; +} + +/* + * Given a (DeviceID, EventID), look up the corresponding ITE, including + * checking for the various invalid-value cases. If we find a valid ITE, + * fill in @ite and @dte and return CMD_CONTINUE_OK. Otherwise return + * CMD_STALL or CMD_CONTINUE as appropriate (and the contents of @ite + * should not be relied on). + * + * The string @who is purely for the LOG_GUEST_ERROR messages, + * and should indicate the name of the calling function or similar. + */ +static ItsCmdResult lookup_ite(GICv3ITSState *s, const char *who, + uint32_t devid, uint32_t eventid, ITEntry *ite, + DTEntry *dte) +{ + uint64_t num_eventids; + + if (devid >= s->dt.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: devid %d>=%d", + who, devid, s->dt.num_entries); + return CMD_CONTINUE; + } + + if (get_dte(s, devid, dte) != MEMTX_OK) { + return CMD_STALL; + } + if (!dte->valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: " + "invalid dte for %d\n", who, devid); + return CMD_CONTINUE; + } + + num_eventids = 1ULL << (dte->size + 1); + if (eventid >= num_eventids) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: eventid %d >= %" + PRId64 "\n", who, eventid, num_eventids); + return CMD_CONTINUE; + } + + if (get_ite(s, eventid, dte, ite) != MEMTX_OK) { + return CMD_STALL; + } + + if (!ite->valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: invalid ITE\n", who); + return CMD_CONTINUE; + } + + return CMD_CONTINUE_OK; +} + +/* + * Given an ICID, look up the corresponding CTE, including checking for various + * invalid-value cases. If we find a valid CTE, fill in @cte and return + * CMD_CONTINUE_OK; otherwise return CMD_STALL or CMD_CONTINUE (and the + * contents of @cte should not be relied on). + * + * The string @who is purely for the LOG_GUEST_ERROR messages, + * and should indicate the name of the calling function or similar. + */ +static ItsCmdResult lookup_cte(GICv3ITSState *s, const char *who, + uint32_t icid, CTEntry *cte) +{ + if (icid >= s->ct.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid ICID 0x%x\n", who, icid); + return CMD_CONTINUE; + } + if (get_cte(s, icid, cte) != MEMTX_OK) { + return CMD_STALL; + } + if (!cte->valid) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CTE\n", who); + return CMD_CONTINUE; + } + if (cte->rdbase >= s->gicv3->num_cpu) { + return CMD_CONTINUE; + } + return CMD_CONTINUE_OK; +} + +/* + * Given a VPEID, look up the corresponding VTE, including checking + * for various invalid-value cases. if we find a valid VTE, fill in @vte + * and return CMD_CONTINUE_OK; otherwise return CMD_STALL or CMD_CONTINUE + * (and the contents of @vte should not be relied on). + * + * The string @who is purely for the LOG_GUEST_ERROR messages, + * and should indicate the name of the calling function or similar. + */ +static ItsCmdResult lookup_vte(GICv3ITSState *s, const char *who, + uint32_t vpeid, VTEntry *vte) +{ + if (vpeid >= s->vpet.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid VPEID 0x%x\n", who, vpeid); + return CMD_CONTINUE; + } + + if (get_vte(s, vpeid, vte) != MEMTX_OK) { + return CMD_STALL; + } + if (!vte->valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid VTE for VPEID 0x%x\n", who, vpeid); + return CMD_CONTINUE; + } + + if (vte->rdbase >= s->gicv3->num_cpu) { + return CMD_CONTINUE; + } + return CMD_CONTINUE_OK; +} + +static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite, + int irqlevel) +{ + CTEntry cte; + ItsCmdResult cmdres; + + cmdres = lookup_cte(s, __func__, ite->icid, &cte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite->intid, irqlevel); + return CMD_CONTINUE_OK; +} + +static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite, + int irqlevel) +{ + VTEntry vte; + ItsCmdResult cmdres; + + cmdres = lookup_vte(s, __func__, ite->vpeid, &vte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + + if (!intid_in_lpi_range(ite->intid) || + ite->intid >= (1ULL << (vte.vptsize + 1))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: intid 0x%x out of range\n", + __func__, ite->intid); + return CMD_CONTINUE; + } + + /* + * For QEMU the actual pending of the vLPI is handled in the + * redistributor code + */ + gicv3_redist_process_vlpi(&s->gicv3->cpu[vte.rdbase], ite->intid, + vte.vptaddr << 16, ite->doorbell, irqlevel); + return CMD_CONTINUE_OK; } /* @@ -278,87 +517,45 @@ static MemTxResult get_dte(GICv3ITSState *s, uint32_t devid, DTEntry *dte) static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, uint32_t eventid, ItsCmdType cmd) { - uint64_t num_eventids; DTEntry dte; - CTEntry cte; ITEntry ite; + ItsCmdResult cmdres; + int irqlevel; - if (devid >= s->dt.num_entries) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: devid %d>=%d", - __func__, devid, s->dt.num_entries); - return CMD_CONTINUE; + cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; } - if (get_dte(s, devid, &dte) != MEMTX_OK) { - return CMD_STALL; - } - if (!dte.valid) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: " - "invalid dte for %d\n", __func__, devid); - return CMD_CONTINUE; + irqlevel = (cmd == CLEAR || cmd == DISCARD) ? 0 : 1; + + switch (ite.inttype) { + case ITE_INTTYPE_PHYSICAL: + cmdres = process_its_cmd_phys(s, &ite, irqlevel); + break; + case ITE_INTTYPE_VIRTUAL: + if (!its_feature_virtual(s)) { + /* Can't happen unless guest is illegally writing to table memory */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid type %d in ITE (table corrupted?)\n", + __func__, ite.inttype); + return CMD_CONTINUE; + } + cmdres = process_its_cmd_virt(s, &ite, irqlevel); + break; + default: + g_assert_not_reached(); } - num_eventids = 1ULL << (dte.size + 1); - if (eventid >= num_eventids) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: eventid %d >= %" - PRId64 "\n", - __func__, eventid, num_eventids); - return CMD_CONTINUE; - } - - if (get_ite(s, eventid, &dte, &ite) != MEMTX_OK) { - return CMD_STALL; - } - - if (!ite.valid || ite.inttype != ITE_INTTYPE_PHYSICAL) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: invalid ITE\n", - __func__); - return CMD_CONTINUE; - } - - if (ite.icid >= s->ct.num_entries) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid ICID 0x%x in ITE (table corrupted?)\n", - __func__, ite.icid); - return CMD_CONTINUE; - } - - if (get_cte(s, ite.icid, &cte) != MEMTX_OK) { - return CMD_STALL; - } - if (!cte.valid) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: invalid CTE\n", - __func__); - return CMD_CONTINUE; - } - - /* - * Current implementation only supports rdbase == procnum - * Hence rdbase physical address is ignored - */ - if (cte.rdbase >= s->gicv3->num_cpu) { - return CMD_CONTINUE; - } - - if ((cmd == CLEAR) || (cmd == DISCARD)) { - gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 0); - } else { - gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 1); - } - - if (cmd == DISCARD) { + if (cmdres == CMD_CONTINUE_OK && cmd == DISCARD) { ITEntry ite = {}; /* remove mapping from interrupt translation table */ ite.valid = false; - return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL; + return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL; } - return CMD_CONTINUE; + return CMD_CONTINUE_OK; } + static ItsCmdResult process_its_cmd(GICv3ITSState *s, const uint64_t *cmdpkt, ItsCmdType cmd) { @@ -366,6 +563,19 @@ static ItsCmdResult process_its_cmd(GICv3ITSState *s, const uint64_t *cmdpkt, devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; eventid = cmdpkt[1] & EVENTID_MASK; + switch (cmd) { + case INTERRUPT: + trace_gicv3_its_cmd_int(devid, eventid); + break; + case CLEAR: + trace_gicv3_its_cmd_clear(devid, eventid); + break; + case DISCARD: + trace_gicv3_its_cmd_discard(devid, eventid); + break; + default: + g_assert_not_reached(); + } return do_process_its_cmd(s, devid, eventid, cmd); } @@ -375,22 +585,22 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, uint32_t devid, eventid; uint32_t pIntid = 0; uint64_t num_eventids; - uint32_t num_intids; uint16_t icid = 0; DTEntry dte; ITEntry ite; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; eventid = cmdpkt[1] & EVENTID_MASK; + icid = cmdpkt[2] & ICID_MASK; if (ignore_pInt) { pIntid = eventid; + trace_gicv3_its_cmd_mapi(devid, eventid, icid); } else { pIntid = (cmdpkt[1] & pINTID_MASK) >> pINTID_SHIFT; + trace_gicv3_its_cmd_mapti(devid, eventid, icid, pIntid); } - icid = cmdpkt[2] & ICID_MASK; - if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: devid %d>=%d", @@ -402,7 +612,6 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, return CMD_STALL; } num_eventids = 1ULL << (dte.size + 1); - num_intids = 1ULL << (GICD_TYPER_IDBITS + 1); if (icid >= s->ct.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, @@ -424,7 +633,7 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, return CMD_CONTINUE; } - if (pIntid < GICV3_LPI_INTID_START || pIntid >= num_intids) { + if (!intid_in_lpi_range(pIntid)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid interrupt ID 0x%x\n", __func__, pIntid); return CMD_CONTINUE; @@ -437,7 +646,86 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, ite.icid = icid; ite.doorbell = INTID_SPURIOUS; ite.vpeid = 0; - return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL; + return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL; +} + +static ItsCmdResult process_vmapti(GICv3ITSState *s, const uint64_t *cmdpkt, + bool ignore_vintid) +{ + uint32_t devid, eventid, vintid, doorbell, vpeid; + uint32_t num_eventids; + DTEntry dte; + ITEntry ite; + + if (!its_feature_virtual(s)) { + return CMD_CONTINUE; + } + + devid = FIELD_EX64(cmdpkt[0], VMAPTI_0, DEVICEID); + eventid = FIELD_EX64(cmdpkt[1], VMAPTI_1, EVENTID); + vpeid = FIELD_EX64(cmdpkt[1], VMAPTI_1, VPEID); + doorbell = FIELD_EX64(cmdpkt[2], VMAPTI_2, DOORBELL); + if (ignore_vintid) { + vintid = eventid; + trace_gicv3_its_cmd_vmapi(devid, eventid, vpeid, doorbell); + } else { + vintid = FIELD_EX64(cmdpkt[2], VMAPTI_2, VINTID); + trace_gicv3_its_cmd_vmapti(devid, eventid, vpeid, vintid, doorbell); + } + + if (devid >= s->dt.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid DeviceID 0x%x (must be less than 0x%x)\n", + __func__, devid, s->dt.num_entries); + return CMD_CONTINUE; + } + + if (get_dte(s, devid, &dte) != MEMTX_OK) { + return CMD_STALL; + } + + if (!dte.valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: no entry in device table for DeviceID 0x%x\n", + __func__, devid); + return CMD_CONTINUE; + } + + num_eventids = 1ULL << (dte.size + 1); + + if (eventid >= num_eventids) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: EventID 0x%x too large for DeviceID 0x%x " + "(must be less than 0x%x)\n", + __func__, eventid, devid, num_eventids); + return CMD_CONTINUE; + } + if (!intid_in_lpi_range(vintid)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VIntID 0x%x not a valid LPI\n", + __func__, vintid); + return CMD_CONTINUE; + } + if (!valid_doorbell(doorbell)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Doorbell %d not 1023 and not a valid LPI\n", + __func__, doorbell); + return CMD_CONTINUE; + } + if (vpeid >= s->vpet.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VPEID 0x%x out of range (must be less than 0x%x)\n", + __func__, vpeid, s->vpet.num_entries); + return CMD_CONTINUE; + } + /* add ite entry to interrupt translation table */ + ite.valid = true; + ite.inttype = ITE_INTTYPE_VIRTUAL; + ite.intid = vintid; + ite.icid = 0; + ite.doorbell = doorbell; + ite.vpeid = vpeid; + return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL; } /* @@ -451,6 +739,8 @@ static bool update_cte(GICv3ITSState *s, uint16_t icid, const CTEntry *cte) uint64_t cteval = 0; MemTxResult res = MEMTX_OK; + trace_gicv3_its_cte_write(icid, cte->valid, cte->rdbase); + if (cte->valid) { /* add mapping entry to collection table */ cteval = FIELD_DP64(cteval, CTE, VALID, 1); @@ -484,18 +774,19 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) } else { cte.rdbase = 0; } + trace_gicv3_its_cmd_mapc(icid, cte.rdbase, cte.valid); if (icid >= s->ct.num_entries) { - qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPC: invalid ICID 0x%d", icid); + qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPC: invalid ICID 0x%x\n", icid); return CMD_CONTINUE; } if (cte.valid && cte.rdbase >= s->gicv3->num_cpu) { qemu_log_mask(LOG_GUEST_ERROR, - "ITS MAPC: invalid RDBASE %u ", cte.rdbase); + "ITS MAPC: invalid RDBASE %u\n", cte.rdbase); return CMD_CONTINUE; } - return update_cte(s, icid, &cte) ? CMD_CONTINUE : CMD_STALL; + return update_cte(s, icid, &cte) ? CMD_CONTINUE_OK : CMD_STALL; } /* @@ -509,6 +800,8 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, const DTEntry *dte) uint64_t dteval = 0; MemTxResult res = MEMTX_OK; + trace_gicv3_its_dte_write(devid, dte->valid, dte->size, dte->ittaddr); + if (dte->valid) { /* add mapping entry to device table */ dteval = FIELD_DP64(dteval, DTE, VALID, 1); @@ -539,6 +832,8 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) dte.ittaddr = (cmdpkt[2] & ITTADDR_MASK) >> ITTADDR_SHIFT; dte.valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; + trace_gicv3_its_cmd_mapd(devid, dte.size, dte.ittaddr, dte.valid); + if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPD: invalid device ID field 0x%x >= 0x%x\n", @@ -552,7 +847,7 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) return CMD_CONTINUE; } - return update_dte(s, devid, &dte) ? CMD_CONTINUE : CMD_STALL; + return update_dte(s, devid, &dte) ? CMD_CONTINUE_OK : CMD_STALL; } static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt) @@ -562,6 +857,8 @@ static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt) rd1 = FIELD_EX64(cmdpkt[2], MOVALL_2, RDBASE1); rd2 = FIELD_EX64(cmdpkt[3], MOVALL_3, RDBASE2); + trace_gicv3_its_cmd_movall(rd1, rd2); + if (rd1 >= s->gicv3->num_cpu) { qemu_log_mask(LOG_GUEST_ERROR, "%s: RDBASE1 %" PRId64 @@ -579,113 +876,49 @@ static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt) if (rd1 == rd2) { /* Move to same target must succeed as a no-op */ - return CMD_CONTINUE; + return CMD_CONTINUE_OK; } /* Move all pending LPIs from redistributor 1 to redistributor 2 */ gicv3_redist_movall_lpis(&s->gicv3->cpu[rd1], &s->gicv3->cpu[rd2]); - return CMD_CONTINUE; + return CMD_CONTINUE_OK; } static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid, eventid; uint16_t new_icid; - uint64_t num_eventids; DTEntry dte; CTEntry old_cte, new_cte; ITEntry old_ite; + ItsCmdResult cmdres; devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID); eventid = FIELD_EX64(cmdpkt[1], MOVI_1, EVENTID); new_icid = FIELD_EX64(cmdpkt[2], MOVI_2, ICID); - if (devid >= s->dt.num_entries) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: devid %d>=%d", - __func__, devid, s->dt.num_entries); - return CMD_CONTINUE; - } - if (get_dte(s, devid, &dte) != MEMTX_OK) { - return CMD_STALL; + trace_gicv3_its_cmd_movi(devid, eventid, new_icid); + + cmdres = lookup_ite(s, __func__, devid, eventid, &old_ite, &dte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; } - if (!dte.valid) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: " - "invalid dte for %d\n", __func__, devid); - return CMD_CONTINUE; - } - - num_eventids = 1ULL << (dte.size + 1); - if (eventid >= num_eventids) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: eventid %d >= %" - PRId64 "\n", - __func__, eventid, num_eventids); - return CMD_CONTINUE; - } - - if (get_ite(s, eventid, &dte, &old_ite) != MEMTX_OK) { - return CMD_STALL; - } - - if (!old_ite.valid || old_ite.inttype != ITE_INTTYPE_PHYSICAL) { + if (old_ite.inttype != ITE_INTTYPE_PHYSICAL) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: invalid ITE\n", __func__); return CMD_CONTINUE; } - if (old_ite.icid >= s->ct.num_entries) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid ICID 0x%x in ITE (table corrupted?)\n", - __func__, old_ite.icid); - return CMD_CONTINUE; + cmdres = lookup_cte(s, __func__, old_ite.icid, &old_cte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; } - - if (new_icid >= s->ct.num_entries) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: ICID 0x%x\n", - __func__, new_icid); - return CMD_CONTINUE; - } - - if (get_cte(s, old_ite.icid, &old_cte) != MEMTX_OK) { - return CMD_STALL; - } - if (!old_cte.valid) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: " - "invalid CTE for old ICID 0x%x\n", - __func__, old_ite.icid); - return CMD_CONTINUE; - } - - if (get_cte(s, new_icid, &new_cte) != MEMTX_OK) { - return CMD_STALL; - } - if (!new_cte.valid) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: " - "invalid CTE for new ICID 0x%x\n", - __func__, new_icid); - return CMD_CONTINUE; - } - - if (old_cte.rdbase >= s->gicv3->num_cpu) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: CTE has invalid rdbase 0x%x\n", - __func__, old_cte.rdbase); - return CMD_CONTINUE; - } - - if (new_cte.rdbase >= s->gicv3->num_cpu) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: CTE has invalid rdbase 0x%x\n", - __func__, new_cte.rdbase); - return CMD_CONTINUE; + cmdres = lookup_cte(s, __func__, new_icid, &new_cte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; } if (old_cte.rdbase != new_cte.rdbase) { @@ -697,7 +930,316 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) /* Update the ICID field in the interrupt translation table entry */ old_ite.icid = new_icid; - return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE : CMD_STALL; + return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE_OK : CMD_STALL; +} + +/* + * Update the vPE Table entry at index @vpeid with the entry @vte. + * Returns true on success, false if there was a memory access error. + */ +static bool update_vte(GICv3ITSState *s, uint32_t vpeid, const VTEntry *vte) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t entry_addr; + uint64_t vteval = 0; + MemTxResult res = MEMTX_OK; + + trace_gicv3_its_vte_write(vpeid, vte->valid, vte->vptsize, vte->vptaddr, + vte->rdbase); + + if (vte->valid) { + vteval = FIELD_DP64(vteval, VTE, VALID, 1); + vteval = FIELD_DP64(vteval, VTE, VPTSIZE, vte->vptsize); + vteval = FIELD_DP64(vteval, VTE, VPTADDR, vte->vptaddr); + vteval = FIELD_DP64(vteval, VTE, RDBASE, vte->rdbase); + } + + entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res); + if (res != MEMTX_OK) { + return false; + } + if (entry_addr == -1) { + /* No L2 table for this index: discard write and continue */ + return true; + } + address_space_stq_le(as, entry_addr, vteval, MEMTXATTRS_UNSPECIFIED, &res); + return res == MEMTX_OK; +} + +static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt) +{ + VTEntry vte; + uint32_t vpeid; + + if (!its_feature_virtual(s)) { + return CMD_CONTINUE; + } + + vpeid = FIELD_EX64(cmdpkt[1], VMAPP_1, VPEID); + vte.rdbase = FIELD_EX64(cmdpkt[2], VMAPP_2, RDBASE); + vte.valid = FIELD_EX64(cmdpkt[2], VMAPP_2, V); + vte.vptsize = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTSIZE); + vte.vptaddr = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTADDR); + + trace_gicv3_its_cmd_vmapp(vpeid, vte.rdbase, vte.valid, + vte.vptaddr, vte.vptsize); + + /* + * For GICv4.0 the VPT_size field is only 5 bits, whereas we + * define our field macros to include the full GICv4.1 8 bits. + * The range check on VPT_size will catch the cases where + * the guest set the RES0-in-GICv4.0 bits [7:6]. + */ + if (vte.vptsize > FIELD_EX64(s->typer, GITS_TYPER, IDBITS)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid VPT_size 0x%x\n", __func__, vte.vptsize); + return CMD_CONTINUE; + } + + if (vte.valid && vte.rdbase >= s->gicv3->num_cpu) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid rdbase 0x%x\n", __func__, vte.rdbase); + return CMD_CONTINUE; + } + + if (vpeid >= s->vpet.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VPEID 0x%x out of range (must be less than 0x%x)\n", + __func__, vpeid, s->vpet.num_entries); + return CMD_CONTINUE; + } + + return update_vte(s, vpeid, &vte) ? CMD_CONTINUE_OK : CMD_STALL; +} + +typedef struct VmovpCallbackData { + uint64_t rdbase; + uint32_t vpeid; + /* + * Overall command result. If more than one callback finds an + * error, STALL beats CONTINUE. + */ + ItsCmdResult result; +} VmovpCallbackData; + +static void vmovp_callback(gpointer data, gpointer opaque) +{ + /* + * This function is called to update the VPEID field in a VPE + * table entry for this ITS. This might be because of a VMOVP + * command executed on any ITS that is connected to the same GIC + * as this ITS. We need to read the VPE table entry for the VPEID + * and update its RDBASE field. + */ + GICv3ITSState *s = data; + VmovpCallbackData *cbdata = opaque; + VTEntry vte; + ItsCmdResult cmdres; + + cmdres = lookup_vte(s, __func__, cbdata->vpeid, &vte); + switch (cmdres) { + case CMD_STALL: + cbdata->result = CMD_STALL; + return; + case CMD_CONTINUE: + if (cbdata->result != CMD_STALL) { + cbdata->result = CMD_CONTINUE; + } + return; + case CMD_CONTINUE_OK: + break; + } + + vte.rdbase = cbdata->rdbase; + if (!update_vte(s, cbdata->vpeid, &vte)) { + cbdata->result = CMD_STALL; + } +} + +static ItsCmdResult process_vmovp(GICv3ITSState *s, const uint64_t *cmdpkt) +{ + VmovpCallbackData cbdata; + + if (!its_feature_virtual(s)) { + return CMD_CONTINUE; + } + + cbdata.vpeid = FIELD_EX64(cmdpkt[1], VMOVP_1, VPEID); + cbdata.rdbase = FIELD_EX64(cmdpkt[2], VMOVP_2, RDBASE); + + trace_gicv3_its_cmd_vmovp(cbdata.vpeid, cbdata.rdbase); + + if (cbdata.rdbase >= s->gicv3->num_cpu) { + return CMD_CONTINUE; + } + + /* + * Our ITS implementation reports GITS_TYPER.VMOVP == 1, which means + * that when the VMOVP command is executed on an ITS to change the + * VPEID field in a VPE table entry the change must be propagated + * to all the ITSes connected to the same GIC. + */ + cbdata.result = CMD_CONTINUE_OK; + gicv3_foreach_its(s->gicv3, vmovp_callback, &cbdata); + return cbdata.result; +} + +static ItsCmdResult process_vmovi(GICv3ITSState *s, const uint64_t *cmdpkt) +{ + uint32_t devid, eventid, vpeid, doorbell; + bool doorbell_valid; + DTEntry dte; + ITEntry ite; + VTEntry old_vte, new_vte; + ItsCmdResult cmdres; + + if (!its_feature_virtual(s)) { + return CMD_CONTINUE; + } + + devid = FIELD_EX64(cmdpkt[0], VMOVI_0, DEVICEID); + eventid = FIELD_EX64(cmdpkt[1], VMOVI_1, EVENTID); + vpeid = FIELD_EX64(cmdpkt[1], VMOVI_1, VPEID); + doorbell_valid = FIELD_EX64(cmdpkt[2], VMOVI_2, D); + doorbell = FIELD_EX64(cmdpkt[2], VMOVI_2, DOORBELL); + + trace_gicv3_its_cmd_vmovi(devid, eventid, vpeid, doorbell_valid, doorbell); + + if (doorbell_valid && !valid_doorbell(doorbell)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid doorbell 0x%x\n", __func__, doorbell); + return CMD_CONTINUE; + } + + cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + + if (ite.inttype != ITE_INTTYPE_VIRTUAL) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: ITE is not for virtual interrupt\n", + __func__); + return CMD_CONTINUE; + } + + cmdres = lookup_vte(s, __func__, ite.vpeid, &old_vte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + cmdres = lookup_vte(s, __func__, vpeid, &new_vte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + + if (!intid_in_lpi_range(ite.intid) || + ite.intid >= (1ULL << (old_vte.vptsize + 1)) || + ite.intid >= (1ULL << (new_vte.vptsize + 1))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ITE intid 0x%x out of range\n", + __func__, ite.intid); + return CMD_CONTINUE; + } + + ite.vpeid = vpeid; + if (doorbell_valid) { + ite.doorbell = doorbell; + } + + /* + * Move the LPI from the old redistributor to the new one. We don't + * need to do anything if the guest somehow specified the + * same pending table for source and destination. + */ + if (old_vte.vptaddr != new_vte.vptaddr) { + gicv3_redist_mov_vlpi(&s->gicv3->cpu[old_vte.rdbase], + old_vte.vptaddr << 16, + &s->gicv3->cpu[new_vte.rdbase], + new_vte.vptaddr << 16, + ite.intid, + ite.doorbell); + } + + /* Update the ITE to the new VPEID and possibly doorbell values */ + return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL; +} + +static ItsCmdResult process_vinvall(GICv3ITSState *s, const uint64_t *cmdpkt) +{ + VTEntry vte; + uint32_t vpeid; + ItsCmdResult cmdres; + + if (!its_feature_virtual(s)) { + return CMD_CONTINUE; + } + + vpeid = FIELD_EX64(cmdpkt[1], VINVALL_1, VPEID); + + trace_gicv3_its_cmd_vinvall(vpeid); + + cmdres = lookup_vte(s, __func__, vpeid, &vte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + + gicv3_redist_vinvall(&s->gicv3->cpu[vte.rdbase], vte.vptaddr << 16); + return CMD_CONTINUE_OK; +} + +static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt) +{ + uint32_t devid, eventid; + ITEntry ite; + DTEntry dte; + CTEntry cte; + VTEntry vte; + ItsCmdResult cmdres; + + devid = FIELD_EX64(cmdpkt[0], INV_0, DEVICEID); + eventid = FIELD_EX64(cmdpkt[1], INV_1, EVENTID); + + trace_gicv3_its_cmd_inv(devid, eventid); + + cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + + switch (ite.inttype) { + case ITE_INTTYPE_PHYSICAL: + cmdres = lookup_cte(s, __func__, ite.icid, &cte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + gicv3_redist_inv_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid); + break; + case ITE_INTTYPE_VIRTUAL: + if (!its_feature_virtual(s)) { + /* Can't happen unless guest is illegally writing to table memory */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid type %d in ITE (table corrupted?)\n", + __func__, ite.inttype); + return CMD_CONTINUE; + } + + cmdres = lookup_vte(s, __func__, ite.vpeid, &vte); + if (cmdres != CMD_CONTINUE_OK) { + return cmdres; + } + if (!intid_in_lpi_range(ite.intid) || + ite.intid >= (1ULL << (vte.vptsize + 1))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: intid 0x%x out of range\n", + __func__, ite.intid); + return CMD_CONTINUE; + } + gicv3_redist_inv_vlpi(&s->gicv3->cpu[vte.rdbase], ite.intid, + vte.vptaddr << 16); + break; + default: + g_assert_not_reached(); + } + + return CMD_CONTINUE_OK; } /* @@ -736,7 +1278,7 @@ static void process_cmdq(GICv3ITSState *s) } while (wr_offset != rd_offset) { - ItsCmdResult result = CMD_CONTINUE; + ItsCmdResult result = CMD_CONTINUE_OK; void *hostmem; hwaddr buflen; uint64_t cmdpkt[GITS_CMDQ_ENTRY_WORDS]; @@ -779,6 +1321,18 @@ static void process_cmdq(GICv3ITSState *s) * is already consistent by the time SYNC command is executed. * Hence no further processing is required for SYNC command. */ + trace_gicv3_its_cmd_sync(); + break; + case GITS_CMD_VSYNC: + /* + * VSYNC also is a nop, because our implementation is always + * in sync. + */ + if (!its_feature_virtual(s)) { + result = CMD_CONTINUE; + break; + } + trace_gicv3_its_cmd_vsync(); break; case GITS_CMD_MAPD: result = process_mapd(s, cmdpkt); @@ -796,13 +1350,18 @@ static void process_cmdq(GICv3ITSState *s) result = process_its_cmd(s, cmdpkt, DISCARD); break; case GITS_CMD_INV: + result = process_inv(s, cmdpkt); + break; case GITS_CMD_INVALL: /* * Current implementation doesn't cache any ITS tables, * but the calculated lpi priority information. We only * need to trigger lpi priority re-calculation to be in * sync with LPI config table or pending table changes. + * INVALL operates on a collection specified by ICID so + * it only affects physical LPIs. */ + trace_gicv3_its_cmd_invall(); for (i = 0; i < s->gicv3->num_cpu; i++) { gicv3_redist_update_lpi(&s->gicv3->cpu[i]); } @@ -813,10 +1372,30 @@ static void process_cmdq(GICv3ITSState *s) case GITS_CMD_MOVALL: result = process_movall(s, cmdpkt); break; + case GITS_CMD_VMAPTI: + result = process_vmapti(s, cmdpkt, false); + break; + case GITS_CMD_VMAPI: + result = process_vmapti(s, cmdpkt, true); + break; + case GITS_CMD_VMAPP: + result = process_vmapp(s, cmdpkt); + break; + case GITS_CMD_VMOVP: + result = process_vmovp(s, cmdpkt); + break; + case GITS_CMD_VMOVI: + result = process_vmovi(s, cmdpkt); + break; + case GITS_CMD_VINVALL: + result = process_vinvall(s, cmdpkt); + break; default: + trace_gicv3_its_cmd_unknown(cmd); break; } - if (result == CMD_CONTINUE) { + if (result != CMD_STALL) { + /* CMD_CONTINUE or CMD_CONTINUE_OK */ rd_offset++; rd_offset %= s->cq.num_entries; s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset); @@ -892,6 +1471,15 @@ static void extract_table_params(GICv3ITSState *s) idbits = 16; } break; + case GITS_BASER_TYPE_VPE: + td = &s->vpet; + /* + * For QEMU vPEIDs are always 16 bits. (GICv4.1 allows an + * implementation to implement fewer bits and report this + * via GICD_TYPER2.) + */ + idbits = 16; + break; default: /* * GITS_BASER.TYPE is read-only, so GITS_BASER_RO_MASK @@ -1111,7 +1699,7 @@ static bool its_readl(GICv3ITSState *s, hwaddr offset, break; case GITS_IDREGS ... GITS_IDREGS + 0x2f: /* ID registers */ - *data = gicv3_idreg(offset - GITS_IDREGS); + *data = gicv3_idreg(s->gicv3, offset - GITS_IDREGS, GICV3_PIDR0_ITS); break; case GITS_TYPER: *data = extract64(s->typer, 0, 32); @@ -1264,7 +1852,7 @@ static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data, if (!result) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_its_badread(offset, size); /* * The spec requires that reserved registers are RAZ/WI; @@ -1300,7 +1888,7 @@ static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data, if (!result) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_its_badwrite(offset, data, size); /* * The spec requires that reserved registers are RAZ/WI; @@ -1346,6 +1934,8 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) } } + gicv3_add_its(s->gicv3, dev); + gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops); /* set the ITS default features supported */ @@ -1356,6 +1946,11 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS, ITS_DEVBITS); s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1); s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS, ITS_CIDBITS); + if (s->gicv3->revision >= 4) { + /* Our VMOVP handles cross-ITS synchronization itself */ + s->typer = FIELD_DP64(s->typer, GITS_TYPER, VMOVP, 1); + s->typer = FIELD_DP64(s->typer, GITS_TYPER, VIRTUAL, 1); + } } static void gicv3_its_reset(DeviceState *dev) @@ -1371,6 +1966,7 @@ static void gicv3_its_reset(DeviceState *dev) /* * setting GITS_BASER0.Type = 0b001 (Device) * GITS_BASER1.Type = 0b100 (Collection Table) + * GITS_BASER2.Type = 0b010 (vPE) for GICv4 and later * GITS_BASER.Type,where n = 3 to 7 are 0b00 (Unimplemented) * GITS_BASER<0,1>.Page_Size = 64KB * and default translation table entry size to 16 bytes @@ -1388,6 +1984,15 @@ static void gicv3_its_reset(DeviceState *dev) GITS_BASER_PAGESIZE_64K); s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, ENTRYSIZE, GITS_CTE_SIZE - 1); + + if (its_feature_virtual(s)) { + s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, TYPE, + GITS_BASER_TYPE_VPE); + s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, PAGESIZE, + GITS_BASER_PAGESIZE_64K); + s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, ENTRYSIZE, + GITS_VPE_SIZE - 1); + } } static void gicv3_its_post_load(GICv3ITSState *s) diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 0b4cbed28b..529c7bd494 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -106,6 +106,8 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0); + gicv3_add_its(s->gicv3, dev); + gicv3_its_init_mmio(s, NULL, NULL); if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 5ec5ff9ef6..06f5aceee5 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -781,6 +781,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + if (s->revision != 3) { + error_setg(errp, "unsupported GIC revision %d for in-kernel GIC", + s->revision); + } + if (s->security_extn) { error_setg(errp, "the in-kernel VGICv3 does not implement the " "security extensions"); diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 412a04f59c..c3d4cdd66b 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -60,6 +60,132 @@ static uint32_t gicr_read_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, return reg; } +static bool vcpu_resident(GICv3CPUState *cs, uint64_t vptaddr) +{ + /* + * Return true if a vCPU is resident, which is defined by + * whether the GICR_VPENDBASER register is marked VALID and + * has the right virtual pending table address. + */ + if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) { + return false; + } + return vptaddr == (cs->gicr_vpendbaser & R_GICR_VPENDBASER_PHYADDR_MASK); +} + +/** + * update_for_one_lpi: Update pending information if this LPI is better + * + * @cs: GICv3CPUState + * @irq: interrupt to look up in the LPI Configuration table + * @ctbase: physical address of the LPI Configuration table to use + * @ds: true if priority value should not be shifted + * @hpp: points to pending information to update + * + * Look up @irq in the Configuration table specified by @ctbase + * to see if it is enabled and what its priority is. If it is an + * enabled interrupt with a higher priority than that currently + * recorded in @hpp, update @hpp. + */ +static void update_for_one_lpi(GICv3CPUState *cs, int irq, + uint64_t ctbase, bool ds, PendingIrq *hpp) +{ + uint8_t lpite; + uint8_t prio; + + address_space_read(&cs->gic->dma_as, + ctbase + ((irq - GICV3_LPI_INTID_START) * sizeof(lpite)), + MEMTXATTRS_UNSPECIFIED, &lpite, sizeof(lpite)); + + if (!(lpite & LPI_CTE_ENABLED)) { + return; + } + + if (ds) { + prio = lpite & LPI_PRIORITY_MASK; + } else { + prio = ((lpite & LPI_PRIORITY_MASK) >> 1) | 0x80; + } + + if ((prio < hpp->prio) || + ((prio == hpp->prio) && (irq <= hpp->irq))) { + hpp->irq = irq; + hpp->prio = prio; + /* LPIs and vLPIs are always non-secure Grp1 interrupts */ + hpp->grp = GICV3_G1NS; + } +} + +/** + * update_for_all_lpis: Fully scan LPI tables and find best pending LPI + * + * @cs: GICv3CPUState + * @ptbase: physical address of LPI Pending table + * @ctbase: physical address of LPI Configuration table + * @ptsizebits: size of tables, specified as number of interrupt ID bits minus 1 + * @ds: true if priority value should not be shifted + * @hpp: points to pending information to set + * + * Recalculate the highest priority pending enabled LPI from scratch, + * and set @hpp accordingly. + * + * We scan the LPI pending table @ptbase; for each pending LPI, we read the + * corresponding entry in the LPI configuration table @ctbase to extract + * the priority and enabled information. + * + * We take @ptsizebits in the form idbits-1 because this is the way that + * LPI table sizes are architecturally specified in GICR_PROPBASER.IDBits + * and in the VMAPP command's VPT_size field. + */ +static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase, + uint64_t ctbase, unsigned ptsizebits, + bool ds, PendingIrq *hpp) +{ + AddressSpace *as = &cs->gic->dma_as; + uint8_t pend; + uint32_t pendt_size = (1ULL << (ptsizebits + 1)); + int i, bit; + + hpp->prio = 0xff; + + for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) { + address_space_read(as, ptbase + i, MEMTXATTRS_UNSPECIFIED, &pend, 1); + while (pend) { + bit = ctz32(pend); + update_for_one_lpi(cs, i * 8 + bit, ctbase, ds, hpp); + pend &= ~(1 << bit); + } + } +} + +/** + * set_lpi_pending_bit: Set or clear pending bit for an LPI + * + * @cs: GICv3CPUState + * @ptbase: physical address of LPI Pending table + * @irq: LPI to change pending state for + * @level: false to clear pending state, true to set + * + * Returns true if we needed to do something, false if the pending bit + * was already at @level. + */ +static bool set_pending_table_bit(GICv3CPUState *cs, uint64_t ptbase, + int irq, bool level) +{ + AddressSpace *as = &cs->gic->dma_as; + uint64_t addr = ptbase + irq / 8; + uint8_t pend; + + address_space_read(as, addr, MEMTXATTRS_UNSPECIFIED, &pend, 1); + if (extract32(pend, irq % 8, 1) == level) { + /* Bit already at requested state, no action required */ + return false; + } + pend = deposit32(pend, irq % 8, 1, level ? 1 : 0); + address_space_write(as, addr, MEMTXATTRS_UNSPECIFIED, &pend, 1); + return true; +} + static uint8_t gicr_read_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs, int irq) { @@ -100,6 +226,87 @@ static void gicr_write_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs, int irq, cs->gicr_ipriorityr[irq] = value; } +static void gicv3_redist_update_vlpi_only(GICv3CPUState *cs) +{ + uint64_t ptbase, ctbase, idbits; + + if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) { + cs->hppvlpi.prio = 0xff; + return; + } + + ptbase = cs->gicr_vpendbaser & R_GICR_VPENDBASER_PHYADDR_MASK; + ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK; + idbits = FIELD_EX64(cs->gicr_vpropbaser, GICR_VPROPBASER, IDBITS); + + update_for_all_lpis(cs, ptbase, ctbase, idbits, true, &cs->hppvlpi); +} + +static void gicv3_redist_update_vlpi(GICv3CPUState *cs) +{ + gicv3_redist_update_vlpi_only(cs); + gicv3_cpuif_virt_irq_fiq_update(cs); +} + +static void gicr_write_vpendbaser(GICv3CPUState *cs, uint64_t newval) +{ + /* Write @newval to GICR_VPENDBASER, handling its effects */ + bool oldvalid = FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID); + bool newvalid = FIELD_EX64(newval, GICR_VPENDBASER, VALID); + bool pendinglast; + + /* + * The DIRTY bit is read-only and for us is always zero; + * other fields are writeable. + */ + newval &= R_GICR_VPENDBASER_INNERCACHE_MASK | + R_GICR_VPENDBASER_SHAREABILITY_MASK | + R_GICR_VPENDBASER_PHYADDR_MASK | + R_GICR_VPENDBASER_OUTERCACHE_MASK | + R_GICR_VPENDBASER_PENDINGLAST_MASK | + R_GICR_VPENDBASER_IDAI_MASK | + R_GICR_VPENDBASER_VALID_MASK; + + if (oldvalid && newvalid) { + /* + * Changing other fields while VALID is 1 is UNPREDICTABLE; + * we choose to log and ignore the write. + */ + if (cs->gicr_vpendbaser ^ newval) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Changing GICR_VPENDBASER when VALID=1 " + "is UNPREDICTABLE\n", __func__); + } + return; + } + if (!oldvalid && !newvalid) { + cs->gicr_vpendbaser = newval; + return; + } + + if (newvalid) { + /* + * Valid going from 0 to 1: update hppvlpi from tables. + * If IDAI is 0 we are allowed to use the info we cached in + * the IMPDEF area of the table. + * PendingLast is RES1 when we make this transition. + */ + pendinglast = true; + } else { + /* + * Valid going from 1 to 0: + * Set PendingLast if there was a pending enabled interrupt + * for the vPE that was just descheduled. + * If we cache info in the IMPDEF area, write it out here. + */ + pendinglast = cs->hppvlpi.prio != 0xff; + } + + newval = FIELD_DP64(newval, GICR_VPENDBASER, PENDINGLAST, pendinglast); + cs->gicr_vpendbaser = newval; + gicv3_redist_update_vlpi(cs); +} + static MemTxResult gicr_readb(GICv3CPUState *cs, hwaddr offset, uint64_t *data, MemTxAttrs attrs) { @@ -234,7 +441,24 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, *data = cs->gicr_nsacr; return MEMTX_OK; case GICR_IDREGS ... GICR_IDREGS + 0x2f: - *data = gicv3_idreg(offset - GICR_IDREGS); + *data = gicv3_idreg(cs->gic, offset - GICR_IDREGS, GICV3_PIDR0_REDIST); + return MEMTX_OK; + /* + * VLPI frame registers. We don't need a version check for + * VPROPBASER and VPENDBASER because gicv3_redist_size() will + * prevent pre-v4 GIC from passing us offsets this high. + */ + case GICR_VPROPBASER: + *data = extract64(cs->gicr_vpropbaser, 0, 32); + return MEMTX_OK; + case GICR_VPROPBASER + 4: + *data = extract64(cs->gicr_vpropbaser, 32, 32); + return MEMTX_OK; + case GICR_VPENDBASER: + *data = extract64(cs->gicr_vpendbaser, 0, 32); + return MEMTX_OK; + case GICR_VPENDBASER + 4: + *data = extract64(cs->gicr_vpendbaser, 32, 32); return MEMTX_OK; default: return MEMTX_ERROR; @@ -379,6 +603,23 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, "%s: invalid guest write to RO register at offset " TARGET_FMT_plx "\n", __func__, offset); return MEMTX_OK; + /* + * VLPI frame registers. We don't need a version check for + * VPROPBASER and VPENDBASER because gicv3_redist_size() will + * prevent pre-v4 GIC from passing us offsets this high. + */ + case GICR_VPROPBASER: + cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 0, 32, value); + return MEMTX_OK; + case GICR_VPROPBASER + 4: + cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 32, 32, value); + return MEMTX_OK; + case GICR_VPENDBASER: + gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 0, 32, value)); + return MEMTX_OK; + case GICR_VPENDBASER + 4: + gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 32, 32, value)); + return MEMTX_OK; default: return MEMTX_ERROR; } @@ -397,6 +638,17 @@ static MemTxResult gicr_readll(GICv3CPUState *cs, hwaddr offset, case GICR_PENDBASER: *data = cs->gicr_pendbaser; return MEMTX_OK; + /* + * VLPI frame registers. We don't need a version check for + * VPROPBASER and VPENDBASER because gicv3_redist_size() will + * prevent pre-v4 GIC from passing us offsets this high. + */ + case GICR_VPROPBASER: + *data = cs->gicr_vpropbaser; + return MEMTX_OK; + case GICR_VPENDBASER: + *data = cs->gicr_vpendbaser; + return MEMTX_OK; default: return MEMTX_ERROR; } @@ -418,6 +670,17 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset, "%s: invalid guest write to RO register at offset " TARGET_FMT_plx "\n", __func__, offset); return MEMTX_OK; + /* + * VLPI frame registers. We don't need a version check for + * VPROPBASER and VPENDBASER because gicv3_redist_size() will + * prevent pre-v4 GIC from passing us offsets this high. + */ + case GICR_VPROPBASER: + cs->gicr_vpropbaser = value; + return MEMTX_OK; + case GICR_VPENDBASER: + gicr_write_vpendbaser(cs, value); + return MEMTX_OK; default: return MEMTX_ERROR; } @@ -442,8 +705,8 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data, * in the memory map); if so then the GIC has multiple MemoryRegions * for the redistributors. */ - cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE; - offset %= GICV3_REDIST_SIZE; + cpuidx = region->cpuidx + offset / gicv3_redist_size(s); + offset %= gicv3_redist_size(s); cs = &s->cpu[cpuidx]; @@ -501,8 +764,8 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, * in the memory map); if so then the GIC has multiple MemoryRegions * for the redistributors. */ - cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE; - offset %= GICV3_REDIST_SIZE; + cpuidx = region->cpuidx + offset / gicv3_redist_size(s); + offset %= gicv3_redist_size(s); cs = &s->cpu[cpuidx]; @@ -542,34 +805,11 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, static void gicv3_redist_check_lpi_priority(GICv3CPUState *cs, int irq) { - AddressSpace *as = &cs->gic->dma_as; - uint64_t lpict_baddr; - uint8_t lpite; - uint8_t prio; + uint64_t lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK; - lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK; - - address_space_read(as, lpict_baddr + ((irq - GICV3_LPI_INTID_START) * - sizeof(lpite)), MEMTXATTRS_UNSPECIFIED, &lpite, - sizeof(lpite)); - - if (!(lpite & LPI_CTE_ENABLED)) { - return; - } - - if (cs->gic->gicd_ctlr & GICD_CTLR_DS) { - prio = lpite & LPI_PRIORITY_MASK; - } else { - prio = ((lpite & LPI_PRIORITY_MASK) >> 1) | 0x80; - } - - if ((prio < cs->hpplpi.prio) || - ((prio == cs->hpplpi.prio) && (irq <= cs->hpplpi.irq))) { - cs->hpplpi.irq = irq; - cs->hpplpi.prio = prio; - /* LPIs are always non-secure Grp1 interrupts */ - cs->hpplpi.grp = GICV3_G1NS; - } + update_for_one_lpi(cs, irq, lpict_baddr, + cs->gic->gicd_ctlr & GICD_CTLR_DS, + &cs->hpplpi); } void gicv3_redist_update_lpi_only(GICv3CPUState *cs) @@ -581,11 +821,7 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs) * priority is lower than the last computed high priority lpi interrupt. * If yes, replace current LPI as the new high priority lpi interrupt. */ - AddressSpace *as = &cs->gic->dma_as; - uint64_t lpipt_baddr; - uint32_t pendt_size = 0; - uint8_t pend; - int i, bit; + uint64_t lpipt_baddr, lpict_baddr; uint64_t idbits; idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS), @@ -595,23 +831,11 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs) return; } - cs->hpplpi.prio = 0xff; - lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; + lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK; - /* Determine the highest priority pending interrupt among LPIs */ - pendt_size = (1ULL << (idbits + 1)); - - for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) { - address_space_read(as, lpipt_baddr + i, MEMTXATTRS_UNSPECIFIED, &pend, - sizeof(pend)); - - while (pend) { - bit = ctz32(pend); - gicv3_redist_check_lpi_priority(cs, i * 8 + bit); - pend &= ~(1 << bit); - } - } + update_for_all_lpis(cs, lpipt_baddr, lpict_baddr, idbits, + cs->gic->gicd_ctlr & GICD_CTLR_DS, &cs->hpplpi); } void gicv3_redist_update_lpi(GICv3CPUState *cs) @@ -626,30 +850,13 @@ void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level) * This function updates the pending bit in lpi pending table for * the irq being activated or deactivated. */ - AddressSpace *as = &cs->gic->dma_as; uint64_t lpipt_baddr; - bool ispend = false; - uint8_t pend; - /* - * get the bit value corresponding to this irq in the - * lpi pending table - */ lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; - - address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)), - MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend)); - - ispend = extract32(pend, irq % 8, 1); - - /* no change in the value of pending bit, return */ - if (ispend == level) { + if (!set_pending_table_bit(cs, lpipt_baddr, irq, level)) { + /* no change in the value of pending bit, return */ return; } - pend = deposit32(pend, irq % 8, 1, level ? 1 : 0); - - address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)), - MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend)); /* * check if this LPI is better than the current hpplpi, if yes @@ -681,6 +888,17 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level) gicv3_redist_lpi_pending(cs, irq, level); } +void gicv3_redist_inv_lpi(GICv3CPUState *cs, int irq) +{ + /* + * The only cached information for LPIs we have is the HPPLPI. + * We could be cleverer about identifying when we don't need + * to do a full rescan of the pending table, but until we find + * this is a performance issue, just always recalculate. + */ + gicv3_redist_update_lpi(cs); +} + void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq) { /* @@ -691,11 +909,9 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq) * we choose to NOP. If LPIs are disabled on source there's nothing * to be transferred anyway. */ - AddressSpace *as = &src->gic->dma_as; uint64_t idbits; uint32_t pendt_size; uint64_t src_baddr; - uint8_t src_pend; if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) { @@ -714,15 +930,10 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq) src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; - address_space_read(as, src_baddr + (irq / 8), - MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend)); - if (!extract32(src_pend, irq % 8, 1)) { + if (!set_pending_table_bit(src, src_baddr, irq, 0)) { /* Not pending on source, nothing to do */ return; } - src_pend &= ~(1 << (irq % 8)); - address_space_write(as, src_baddr + (irq / 8), - MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend)); if (irq == src->hpplpi.irq) { /* * We just made this LPI not-pending so only need to update @@ -788,6 +999,117 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest) gicv3_redist_update_lpi(dest); } +void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level) +{ + /* + * Change the pending state of the specified vLPI. + * Unlike gicv3_redist_process_vlpi(), we know here that the + * vCPU is definitely resident on this redistributor, and that + * the irq is in range. + */ + uint64_t vptbase, ctbase; + + vptbase = FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, PHYADDR) << 16; + + if (set_pending_table_bit(cs, vptbase, irq, level)) { + if (level) { + /* Check whether this vLPI is now the best */ + ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK; + update_for_one_lpi(cs, irq, ctbase, true, &cs->hppvlpi); + gicv3_cpuif_virt_irq_fiq_update(cs); + } else { + /* Only need to recalculate if this was previously the best vLPI */ + if (irq == cs->hppvlpi.irq) { + gicv3_redist_update_vlpi(cs); + } + } + } +} + +void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr, + int doorbell, int level) +{ + bool bit_changed; + bool resident = vcpu_resident(cs, vptaddr); + uint64_t ctbase; + + if (resident) { + uint32_t idbits = FIELD_EX64(cs->gicr_vpropbaser, GICR_VPROPBASER, IDBITS); + if (irq >= (1ULL << (idbits + 1))) { + return; + } + } + + bit_changed = set_pending_table_bit(cs, vptaddr, irq, level); + if (resident && bit_changed) { + if (level) { + /* Check whether this vLPI is now the best */ + ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK; + update_for_one_lpi(cs, irq, ctbase, true, &cs->hppvlpi); + gicv3_cpuif_virt_irq_fiq_update(cs); + } else { + /* Only need to recalculate if this was previously the best vLPI */ + if (irq == cs->hppvlpi.irq) { + gicv3_redist_update_vlpi(cs); + } + } + } + + if (!resident && level && doorbell != INTID_SPURIOUS && + (cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) { + /* vCPU is not currently resident: ring the doorbell */ + gicv3_redist_process_lpi(cs, doorbell, 1); + } +} + +void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr, + GICv3CPUState *dest, uint64_t dest_vptaddr, + int irq, int doorbell) +{ + /* + * Move the specified vLPI's pending state from the source redistributor + * to the destination. + */ + if (!set_pending_table_bit(src, src_vptaddr, irq, 0)) { + /* Not pending on source, nothing to do */ + return; + } + if (vcpu_resident(src, src_vptaddr) && irq == src->hppvlpi.irq) { + /* + * Update src's cached highest-priority pending vLPI if we just made + * it not-pending + */ + gicv3_redist_update_vlpi(src); + } + /* + * Mark the vLPI pending on the destination (ringing the doorbell + * if the vCPU isn't resident) + */ + gicv3_redist_process_vlpi(dest, irq, dest_vptaddr, doorbell, irq); +} + +void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr) +{ + if (!vcpu_resident(cs, vptaddr)) { + /* We don't have anything cached if the vCPU isn't resident */ + return; + } + + /* Otherwise, our only cached information is the HPPVLPI info */ + gicv3_redist_update_vlpi(cs); +} + +void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr) +{ + /* + * The only cached information for LPIs we have is the HPPLPI. + * We could be cleverer about identifying when we don't need + * to do a full rescan of the pending table, but until we find + * this is a performance issue, just always recalculate. + */ + gicv3_redist_vinvall(cs, vptaddr); +} + void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level) { /* Update redistributor state for a change in an external PPI input line */ diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index 4534ee248d..a289510bdb 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -31,7 +31,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" - +#include "hw/intc/exynos4210_combiner.h" #include "hw/arm/exynos4210.h" #include "hw/hw.h" #include "hw/irq.h" @@ -48,36 +48,7 @@ #define DPRINTF(fmt, ...) do {} while (0) #endif -#define IIC_NGRP 64 /* Internal Interrupt Combiner - Groups number */ -#define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner - Interrupts number */ #define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */ -#define IIC_REGSET_SIZE 0x41 - -/* - * State for each output signal of internal combiner - */ -typedef struct CombinerGroupState { - uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ - uint8_t src_pending; /* Pending source interrupts before masking */ -} CombinerGroupState; - -#define TYPE_EXYNOS4210_COMBINER "exynos4210.combiner" -OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210CombinerState, EXYNOS4210_COMBINER) - -struct Exynos4210CombinerState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - struct CombinerGroupState group[IIC_NGRP]; - uint32_t reg_set[IIC_REGSET_SIZE]; - uint32_t icipsr[2]; - uint32_t external; /* 1 means that this combiner is external */ - - qemu_irq output_irq[IIC_NGRP]; -}; static const VMStateDescription vmstate_exynos4210_combiner_group_state = { .name = "exynos4210.combiner.groupstate", @@ -105,83 +76,6 @@ static const VMStateDescription vmstate_exynos4210_combiner = { } }; -/* - * Get Combiner input GPIO into irqs structure - */ -void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, - int ext) -{ - int n; - int bit; - int max; - qemu_irq *irq; - - max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ : - EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; - irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq; - - /* - * Some IRQs of Int/External Combiner are going to two Combiners groups, - * so let split them. - */ - for (n = 0; n < max; n++) { - - bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); - - switch (n) { - /* MDNIE_LCD1 INTG1 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]); - continue; - - /* TMU INTG3 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]); - continue; - - /* LCD1 INTG12 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]); - continue; - - /* Multi-Core Timer INTG12 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - - /* Multi-Core Timer INTG35 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - - /* Multi-Core Timer INTG51 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - - /* Multi-Core Timer INTG53 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - } - - irq[n] = qdev_get_gpio_in(dev, n); - } -} - static uint64_t exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size) { diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index bc73d1f115..fcca85c6c6 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -27,157 +27,10 @@ #include "qemu/module.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/intc/exynos4210_gic.h" #include "hw/arm/exynos4210.h" #include "qom/object.h" -enum ExtGicId { - EXT_GIC_ID_MDMA_LCD0 = 66, - EXT_GIC_ID_PDMA0, - EXT_GIC_ID_PDMA1, - EXT_GIC_ID_TIMER0, - EXT_GIC_ID_TIMER1, - EXT_GIC_ID_TIMER2, - EXT_GIC_ID_TIMER3, - EXT_GIC_ID_TIMER4, - EXT_GIC_ID_MCT_L0, - EXT_GIC_ID_WDT, - EXT_GIC_ID_RTC_ALARM, - EXT_GIC_ID_RTC_TIC, - EXT_GIC_ID_GPIO_XB, - EXT_GIC_ID_GPIO_XA, - EXT_GIC_ID_MCT_L1, - EXT_GIC_ID_IEM_APC, - EXT_GIC_ID_IEM_IEC, - EXT_GIC_ID_NFC, - EXT_GIC_ID_UART0, - EXT_GIC_ID_UART1, - EXT_GIC_ID_UART2, - EXT_GIC_ID_UART3, - EXT_GIC_ID_UART4, - EXT_GIC_ID_MCT_G0, - EXT_GIC_ID_I2C0, - EXT_GIC_ID_I2C1, - EXT_GIC_ID_I2C2, - EXT_GIC_ID_I2C3, - EXT_GIC_ID_I2C4, - EXT_GIC_ID_I2C5, - EXT_GIC_ID_I2C6, - EXT_GIC_ID_I2C7, - EXT_GIC_ID_SPI0, - EXT_GIC_ID_SPI1, - EXT_GIC_ID_SPI2, - EXT_GIC_ID_MCT_G1, - EXT_GIC_ID_USB_HOST, - EXT_GIC_ID_USB_DEVICE, - EXT_GIC_ID_MODEMIF, - EXT_GIC_ID_HSMMC0, - EXT_GIC_ID_HSMMC1, - EXT_GIC_ID_HSMMC2, - EXT_GIC_ID_HSMMC3, - EXT_GIC_ID_SDMMC, - EXT_GIC_ID_MIPI_CSI_4LANE, - EXT_GIC_ID_MIPI_DSI_4LANE, - EXT_GIC_ID_MIPI_CSI_2LANE, - EXT_GIC_ID_MIPI_DSI_2LANE, - EXT_GIC_ID_ONENAND_AUDI, - EXT_GIC_ID_ROTATOR, - EXT_GIC_ID_FIMC0, - EXT_GIC_ID_FIMC1, - EXT_GIC_ID_FIMC2, - EXT_GIC_ID_FIMC3, - EXT_GIC_ID_JPEG, - EXT_GIC_ID_2D, - EXT_GIC_ID_PCIe, - EXT_GIC_ID_MIXER, - EXT_GIC_ID_HDMI, - EXT_GIC_ID_HDMI_I2C, - EXT_GIC_ID_MFC, - EXT_GIC_ID_TVENC, -}; - -enum ExtInt { - EXT_GIC_ID_EXTINT0 = 48, - EXT_GIC_ID_EXTINT1, - EXT_GIC_ID_EXTINT2, - EXT_GIC_ID_EXTINT3, - EXT_GIC_ID_EXTINT4, - EXT_GIC_ID_EXTINT5, - EXT_GIC_ID_EXTINT6, - EXT_GIC_ID_EXTINT7, - EXT_GIC_ID_EXTINT8, - EXT_GIC_ID_EXTINT9, - EXT_GIC_ID_EXTINT10, - EXT_GIC_ID_EXTINT11, - EXT_GIC_ID_EXTINT12, - EXT_GIC_ID_EXTINT13, - EXT_GIC_ID_EXTINT14, - EXT_GIC_ID_EXTINT15 -}; - -/* - * External GIC sources which are not from External Interrupt Combiner or - * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ, - * which is INTG16 in Internal Interrupt Combiner. - */ - -static const uint32_t -combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { - /* int combiner groups 16-19 */ - { }, { }, { }, { }, - /* int combiner group 20 */ - { 0, EXT_GIC_ID_MDMA_LCD0 }, - /* int combiner group 21 */ - { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 }, - /* int combiner group 22 */ - { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2, - EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 }, - /* int combiner group 23 */ - { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC }, - /* int combiner group 24 */ - { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA }, - /* int combiner group 25 */ - { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC }, - /* int combiner group 26 */ - { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3, - EXT_GIC_ID_UART4 }, - /* int combiner group 27 */ - { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3, - EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6, - EXT_GIC_ID_I2C7 }, - /* int combiner group 28 */ - { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST}, - /* int combiner group 29 */ - { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2, - EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC }, - /* int combiner group 30 */ - { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE }, - /* int combiner group 31 */ - { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE }, - /* int combiner group 32 */ - { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 }, - /* int combiner group 33 */ - { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 }, - /* int combiner group 34 */ - { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC }, - /* int combiner group 35 */ - { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, - /* int combiner group 36 */ - { EXT_GIC_ID_MIXER }, - /* int combiner group 37 */ - { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6, - EXT_GIC_ID_EXTINT7 }, - /* groups 38-50 */ - { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, - /* int combiner group 51 */ - { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, - /* group 52 */ - { }, - /* int combiner group 53 */ - { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, - /* groups 54-63 */ - { }, { }, { }, { }, { }, { }, { }, { }, { }, { } -}; - #define EXYNOS4210_GIC_NIRQ 160 #define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE 0x10000 @@ -192,92 +45,6 @@ combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { #define EXYNOS4210_GIC_CPU_REGION_SIZE 0x100 #define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000 -static void exynos4210_irq_handler(void *opaque, int irq, int level) -{ - Exynos4210Irq *s = (Exynos4210Irq *)opaque; - - /* Bypass */ - qemu_set_irq(s->board_irqs[irq], level); -} - -/* - * Initialize exynos4210 IRQ subsystem stub. - */ -qemu_irq *exynos4210_init_irq(Exynos4210Irq *s) -{ - return qemu_allocate_irqs(exynos4210_irq_handler, s, - EXYNOS4210_MAX_INT_COMBINER_IN_IRQ); -} - -/* - * Initialize board IRQs. - * These IRQs contain splitted Int/External Combiner and External Gic IRQs. - */ -void exynos4210_init_board_irqs(Exynos4210Irq *s) -{ - uint32_t grp, bit, irq_id, n; - - for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) { - irq_id = 0; - if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) || - n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) { - /* MCT_G0 is passed to External GIC */ - irq_id = EXT_GIC_ID_MCT_G0; - } - if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) || - n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) { - /* MCT_G1 is passed to External and GIC */ - irq_id = EXT_GIC_ID_MCT_G1; - } - if (irq_id) { - s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], - s->ext_gic_irq[irq_id-32]); - } else { - s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], - s->ext_combiner_irq[n]); - } - } - for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) { - /* these IDs are passed to Internal Combiner and External GIC */ - grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n); - bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); - irq_id = combiner_grp_to_gic_id[grp - - EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit]; - - if (irq_id) { - s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], - s->ext_gic_irq[irq_id-32]); - } - } -} - -/* - * Get IRQ number from exynos4210 IRQ subsystem stub. - * To identify IRQ source use internal combiner group and bit number - * grp - group number - * bit - bit number inside group - */ -uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit) -{ - return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit); -} - -/********* GIC part *********/ - -#define TYPE_EXYNOS4210_GIC "exynos4210.gic" -OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210GicState, EXYNOS4210_GIC) - -struct Exynos4210GicState { - SysBusDevice parent_obj; - - MemoryRegion cpu_container; - MemoryRegion dist_container; - MemoryRegion cpu_alias[EXYNOS4210_NCPUS]; - MemoryRegion dist_alias[EXYNOS4210_NCPUS]; - uint32_t num_cpu; - DeviceState *gic; -}; - static void exynos4210_gic_set_irq(void *opaque, int irq, int level) { Exynos4210GicState *s = (Exynos4210GicState *)opaque; @@ -289,10 +56,6 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) Object *obj = OBJECT(dev); Exynos4210GicState *s = EXYNOS4210_GIC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; - const char dist_prefix[] = "exynos4210-gic-alias_dist"; - char cpu_alias_name[sizeof(cpu_prefix) + 3]; - char dist_alias_name[sizeof(cpu_prefix) + 3]; SysBusDevice *gicbusdev; uint32_t n = s->num_cpu; uint32_t i; @@ -320,10 +83,12 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) * enough room for the cpu numbers. gcc 9.2.1 on 32-bit x86 * doesn't figure this out, otherwise and gives spurious warnings. */ - assert(n <= EXYNOS4210_NCPUS); + assert(n <= EXYNOS4210_GIC_NCPUS); for (i = 0; i < n; i++) { + g_autofree char *cpu_alias_name = g_strdup_printf("exynos4210-gic-alias_cpu%u", i); + g_autofree char *dist_alias_name = g_strdup_printf("exynos4210-gic-alias_dist%u", i); + /* Map CPU interface per SMP Core */ - sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); memory_region_init_alias(&s->cpu_alias[i], obj, cpu_alias_name, sysbus_mmio_get_region(gicbusdev, 1), @@ -333,7 +98,6 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]); /* Map Distributor per SMP Core */ - sprintf(dist_alias_name, "%s%x", dist_prefix, i); memory_region_init_alias(&s->dist_alias[i], obj, dist_alias_name, sysbus_mmio_get_region(gicbusdev, 0), @@ -373,110 +137,3 @@ static void exynos4210_gic_register_types(void) } type_init(exynos4210_gic_register_types) - -/* IRQ OR Gate struct. - * - * This device models an OR gate. There are n_in input qdev gpio lines and one - * output sysbus IRQ line. The output IRQ level is formed as OR between all - * gpio inputs. - */ - -#define TYPE_EXYNOS4210_IRQ_GATE "exynos4210.irq_gate" -OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210IRQGateState, EXYNOS4210_IRQ_GATE) - -struct Exynos4210IRQGateState { - SysBusDevice parent_obj; - - uint32_t n_in; /* inputs amount */ - uint32_t *level; /* input levels */ - qemu_irq out; /* output IRQ */ -}; - -static Property exynos4210_irq_gate_properties[] = { - DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_exynos4210_irq_gate = { - .name = "exynos4210.irq_gate", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, n_in), - VMSTATE_END_OF_LIST() - } -}; - -/* Process a change in IRQ input. */ -static void exynos4210_irq_gate_handler(void *opaque, int irq, int level) -{ - Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque; - uint32_t i; - - assert(irq < s->n_in); - - s->level[irq] = level; - - for (i = 0; i < s->n_in; i++) { - if (s->level[i] >= 1) { - qemu_irq_raise(s->out); - return; - } - } - - qemu_irq_lower(s->out); -} - -static void exynos4210_irq_gate_reset(DeviceState *d) -{ - Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(d); - - memset(s->level, 0, s->n_in * sizeof(*s->level)); -} - -/* - * IRQ Gate initialization. - */ -static void exynos4210_irq_gate_init(Object *obj) -{ - Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - sysbus_init_irq(sbd, &s->out); -} - -static void exynos4210_irq_gate_realize(DeviceState *dev, Error **errp) -{ - Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(dev); - - /* Allocate general purpose input signals and connect a handler to each of - * them */ - qdev_init_gpio_in(dev, exynos4210_irq_gate_handler, s->n_in); - - s->level = g_malloc0(s->n_in * sizeof(*s->level)); -} - -static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = exynos4210_irq_gate_reset; - dc->vmsd = &vmstate_exynos4210_irq_gate; - device_class_set_props(dc, exynos4210_irq_gate_properties); - dc->realize = exynos4210_irq_gate_realize; -} - -static const TypeInfo exynos4210_irq_gate_info = { - .name = TYPE_EXYNOS4210_IRQ_GATE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210IRQGateState), - .instance_init = exynos4210_irq_gate_init, - .class_init = exynos4210_irq_gate_class_init, -}; - -static void exynos4210_irq_gate_register_types(void) -{ - type_register_static(&exynos4210_irq_gate_info); -} - -type_init(exynos4210_irq_gate_register_types) diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 2bf1baef04..29d5cdc1b6 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -77,6 +77,7 @@ * Redistributor frame offsets from RD_base */ #define GICR_SGI_OFFSET 0x10000 +#define GICR_VLPI_OFFSET 0x20000 /* * Redistributor registers, offsets from RD_base @@ -109,6 +110,10 @@ #define GICR_IGRPMODR0 (GICR_SGI_OFFSET + 0x0D00) #define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00) +/* VLPI redistributor registers, offsets from VLPI_base */ +#define GICR_VPROPBASER (GICR_VLPI_OFFSET + 0x70) +#define GICR_VPENDBASER (GICR_VLPI_OFFSET + 0x78) + #define GICR_CTLR_ENABLE_LPIS (1U << 0) #define GICR_CTLR_CES (1U << 1) #define GICR_CTLR_RWP (1U << 3) @@ -143,6 +148,22 @@ FIELD(GICR_PENDBASER, PTZ, 62, 1) #define GICR_PROPBASER_IDBITS_THRESHOLD 0xd +/* These are the GICv4 VPROPBASER and VPENDBASER layouts; v4.1 is different */ +FIELD(GICR_VPROPBASER, IDBITS, 0, 5) +FIELD(GICR_VPROPBASER, INNERCACHE, 7, 3) +FIELD(GICR_VPROPBASER, SHAREABILITY, 10, 2) +FIELD(GICR_VPROPBASER, PHYADDR, 12, 40) +FIELD(GICR_VPROPBASER, OUTERCACHE, 56, 3) + +FIELD(GICR_VPENDBASER, INNERCACHE, 7, 3) +FIELD(GICR_VPENDBASER, SHAREABILITY, 10, 2) +FIELD(GICR_VPENDBASER, PHYADDR, 16, 36) +FIELD(GICR_VPENDBASER, OUTERCACHE, 56, 3) +FIELD(GICR_VPENDBASER, DIRTY, 60, 1) +FIELD(GICR_VPENDBASER, PENDINGLAST, 61, 1) +FIELD(GICR_VPENDBASER, IDAI, 62, 1) +FIELD(GICR_VPENDBASER, VALID, 63, 1) + #define ICC_CTLR_EL1_CBPR (1U << 0) #define ICC_CTLR_EL1_EOIMODE (1U << 1) #define ICC_CTLR_EL1_PMHE (1U << 6) @@ -280,6 +301,7 @@ FIELD(GITS_CTLR, ENABLED, 0, 1) FIELD(GITS_CTLR, QUIESCENT, 31, 1) FIELD(GITS_TYPER, PHYSICAL, 0, 1) +FIELD(GITS_TYPER, VIRTUAL, 1, 1) FIELD(GITS_TYPER, ITT_ENTRY_SIZE, 4, 4) FIELD(GITS_TYPER, IDBITS, 8, 5) FIELD(GITS_TYPER, DEVBITS, 13, 5) @@ -287,6 +309,7 @@ FIELD(GITS_TYPER, SEIS, 18, 1) FIELD(GITS_TYPER, PTA, 19, 1) FIELD(GITS_TYPER, CIDBITS, 32, 4) FIELD(GITS_TYPER, CIL, 36, 1) +FIELD(GITS_TYPER, VMOVP, 37, 1) #define GITS_IDREGS 0xFFD0 @@ -298,6 +321,7 @@ FIELD(GITS_TYPER, CIL, 36, 1) #define GITS_BASER_PAGESIZE_64K 2 #define GITS_BASER_TYPE_DEVICE 1ULL +#define GITS_BASER_TYPE_VPE 2ULL #define GITS_BASER_TYPE_COLLECTION 4ULL #define GITS_PAGE_SIZE_4K 0x1000 @@ -327,6 +351,13 @@ FIELD(GITS_TYPER, CIL, 36, 1) #define GITS_CMD_INVALL 0x0D #define GITS_CMD_MOVALL 0x0E #define GITS_CMD_DISCARD 0x0F +#define GITS_CMD_VMOVI 0x21 +#define GITS_CMD_VMOVP 0x22 +#define GITS_CMD_VSYNC 0x25 +#define GITS_CMD_VMAPP 0x29 +#define GITS_CMD_VMAPTI 0x2A +#define GITS_CMD_VMAPI 0x2B +#define GITS_CMD_VINVALL 0x2D /* MAPC command fields */ #define ICID_LENGTH 16 @@ -366,6 +397,46 @@ FIELD(MOVI_0, DEVICEID, 32, 32) FIELD(MOVI_1, EVENTID, 0, 32) FIELD(MOVI_2, ICID, 0, 16) +/* INV command fields */ +FIELD(INV_0, DEVICEID, 32, 32) +FIELD(INV_1, EVENTID, 0, 32) + +/* VMAPI, VMAPTI command fields */ +FIELD(VMAPTI_0, DEVICEID, 32, 32) +FIELD(VMAPTI_1, EVENTID, 0, 32) +FIELD(VMAPTI_1, VPEID, 32, 16) +FIELD(VMAPTI_2, VINTID, 0, 32) /* VMAPTI only */ +FIELD(VMAPTI_2, DOORBELL, 32, 32) + +/* VMAPP command fields */ +FIELD(VMAPP_0, ALLOC, 8, 1) /* GICv4.1 only */ +FIELD(VMAPP_0, PTZ, 9, 1) /* GICv4.1 only */ +FIELD(VMAPP_0, VCONFADDR, 16, 36) /* GICv4.1 only */ +FIELD(VMAPP_1, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */ +FIELD(VMAPP_1, VPEID, 32, 16) +FIELD(VMAPP_2, RDBASE, 16, 36) +FIELD(VMAPP_2, V, 63, 1) +FIELD(VMAPP_3, VPTSIZE, 0, 8) /* For GICv4.0, bits [7:6] are RES0 */ +FIELD(VMAPP_3, VPTADDR, 16, 36) + +/* VMOVP command fields */ +FIELD(VMOVP_0, SEQNUM, 32, 16) /* not used for GITS_TYPER.VMOVP == 1 */ +FIELD(VMOVP_1, ITSLIST, 0, 16) /* not used for GITS_TYPER.VMOVP == 1 */ +FIELD(VMOVP_1, VPEID, 32, 16) +FIELD(VMOVP_2, RDBASE, 16, 36) +FIELD(VMOVP_2, DB, 63, 1) /* GICv4.1 only */ +FIELD(VMOVP_3, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */ + +/* VMOVI command fields */ +FIELD(VMOVI_0, DEVICEID, 32, 32) +FIELD(VMOVI_1, EVENTID, 0, 32) +FIELD(VMOVI_1, VPEID, 32, 16) +FIELD(VMOVI_2, D, 0, 1) +FIELD(VMOVI_2, DOORBELL, 32, 32) + +/* VINVALL command fields */ +FIELD(VINVALL_1, VPEID, 32, 16) + /* * 12 bytes Interrupt translation Table Entry size * as per Table 5.3 in GICv3 spec @@ -419,6 +490,20 @@ FIELD(DTE, ITTADDR, 6, 44) FIELD(CTE, VALID, 0, 1) FIELD(CTE, RDBASE, 1, RDBASE_PROCNUM_LENGTH) +/* + * 8 bytes VPE table entry size: + * Valid = 1 bit, VPTsize = 5 bits, VPTaddr = 36 bits, RDbase = 16 bits + * + * Field sizes for Valid and size are mandated; field sizes for RDbase + * and VPT_addr are IMPDEF. + */ +#define GITS_VPE_SIZE 0x8ULL + +FIELD(VTE, VALID, 0, 1) +FIELD(VTE, VPTSIZE, 1, 5) +FIELD(VTE, VPTADDR, 6, 36) +FIELD(VTE, RDBASE, 42, RDBASE_PROCNUM_LENGTH) + /* Special interrupt IDs */ #define INTID_SECURE 1020 #define INTID_NONSECURE 1021 @@ -426,6 +511,27 @@ FIELD(CTE, RDBASE, 1, RDBASE_PROCNUM_LENGTH) /* Functions internal to the emulated GICv3 */ +/** + * gicv3_redist_size: + * @s: GICv3State + * + * Return the size of the redistributor register frame in bytes + * (which depends on what GIC version this is) + */ +static inline int gicv3_redist_size(GICv3State *s) +{ + /* + * Redistributor size is controlled by the redistributor GICR_TYPER.VLPIS. + * It's the same for every redistributor in the GIC, so arbitrarily + * use the register field in the first one. + */ + if (s->cpu[0].gicr_typer & GICR_TYPER_VLPIS) { + return GICV4_REDIST_SIZE; + } else { + return GICV3_REDIST_SIZE; + } +} + /** * gicv3_intid_is_special: * @intid: interrupt ID @@ -490,6 +596,36 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, void gicv3_dist_set_irq(GICv3State *s, int irq, int level); void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level); void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level); +/** + * gicv3_redist_process_vlpi: + * @cs: GICv3CPUState + * @irq: (virtual) interrupt number + * @vptaddr: (guest) address of VLPI table + * @doorbell: doorbell (physical) interrupt number (1023 for "no doorbell") + * @level: level to set @irq to + * + * Process a virtual LPI being directly injected by the ITS. This function + * will update the VLPI table specified by @vptaddr and @vptsize. If the + * vCPU corresponding to that VLPI table is currently running on + * the CPU associated with this redistributor, directly inject the VLPI + * @irq. If the vCPU is not running on this CPU, raise the doorbell + * interrupt instead. + */ +void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr, + int doorbell, int level); +/** + * gicv3_redist_vlpi_pending: + * @cs: GICv3CPUState + * @irq: (virtual) interrupt number + * @level: level to set @irq to + * + * Set/clear the pending status of a virtual LPI in the vLPI table + * that this redistributor is currently using. (The difference between + * this and gicv3_redist_process_vlpi() is that this is called from + * the cpuif and does not need to do the not-running-on-this-vcpu checks.) + */ +void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level); + void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level); /** * gicv3_redist_update_lpi: @@ -509,6 +645,23 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs); * an incoming migration has loaded new state. */ void gicv3_redist_update_lpi_only(GICv3CPUState *cs); +/** + * gicv3_redist_inv_lpi: + * @cs: GICv3CPUState + * @irq: LPI to invalidate cached information for + * + * Forget or update any cached information associated with this LPI. + */ +void gicv3_redist_inv_lpi(GICv3CPUState *cs, int irq); +/** + * gicv3_redist_inv_vlpi: + * @cs: GICv3CPUState + * @irq: vLPI to invalidate cached information for + * @vptaddr: (guest) address of vLPI table + * + * Forget or update any cached information associated with this vLPI. + */ +void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr); /** * gicv3_redist_mov_lpi: * @src: source redistributor @@ -529,6 +682,30 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq); * by the ITS MOVALL command. */ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest); +/** + * gicv3_redist_mov_vlpi: + * @src: source redistributor + * @src_vptaddr: (guest) address of source VLPI table + * @dest: destination redistributor + * @dest_vptaddr: (guest) address of destination VLPI table + * @irq: VLPI to update + * @doorbell: doorbell for destination (1023 for "no doorbell") + * + * Move the pending state of the specified VLPI from @src to @dest, + * as required by the ITS VMOVI command. + */ +void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr, + GICv3CPUState *dest, uint64_t dest_vptaddr, + int irq, int doorbell); +/** + * gicv3_redist_vinvall: + * @cs: GICv3CPUState + * @vptaddr: address of VLPI pending table + * + * On redistributor @cs, invalidate all cached information associated + * with the vCPU defined by @vptaddr. + */ +void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr); void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns); void gicv3_init_cpuif(GICv3State *s); @@ -544,6 +721,17 @@ void gicv3_init_cpuif(GICv3State *s); */ void gicv3_cpuif_update(GICv3CPUState *cs); +/* + * gicv3_cpuif_virt_irq_fiq_update: + * @cs: GICv3CPUState for the CPU to update + * + * Recalculate whether to assert the virtual IRQ or FIQ lines after + * a change to the current highest priority pending virtual interrupt. + * Note that this does not recalculate and change the maintenance + * interrupt status (for that, see gicv3_cpuif_virt_update()). + */ +void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs); + static inline uint32_t gicv3_iidr(void) { /* Return the Implementer Identification Register value @@ -555,17 +743,34 @@ static inline uint32_t gicv3_iidr(void) return 0x43b; } -static inline uint32_t gicv3_idreg(int regoffset) +/* CoreSight PIDR0 values for ARM GICv3 implementations */ +#define GICV3_PIDR0_DIST 0x92 +#define GICV3_PIDR0_REDIST 0x93 +#define GICV3_PIDR0_ITS 0x94 + +static inline uint32_t gicv3_idreg(GICv3State *s, int regoffset, uint8_t pidr0) { /* Return the value of the CoreSight ID register at the specified * offset from the first ID register (as found in the distributor * and redistributor register banks). - * These values indicate an ARM implementation of a GICv3. + * These values indicate an ARM implementation of a GICv3 or v4. */ static const uint8_t gicd_ids[] = { - 0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x3B, 0x00, 0x0D, 0xF0, 0x05, 0xB1 + 0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x0B, 0x00, 0x0D, 0xF0, 0x05, 0xB1 }; - return gicd_ids[regoffset / 4]; + uint32_t id; + + regoffset /= 4; + + if (regoffset == 4) { + return pidr0; + } + id = gicd_ids[regoffset]; + if (regoffset == 6) { + /* PIDR2 bits [7:4] are the GIC architecture revision */ + id |= s->revision << 4; + } + return id; } /** diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 7466024402..8b35139f82 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -6,7 +6,7 @@ softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( 'arm_gicv3_common.c', 'arm_gicv3_its_common.c', )) -softmmu_ss.add(when: 'CONFIG_ARM_GIC_TCG', if_true: files( +softmmu_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( 'arm_gicv3.c', 'arm_gicv3_dist.c', 'arm_gicv3_its.c', @@ -28,7 +28,7 @@ softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_PMU', if_true: files('xlnx-pmu-iomod-in specific_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c')) specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c')) specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) -specific_ss.add(when: 'CONFIG_ARM_GIC_TCG', if_true: files('arm_gicv3_cpuif.c')) +specific_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) @@ -42,7 +42,7 @@ specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) specific_ss.add(when: 'CONFIG_OMPIC', if_true: files('ompic.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_OPENPIC'], if_true: files('openpic_kvm.c')) -specific_ss.add(when: 'CONFIG_POWERNV', if_true: files('xics_pnv.c', 'pnv_xive.c')) +specific_ss.add(when: 'CONFIG_POWERNV', if_true: files('xics_pnv.c', 'pnv_xive.c', 'pnv_xive2.c')) specific_ss.add(when: 'CONFIG_PPC_UIC', if_true: files('ppc-uic.c')) specific_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c')) specific_ss.add(when: 'CONFIG_RX_ICU', if_true: files('rx_icu.c')) @@ -51,8 +51,9 @@ specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c')) specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c')) specific_ss.add(when: 'CONFIG_RISCV_ACLINT', if_true: files('riscv_aclint.c')) specific_ss.add(when: 'CONFIG_RISCV_APLIC', if_true: files('riscv_aplic.c')) +specific_ss.add(when: 'CONFIG_RISCV_IMSIC', if_true: files('riscv_imsic.c')) specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c')) -specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c')) +specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c', 'xive2.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'], if_true: files('xics_kvm.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xive.c')) @@ -61,3 +62,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) +specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c')) diff --git a/hw/intc/nios2_vic.c b/hw/intc/nios2_vic.c new file mode 100644 index 0000000000..cf63212a88 --- /dev/null +++ b/hw/intc/nios2_vic.c @@ -0,0 +1,313 @@ +/* + * Vectored Interrupt Controller for nios2 processor + * + * Copyright (c) 2022 Neuroblade + * + * Interface: + * QOM property "cpu": link to the Nios2 CPU (must be set) + * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines + * IRQ should be connected to nios2 IRQ0. + * + * Reference: "Embedded Peripherals IP User Guide + * for Intel® Quartus® Prime Design Suite: 21.4" + * Chapter 38 "Vectored Interrupt Controller Core" + * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qom/object.h" +#include "hw/intc/nios2_vic.h" +#include "cpu.h" + + +enum { + INT_CONFIG0 = 0, + INT_CONFIG31 = 31, + INT_ENABLE = 32, + INT_ENABLE_SET = 33, + INT_ENABLE_CLR = 34, + INT_PENDING = 35, + INT_RAW_STATUS = 36, + SW_INTERRUPT = 37, + SW_INTERRUPT_SET = 38, + SW_INTERRUPT_CLR = 39, + VIC_CONFIG = 40, + VIC_STATUS = 41, + VEC_TBL_BASE = 42, + VEC_TBL_ADDR = 43, + CSR_COUNT /* Last! */ +}; + +/* Requested interrupt level (INT_CONFIG[0:5]) */ +static inline uint32_t vic_int_config_ril(const Nios2VIC *vic, int irq_num) +{ + return extract32(vic->int_config[irq_num], 0, 6); +} + +/* Requested NMI (INT_CONFIG[6]) */ +static inline uint32_t vic_int_config_rnmi(const Nios2VIC *vic, int irq_num) +{ + return extract32(vic->int_config[irq_num], 6, 1); +} + +/* Requested register set (INT_CONFIG[7:12]) */ +static inline uint32_t vic_int_config_rrs(const Nios2VIC *vic, int irq_num) +{ + return extract32(vic->int_config[irq_num], 7, 6); +} + +static inline uint32_t vic_config_vec_size(const Nios2VIC *vic) +{ + return 1 << (2 + extract32(vic->vic_config, 0, 3)); +} + +static inline uint32_t vic_int_pending(const Nios2VIC *vic) +{ + return (vic->int_raw_status | vic->sw_int) & vic->int_enable; +} + +static void vic_update_irq(Nios2VIC *vic) +{ + Nios2CPU *cpu = NIOS2_CPU(vic->cpu); + uint32_t pending = vic_int_pending(vic); + int irq = -1; + int max_ril = 0; + /* Note that if RIL is 0 for an interrupt it is effectively disabled */ + + vic->vec_tbl_addr = 0; + vic->vic_status = 0; + + if (pending == 0) { + qemu_irq_lower(vic->output_int); + return; + } + + for (int i = 0; i < NIOS2_VIC_MAX_IRQ; i++) { + if (pending & BIT(i)) { + int ril = vic_int_config_ril(vic, i); + if (ril > max_ril) { + irq = i; + max_ril = ril; + } + } + } + + if (irq < 0) { + qemu_irq_lower(vic->output_int); + return; + } + + vic->vec_tbl_addr = irq * vic_config_vec_size(vic) + vic->vec_tbl_base; + vic->vic_status = irq | BIT(31); + + /* + * In hardware, the interface between the VIC and the CPU is via the + * External Interrupt Controller interface, where the interrupt controller + * presents the CPU with a packet of data containing: + * - Requested Handler Address (RHA): 32 bits + * - Requested Register Set (RRS) : 6 bits + * - Requested Interrupt Level (RIL) : 6 bits + * - Requested NMI flag (RNMI) : 1 bit + * In our emulation, we implement this by writing the data directly to + * fields in the CPU object and then raising the IRQ line to tell + * the CPU that we've done so. + */ + + cpu->rha = vic->vec_tbl_addr; + cpu->ril = max_ril; + cpu->rrs = vic_int_config_rrs(vic, irq); + cpu->rnmi = vic_int_config_rnmi(vic, irq); + + qemu_irq_raise(vic->output_int); +} + +static void vic_set_irq(void *opaque, int irq_num, int level) +{ + Nios2VIC *vic = opaque; + + vic->int_raw_status = deposit32(vic->int_raw_status, irq_num, 1, !!level); + vic_update_irq(vic); +} + +static void nios2_vic_reset(DeviceState *dev) +{ + Nios2VIC *vic = NIOS2_VIC(dev); + + memset(&vic->int_config, 0, sizeof(vic->int_config)); + vic->vic_config = 0; + vic->int_raw_status = 0; + vic->int_enable = 0; + vic->sw_int = 0; + vic->vic_status = 0; + vic->vec_tbl_base = 0; + vic->vec_tbl_addr = 0; +} + +static uint64_t nios2_vic_csr_read(void *opaque, hwaddr offset, unsigned size) +{ + Nios2VIC *vic = opaque; + int index = offset / 4; + + switch (index) { + case INT_CONFIG0 ... INT_CONFIG31: + return vic->int_config[index - INT_CONFIG0]; + case INT_ENABLE: + return vic->int_enable; + case INT_PENDING: + return vic_int_pending(vic); + case INT_RAW_STATUS: + return vic->int_raw_status; + case SW_INTERRUPT: + return vic->sw_int; + case VIC_CONFIG: + return vic->vic_config; + case VIC_STATUS: + return vic->vic_status; + case VEC_TBL_BASE: + return vic->vec_tbl_base; + case VEC_TBL_ADDR: + return vic->vec_tbl_addr; + default: + return 0; + } +} + +static void nios2_vic_csr_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + Nios2VIC *vic = opaque; + int index = offset / 4; + + switch (index) { + case INT_CONFIG0 ... INT_CONFIG31: + vic->int_config[index - INT_CONFIG0] = value; + break; + case INT_ENABLE: + vic->int_enable = value; + break; + case INT_ENABLE_SET: + vic->int_enable |= value; + break; + case INT_ENABLE_CLR: + vic->int_enable &= ~value; + break; + case SW_INTERRUPT: + vic->sw_int = value; + break; + case SW_INTERRUPT_SET: + vic->sw_int |= value; + break; + case SW_INTERRUPT_CLR: + vic->sw_int &= ~value; + break; + case VIC_CONFIG: + vic->vic_config = value; + break; + case VEC_TBL_BASE: + vic->vec_tbl_base = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "nios2-vic: write to invalid CSR address %#" + HWADDR_PRIx "\n", offset); + } + + vic_update_irq(vic); +} + +static const MemoryRegionOps nios2_vic_csr_ops = { + .read = nios2_vic_csr_read, + .write = nios2_vic_csr_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { .min_access_size = 4, .max_access_size = 4 } +}; + +static void nios2_vic_realize(DeviceState *dev, Error **errp) +{ + Nios2VIC *vic = NIOS2_VIC(dev); + + if (!vic->cpu) { + /* This is a programming error in the code using this device */ + error_setg(errp, "nios2-vic 'cpu' link property was not set"); + return; + } + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &vic->output_int); + qdev_init_gpio_in(dev, vic_set_irq, NIOS2_VIC_MAX_IRQ); + + memory_region_init_io(&vic->csr, OBJECT(dev), &nios2_vic_csr_ops, vic, + "nios2.vic.csr", CSR_COUNT * sizeof(uint32_t)); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &vic->csr); +} + +static Property nios2_vic_properties[] = { + DEFINE_PROP_LINK("cpu", Nios2VIC, cpu, TYPE_CPU, CPUState *), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription nios2_vic_vmstate = { + .name = "nios2-vic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]){ + VMSTATE_UINT32_ARRAY(int_config, Nios2VIC, 32), + VMSTATE_UINT32(vic_config, Nios2VIC), + VMSTATE_UINT32(int_raw_status, Nios2VIC), + VMSTATE_UINT32(int_enable, Nios2VIC), + VMSTATE_UINT32(sw_int, Nios2VIC), + VMSTATE_UINT32(vic_status, Nios2VIC), + VMSTATE_UINT32(vec_tbl_base, Nios2VIC), + VMSTATE_UINT32(vec_tbl_addr, Nios2VIC), + VMSTATE_END_OF_LIST() + }, +}; + +static void nios2_vic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = nios2_vic_reset; + dc->realize = nios2_vic_realize; + dc->vmsd = &nios2_vic_vmstate; + device_class_set_props(dc, nios2_vic_properties); +} + +static const TypeInfo nios2_vic_info = { + .name = TYPE_NIOS2_VIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Nios2VIC), + .class_init = nios2_vic_class_init, +}; + +static void nios2_vic_register_types(void) +{ + type_register_static(&nios2_vic_info); +} + +type_init(nios2_vic_register_types); diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 621b20a03f..1ce1d7b07d 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -403,6 +403,34 @@ static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx, return pnv_xive_vst_read(xive, VST_TSEL_IVT, blk, idx, eas); } +static int pnv_xive_get_pq(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + uint8_t *pq) +{ + PnvXive *xive = PNV_XIVE(xrtr); + + if (pnv_xive_block_id(xive) != blk) { + xive_error(xive, "VST: EAS %x is remote !?", XIVE_EAS(blk, idx)); + return -1; + } + + *pq = xive_source_esb_get(&xive->ipi_source, idx); + return 0; +} + +static int pnv_xive_set_pq(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + uint8_t *pq) +{ + PnvXive *xive = PNV_XIVE(xrtr); + + if (pnv_xive_block_id(xive) != blk) { + xive_error(xive, "VST: EAS %x is remote !?", XIVE_EAS(blk, idx)); + return -1; + } + + *pq = xive_source_esb_set(&xive->ipi_source, idx, *pq); + return 0; +} + /* * One bit per thread id. The first register PC_THREAD_EN_REG0 covers * the first cores 0-15 (normal) of the chip or 0-7 (fused). The @@ -499,12 +527,12 @@ static PnvXive *pnv_xive_tm_get_xive(PowerPCCPU *cpu) * event notification to the Router. This is required on a multichip * system. */ -static void pnv_xive_notify(XiveNotifier *xn, uint32_t srcno) +static void pnv_xive_notify(XiveNotifier *xn, uint32_t srcno, bool pq_checked) { PnvXive *xive = PNV_XIVE(xn); uint8_t blk = pnv_xive_block_id(xive); - xive_router_notify(xn, XIVE_EAS(blk, srcno)); + xive_router_notify(xn, XIVE_EAS(blk, srcno), pq_checked); } /* @@ -1351,7 +1379,8 @@ static void pnv_xive_ic_hw_trigger(PnvXive *xive, hwaddr addr, uint64_t val) blk = XIVE_EAS_BLOCK(val); idx = XIVE_EAS_INDEX(val); - xive_router_notify(XIVE_NOTIFIER(xive), XIVE_EAS(blk, idx)); + xive_router_notify(XIVE_NOTIFIER(xive), XIVE_EAS(blk, idx), + !!(val & XIVE_TRIGGER_PQ)); } static void pnv_xive_ic_notify_write(void *opaque, hwaddr addr, uint64_t val, @@ -1971,6 +2000,8 @@ static void pnv_xive_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, pnv_xive_properties); xrc->get_eas = pnv_xive_get_eas; + xrc->get_pq = pnv_xive_get_pq; + xrc->set_pq = pnv_xive_set_pq; xrc->get_end = pnv_xive_get_end; xrc->write_end = pnv_xive_write_end; xrc->get_nvt = pnv_xive_get_nvt; diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c new file mode 100644 index 0000000000..87303b4064 --- /dev/null +++ b/hw/intc/pnv_xive2.c @@ -0,0 +1,2128 @@ +/* + * QEMU PowerPC XIVE2 interrupt controller model (POWER10) + * + * Copyright (c) 2019-2022, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "target/ppc/cpu.h" +#include "sysemu/cpus.h" +#include "sysemu/dma.h" +#include "monitor/monitor.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_core.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/xive2.h" +#include "hw/ppc/pnv_xive.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/xive2_regs.h" +#include "hw/ppc/ppc.h" +#include "hw/qdev-properties.h" +#include "sysemu/reset.h" + +#include + +#include "pnv_xive2_regs.h" + +#undef XIVE2_DEBUG + +/* + * Virtual structures table (VST) + */ +#define SBE_PER_BYTE 4 + +typedef struct XiveVstInfo { + const char *name; + uint32_t size; + uint32_t max_blocks; +} XiveVstInfo; + +static const XiveVstInfo vst_infos[] = { + + [VST_EAS] = { "EAT", sizeof(Xive2Eas), 16 }, + [VST_ESB] = { "ESB", 1, 16 }, + [VST_END] = { "ENDT", sizeof(Xive2End), 16 }, + + [VST_NVP] = { "NVPT", sizeof(Xive2Nvp), 16 }, + [VST_NVG] = { "NVGT", sizeof(Xive2Nvgc), 16 }, + [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, + + [VST_IC] = { "IC", 1 /* ? */ , 16 }, /* Topology # */ + [VST_SYNC] = { "SYNC", 1 /* ? */ , 16 }, /* Topology # */ + + /* + * This table contains the backing store pages for the interrupt + * fifos of the VC sub-engine in case of overflow. + * + * 0 - IPI, + * 1 - HWD, + * 2 - NxC, + * 3 - INT, + * 4 - OS-Queue, + * 5 - Pool-Queue, + * 6 - Hard-Queue + */ + [VST_ERQ] = { "ERQ", 1, VC_QUEUE_COUNT }, +}; + +#define xive2_error(xive, fmt, ...) \ + qemu_log_mask(LOG_GUEST_ERROR, "XIVE[%x] - " fmt "\n", \ + (xive)->chip->chip_id, ## __VA_ARGS__); + +/* + * QEMU version of the GETFIELD/SETFIELD macros + * + * TODO: It might be better to use the existing extract64() and + * deposit64() but this means that all the register definitions will + * change and become incompatible with the ones found in skiboot. + * + * Keep it as it is for now until we find a common ground. + */ +static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) +{ + return (word & mask) >> ctz64(mask); +} + +static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, + uint64_t value) +{ + return (word & ~mask) | ((value << ctz64(mask)) & mask); +} + +/* + * TODO: Document block id override + */ +static uint32_t pnv_xive2_block_id(PnvXive2 *xive) +{ + uint8_t blk = xive->chip->chip_id; + uint64_t cfg_val = xive->cq_regs[CQ_XIVE_CFG >> 3]; + + if (cfg_val & CQ_XIVE_CFG_HYP_HARD_BLKID_OVERRIDE) { + blk = GETFIELD(CQ_XIVE_CFG_HYP_HARD_BLOCK_ID, cfg_val); + } + + return blk; +} + +/* + * Remote access to controllers. HW uses MMIOs. For now, a simple scan + * of the chips is good enough. + * + * TODO: Block scope support + */ +static PnvXive2 *pnv_xive2_get_remote(uint8_t blk) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); + PnvXive2 *xive = &chip10->xive; + + if (pnv_xive2_block_id(xive) == blk) { + return xive; + } + } + return NULL; +} + +/* + * VST accessors for ESB, EAT, ENDT, NVP + * + * Indirect VST tables are arrays of VSDs pointing to a page (of same + * size). Each page is a direct VST table. + */ + +#define XIVE_VSD_SIZE 8 + +/* Indirect page size can be 4K, 64K, 2M, 16M. */ +static uint64_t pnv_xive2_vst_page_size_allowed(uint32_t page_shift) +{ + return page_shift == 12 || page_shift == 16 || + page_shift == 21 || page_shift == 24; +} + +static uint64_t pnv_xive2_vst_addr_direct(PnvXive2 *xive, uint32_t type, + uint64_t vsd, uint32_t idx) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; + uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12); + uint32_t idx_max; + + idx_max = vst_tsize / info->size - 1; + if (idx > idx_max) { +#ifdef XIVE2_DEBUG + xive2_error(xive, "VST: %s entry %x out of range [ 0 .. %x ] !?", + info->name, idx, idx_max); +#endif + return 0; + } + + return vst_addr + idx * info->size; +} + +static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, + uint64_t vsd, uint32_t idx) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vsd_addr; + uint32_t vsd_idx; + uint32_t page_shift; + uint32_t vst_per_page; + + /* Get the page size of the indirect table. */ + vsd_addr = vsd & VSD_ADDRESS_MASK; + ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED); + + if (!(vsd & VSD_ADDRESS_MASK)) { + xive2_error(xive, "VST: invalid %s entry %x !?", info->name, idx); + return 0; + } + + page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + + if (!pnv_xive2_vst_page_size_allowed(page_shift)) { + xive2_error(xive, "VST: invalid %s page shift %d", info->name, + page_shift); + return 0; + } + + vst_per_page = (1ull << page_shift) / info->size; + vsd_idx = idx / vst_per_page; + + /* Load the VSD we are looking for, if not already done */ + if (vsd_idx) { + vsd_addr = vsd_addr + vsd_idx * XIVE_VSD_SIZE; + ldq_be_dma(&address_space_memory, vsd_addr, &vsd, + MEMTXATTRS_UNSPECIFIED); + + if (!(vsd & VSD_ADDRESS_MASK)) { + xive2_error(xive, "VST: invalid %s entry %x !?", info->name, idx); + return 0; + } + + /* + * Check that the pages have a consistent size across the + * indirect table + */ + if (page_shift != GETFIELD(VSD_TSIZE, vsd) + 12) { + xive2_error(xive, "VST: %s entry %x indirect page size differ !?", + info->name, idx); + return 0; + } + } + + return pnv_xive2_vst_addr_direct(xive, type, vsd, (idx % vst_per_page)); +} + +static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, + uint32_t idx) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vsd; + + if (blk >= info->max_blocks) { + xive2_error(xive, "VST: invalid block id %d for VST %s %d !?", + blk, info->name, idx); + return 0; + } + + vsd = xive->vsds[type][blk]; + + /* Remote VST access */ + if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { + xive = pnv_xive2_get_remote(blk); + + return xive ? pnv_xive2_vst_addr(xive, type, blk, idx) : 0; + } + + if (VSD_INDIRECT & vsd) { + return pnv_xive2_vst_addr_indirect(xive, type, vsd, idx); + } + + return pnv_xive2_vst_addr_direct(xive, type, vsd, idx); +} + +static int pnv_xive2_vst_read(PnvXive2 *xive, uint32_t type, uint8_t blk, + uint32_t idx, void *data) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t addr = pnv_xive2_vst_addr(xive, type, blk, idx); + + if (!addr) { + return -1; + } + + cpu_physical_memory_read(addr, data, info->size); + return 0; +} + +#define XIVE_VST_WORD_ALL -1 + +static int pnv_xive2_vst_write(PnvXive2 *xive, uint32_t type, uint8_t blk, + uint32_t idx, void *data, uint32_t word_number) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t addr = pnv_xive2_vst_addr(xive, type, blk, idx); + + if (!addr) { + return -1; + } + + if (word_number == XIVE_VST_WORD_ALL) { + cpu_physical_memory_write(addr, data, info->size); + } else { + cpu_physical_memory_write(addr + word_number * 4, + data + word_number * 4, 4); + } + return 0; +} + +static int pnv_xive2_get_pq(Xive2Router *xrtr, uint8_t blk, uint32_t idx, + uint8_t *pq) +{ + PnvXive2 *xive = PNV_XIVE2(xrtr); + + if (pnv_xive2_block_id(xive) != blk) { + xive2_error(xive, "VST: EAS %x is remote !?", XIVE_EAS(blk, idx)); + return -1; + } + + *pq = xive_source_esb_get(&xive->ipi_source, idx); + return 0; +} + +static int pnv_xive2_set_pq(Xive2Router *xrtr, uint8_t blk, uint32_t idx, + uint8_t *pq) +{ + PnvXive2 *xive = PNV_XIVE2(xrtr); + + if (pnv_xive2_block_id(xive) != blk) { + xive2_error(xive, "VST: EAS %x is remote !?", XIVE_EAS(blk, idx)); + return -1; + } + + *pq = xive_source_esb_set(&xive->ipi_source, idx, *pq); + return 0; +} + +static int pnv_xive2_get_end(Xive2Router *xrtr, uint8_t blk, uint32_t idx, + Xive2End *end) +{ + return pnv_xive2_vst_read(PNV_XIVE2(xrtr), VST_END, blk, idx, end); +} + +static int pnv_xive2_write_end(Xive2Router *xrtr, uint8_t blk, uint32_t idx, + Xive2End *end, uint8_t word_number) +{ + return pnv_xive2_vst_write(PNV_XIVE2(xrtr), VST_END, blk, idx, end, + word_number); +} + +static int pnv_xive2_end_update(PnvXive2 *xive) +{ + uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, + xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); + uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, + xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); + int i; + uint64_t endc_watch[4]; + + for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { + endc_watch[i] = + cpu_to_be64(xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i]); + } + + return pnv_xive2_vst_write(xive, VST_END, blk, idx, endc_watch, + XIVE_VST_WORD_ALL); +} + +static void pnv_xive2_end_cache_load(PnvXive2 *xive) +{ + uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, + xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); + uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, + xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); + uint64_t endc_watch[4] = { 0 }; + int i; + + if (pnv_xive2_vst_read(xive, VST_END, blk, idx, endc_watch)) { + xive2_error(xive, "VST: no END entry %x/%x !?", blk, idx); + } + + for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { + xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i] = + be64_to_cpu(endc_watch[i]); + } +} + +static int pnv_xive2_get_nvp(Xive2Router *xrtr, uint8_t blk, uint32_t idx, + Xive2Nvp *nvp) +{ + return pnv_xive2_vst_read(PNV_XIVE2(xrtr), VST_NVP, blk, idx, nvp); +} + +static int pnv_xive2_write_nvp(Xive2Router *xrtr, uint8_t blk, uint32_t idx, + Xive2Nvp *nvp, uint8_t word_number) +{ + return pnv_xive2_vst_write(PNV_XIVE2(xrtr), VST_NVP, blk, idx, nvp, + word_number); +} + +static int pnv_xive2_nvp_update(PnvXive2 *xive) +{ + uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, + xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); + uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, + xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); + int i; + uint64_t nxc_watch[4]; + + for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { + nxc_watch[i] = + cpu_to_be64(xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i]); + } + + return pnv_xive2_vst_write(xive, VST_NVP, blk, idx, nxc_watch, + XIVE_VST_WORD_ALL); +} + +static void pnv_xive2_nvp_cache_load(PnvXive2 *xive) +{ + uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, + xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); + uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, + xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); + uint64_t nxc_watch[4] = { 0 }; + int i; + + if (pnv_xive2_vst_read(xive, VST_NVP, blk, idx, nxc_watch)) { + xive2_error(xive, "VST: no NVP entry %x/%x !?", blk, idx); + } + + for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { + xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i] = + be64_to_cpu(nxc_watch[i]); + } +} + +static int pnv_xive2_get_eas(Xive2Router *xrtr, uint8_t blk, uint32_t idx, + Xive2Eas *eas) +{ + PnvXive2 *xive = PNV_XIVE2(xrtr); + + if (pnv_xive2_block_id(xive) != blk) { + xive2_error(xive, "VST: EAS %x is remote !?", XIVE_EAS(blk, idx)); + return -1; + } + + return pnv_xive2_vst_read(xive, VST_EAS, blk, idx, eas); +} + +static uint32_t pnv_xive2_get_config(Xive2Router *xrtr) +{ + PnvXive2 *xive = PNV_XIVE2(xrtr); + uint32_t cfg = 0; + + if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS) { + cfg |= XIVE2_GEN1_TIMA_OS; + } + + if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_EN_VP_SAVE_RESTORE) { + cfg |= XIVE2_VP_SAVE_RESTORE; + } + + if (GETFIELD(CQ_XIVE_CFG_HYP_HARD_RANGE, + xive->cq_regs[CQ_XIVE_CFG >> 3]) == CQ_XIVE_CFG_THREADID_8BITS) { + cfg |= XIVE2_THREADID_8BITS; + } + + return cfg; +} + +static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu) +{ + int pir = ppc_cpu_pir(cpu); + uint32_t fc = PNV10_PIR2FUSEDCORE(pir); + uint64_t reg = fc < 8 ? TCTXT_EN0 : TCTXT_EN1; + uint32_t bit = pir & 0x3f; + + return xive->tctxt_regs[reg >> 3] & PPC_BIT(bit); +} + +static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) +{ + PnvXive2 *xive = PNV_XIVE2(xptr); + PnvChip *chip = xive->chip; + int count = 0; + int i, j; + bool gen1_tima_os = + xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + + for (j = 0; j < cc->nr_threads; j++) { + PowerPCCPU *cpu = pc->threads[j]; + XiveTCTX *tctx; + int ring; + + if (!pnv_xive2_is_cpu_enabled(xive, cpu)) { + continue; + } + + tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + + if (gen1_tima_os) { + ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk, + nvt_idx, cam_ignore, + logic_serv); + } else { + ring = xive2_presenter_tctx_match(xptr, tctx, format, nvt_blk, + nvt_idx, cam_ignore, + logic_serv); + } + + /* + * Save the context and follow on to catch duplicates, + * that we don't support yet. + */ + if (ring != -1) { + if (match->tctx) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a " + "thread context NVT %x/%x\n", + nvt_blk, nvt_idx); + return false; + } + + match->ring = ring; + match->tctx = tctx; + count++; + } + } + } + + return count; +} + +static uint8_t pnv_xive2_get_block_id(Xive2Router *xrtr) +{ + return pnv_xive2_block_id(PNV_XIVE2(xrtr)); +} + +/* + * The TIMA MMIO space is shared among the chips and to identify the + * chip from which the access is being done, we extract the chip id + * from the PIR. + */ +static PnvXive2 *pnv_xive2_tm_get_xive(PowerPCCPU *cpu) +{ + int pir = ppc_cpu_pir(cpu); + XivePresenter *xptr = XIVE_TCTX(pnv_cpu_state(cpu)->intc)->xptr; + PnvXive2 *xive = PNV_XIVE2(xptr); + + if (!pnv_xive2_is_cpu_enabled(xive, cpu)) { + xive2_error(xive, "IC: CPU %x is not enabled", pir); + } + return xive; +} + +/* + * The internal sources of the interrupt controller have no knowledge + * of the XIVE2 chip on which they reside. Encode the block id in the + * source interrupt number before forwarding the source event + * notification to the Router. This is required on a multichip system. + */ +static void pnv_xive2_notify(XiveNotifier *xn, uint32_t srcno, bool pq_checked) +{ + PnvXive2 *xive = PNV_XIVE2(xn); + uint8_t blk = pnv_xive2_block_id(xive); + + xive2_router_notify(xn, XIVE_EAS(blk, srcno), pq_checked); +} + +/* + * Set Translation Tables + * + * TODO add support for multiple sets + */ +static int pnv_xive2_stt_set_data(PnvXive2 *xive, uint64_t val) +{ + uint8_t tsel = GETFIELD(CQ_TAR_SELECT, xive->cq_regs[CQ_TAR >> 3]); + uint8_t entry = GETFIELD(CQ_TAR_ENTRY_SELECT, + xive->cq_regs[CQ_TAR >> 3]); + + switch (tsel) { + case CQ_TAR_NVPG: + case CQ_TAR_ESB: + case CQ_TAR_END: + xive->tables[tsel][entry] = val; + break; + default: + xive2_error(xive, "IC: unsupported table %d", tsel); + return -1; + } + + if (xive->cq_regs[CQ_TAR >> 3] & CQ_TAR_AUTOINC) { + xive->cq_regs[CQ_TAR >> 3] = SETFIELD(CQ_TAR_ENTRY_SELECT, + xive->cq_regs[CQ_TAR >> 3], ++entry); + } + + return 0; +} +/* + * Virtual Structure Tables (VST) configuration + */ +static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, + uint8_t blk, uint64_t vsd) +{ + Xive2EndSource *end_xsrc = &xive->end_source; + XiveSource *xsrc = &xive->ipi_source; + const XiveVstInfo *info = &vst_infos[type]; + uint32_t page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + uint64_t vst_tsize = 1ull << page_shift; + uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; + + /* Basic checks */ + + if (VSD_INDIRECT & vsd) { + if (!pnv_xive2_vst_page_size_allowed(page_shift)) { + xive2_error(xive, "VST: invalid %s page shift %d", info->name, + page_shift); + return; + } + } + + if (!QEMU_IS_ALIGNED(vst_addr, 1ull << page_shift)) { + xive2_error(xive, "VST: %s table address 0x%"PRIx64 + " is not aligned with page shift %d", + info->name, vst_addr, page_shift); + return; + } + + /* Record the table configuration (in SRAM on HW) */ + xive->vsds[type][blk] = vsd; + + /* Now tune the models with the configuration provided by the FW */ + + switch (type) { + case VST_ESB: + /* + * Backing store pages for the source PQ bits. The model does + * not use these PQ bits backed in RAM because the XiveSource + * model has its own. + * + * If the table is direct, we can compute the number of PQ + * entries provisioned by FW (such as skiboot) and resize the + * ESB window accordingly. + */ + if (!(VSD_INDIRECT & vsd)) { + memory_region_set_size(&xsrc->esb_mmio, vst_tsize * SBE_PER_BYTE + * (1ull << xsrc->esb_shift)); + } + + memory_region_add_subregion(&xive->esb_mmio, 0, &xsrc->esb_mmio); + break; + + case VST_EAS: /* Nothing to be done */ + break; + + case VST_END: + /* + * Backing store pages for the END. + */ + if (!(VSD_INDIRECT & vsd)) { + memory_region_set_size(&end_xsrc->esb_mmio, (vst_tsize / info->size) + * (1ull << end_xsrc->esb_shift)); + } + memory_region_add_subregion(&xive->end_mmio, 0, &end_xsrc->esb_mmio); + break; + + case VST_NVP: /* Not modeled */ + case VST_NVG: /* Not modeled */ + case VST_NVC: /* Not modeled */ + case VST_IC: /* Not modeled */ + case VST_SYNC: /* Not modeled */ + case VST_ERQ: /* Not modeled */ + break; + + default: + g_assert_not_reached(); + } +} + +/* + * Both PC and VC sub-engines are configured as each use the Virtual + * Structure Tables + */ +static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd) +{ + uint8_t mode = GETFIELD(VSD_MODE, vsd); + uint8_t type = GETFIELD(VC_VSD_TABLE_SELECT, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(VC_VSD_TABLE_ADDRESS, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; + + if (type > VST_ERQ) { + xive2_error(xive, "VST: invalid table type %d", type); + return; + } + + if (blk >= vst_infos[type].max_blocks) { + xive2_error(xive, "VST: invalid block id %d for" + " %s table", blk, vst_infos[type].name); + return; + } + + if (!vst_addr) { + xive2_error(xive, "VST: invalid %s table address", + vst_infos[type].name); + return; + } + + switch (mode) { + case VSD_MODE_FORWARD: + xive->vsds[type][blk] = vsd; + break; + + case VSD_MODE_EXCLUSIVE: + pnv_xive2_vst_set_exclusive(xive, type, blk, vsd); + break; + + default: + xive2_error(xive, "VST: unsupported table mode %d", mode); + return; + } +} + +/* + * MMIO handlers + */ + + +/* + * IC BAR layout + * + * Page 0: Internal CQ register accesses (reads & writes) + * Page 1: Internal PC register accesses (reads & writes) + * Page 2: Internal VC register accesses (reads & writes) + * Page 3: Internal TCTXT (TIMA) reg accesses (read & writes) + * Page 4: Notify Port page (writes only, w/data), + * Page 5: Reserved + * Page 6: Sync Poll page (writes only, dataless) + * Page 7: Sync Inject page (writes only, dataless) + * Page 8: LSI Trigger page (writes only, dataless) + * Page 9: LSI SB Management page (reads & writes dataless) + * Pages 10-255: Reserved + * Pages 256-383: Direct mapped Thread Context Area (reads & writes) + * covering the 128 threads in P10. + * Pages 384-511: Reserved + */ +typedef struct PnvXive2Region { + const char *name; + uint32_t pgoff; + uint32_t pgsize; + const MemoryRegionOps *ops; +} PnvXive2Region; + +static const MemoryRegionOps pnv_xive2_ic_cq_ops; +static const MemoryRegionOps pnv_xive2_ic_pc_ops; +static const MemoryRegionOps pnv_xive2_ic_vc_ops; +static const MemoryRegionOps pnv_xive2_ic_tctxt_ops; +static const MemoryRegionOps pnv_xive2_ic_notify_ops; +static const MemoryRegionOps pnv_xive2_ic_sync_ops; +static const MemoryRegionOps pnv_xive2_ic_lsi_ops; +static const MemoryRegionOps pnv_xive2_ic_tm_indirect_ops; + +/* 512 pages. 4K: 2M range, 64K: 32M range */ +static const PnvXive2Region pnv_xive2_ic_regions[] = { + { "xive-ic-cq", 0, 1, &pnv_xive2_ic_cq_ops }, + { "xive-ic-vc", 1, 1, &pnv_xive2_ic_vc_ops }, + { "xive-ic-pc", 2, 1, &pnv_xive2_ic_pc_ops }, + { "xive-ic-tctxt", 3, 1, &pnv_xive2_ic_tctxt_ops }, + { "xive-ic-notify", 4, 1, &pnv_xive2_ic_notify_ops }, + /* page 5 reserved */ + { "xive-ic-sync", 6, 2, &pnv_xive2_ic_sync_ops }, + { "xive-ic-lsi", 8, 2, &pnv_xive2_ic_lsi_ops }, + /* pages 10-255 reserved */ + { "xive-ic-tm-indirect", 256, 128, &pnv_xive2_ic_tm_indirect_ops }, + /* pages 384-511 reserved */ +}; + +/* + * CQ operations + */ + +static uint64_t pnv_xive2_ic_cq_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t reg = offset >> 3; + uint64_t val = 0; + + switch (offset) { + case CQ_XIVE_CAP: /* Set at reset */ + case CQ_XIVE_CFG: + val = xive->cq_regs[reg]; + break; + case CQ_MSGSND: /* TODO check the #cores of the machine */ + val = 0xffffffff00000000; + break; + case CQ_CFG_PB_GEN: + val = CQ_CFG_PB_GEN_PB_INIT; /* TODO: fix CQ_CFG_PB_GEN default value */ + break; + default: + xive2_error(xive, "CQ: invalid read @%"HWADDR_PRIx, offset); + } + + return val; +} + +static uint64_t pnv_xive2_bar_size(uint64_t val) +{ + return 1ull << (GETFIELD(CQ_BAR_RANGE, val) + 24); +} + +static void pnv_xive2_ic_cq_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + MemoryRegion *sysmem = get_system_memory(); + uint32_t reg = offset >> 3; + int i; + + switch (offset) { + case CQ_XIVE_CFG: + case CQ_RST_CTL: /* TODO: reset all BARs */ + break; + + case CQ_IC_BAR: + xive->ic_shift = val & CQ_IC_BAR_64K ? 16 : 12; + if (!(val & CQ_IC_BAR_VALID)) { + xive->ic_base = 0; + if (xive->cq_regs[reg] & CQ_IC_BAR_VALID) { + for (i = 0; i < ARRAY_SIZE(xive->ic_mmios); i++) { + memory_region_del_subregion(&xive->ic_mmio, + &xive->ic_mmios[i]); + } + memory_region_del_subregion(sysmem, &xive->ic_mmio); + } + } else { + xive->ic_base = val & ~(CQ_IC_BAR_VALID | CQ_IC_BAR_64K); + if (!(xive->cq_regs[reg] & CQ_IC_BAR_VALID)) { + for (i = 0; i < ARRAY_SIZE(xive->ic_mmios); i++) { + memory_region_add_subregion(&xive->ic_mmio, + pnv_xive2_ic_regions[i].pgoff << xive->ic_shift, + &xive->ic_mmios[i]); + } + memory_region_add_subregion(sysmem, xive->ic_base, + &xive->ic_mmio); + } + } + break; + + case CQ_TM_BAR: + xive->tm_shift = val & CQ_TM_BAR_64K ? 16 : 12; + if (!(val & CQ_TM_BAR_VALID)) { + xive->tm_base = 0; + if (xive->cq_regs[reg] & CQ_TM_BAR_VALID) { + memory_region_del_subregion(sysmem, &xive->tm_mmio); + } + } else { + xive->tm_base = val & ~(CQ_TM_BAR_VALID | CQ_TM_BAR_64K); + if (!(xive->cq_regs[reg] & CQ_TM_BAR_VALID)) { + memory_region_add_subregion(sysmem, xive->tm_base, + &xive->tm_mmio); + } + } + break; + + case CQ_ESB_BAR: + xive->esb_shift = val & CQ_BAR_64K ? 16 : 12; + if (!(val & CQ_BAR_VALID)) { + xive->esb_base = 0; + if (xive->cq_regs[reg] & CQ_BAR_VALID) { + memory_region_del_subregion(sysmem, &xive->esb_mmio); + } + } else { + xive->esb_base = val & CQ_BAR_ADDR; + if (!(xive->cq_regs[reg] & CQ_BAR_VALID)) { + memory_region_set_size(&xive->esb_mmio, + pnv_xive2_bar_size(val)); + memory_region_add_subregion(sysmem, xive->esb_base, + &xive->esb_mmio); + } + } + break; + + case CQ_END_BAR: + xive->end_shift = val & CQ_BAR_64K ? 16 : 12; + if (!(val & CQ_BAR_VALID)) { + xive->end_base = 0; + if (xive->cq_regs[reg] & CQ_BAR_VALID) { + memory_region_del_subregion(sysmem, &xive->end_mmio); + } + } else { + xive->end_base = val & CQ_BAR_ADDR; + if (!(xive->cq_regs[reg] & CQ_BAR_VALID)) { + memory_region_set_size(&xive->end_mmio, + pnv_xive2_bar_size(val)); + memory_region_add_subregion(sysmem, xive->end_base, + &xive->end_mmio); + } + } + break; + + case CQ_NVC_BAR: + xive->nvc_shift = val & CQ_BAR_64K ? 16 : 12; + if (!(val & CQ_BAR_VALID)) { + xive->nvc_base = 0; + if (xive->cq_regs[reg] & CQ_BAR_VALID) { + memory_region_del_subregion(sysmem, &xive->nvc_mmio); + } + } else { + xive->nvc_base = val & CQ_BAR_ADDR; + if (!(xive->cq_regs[reg] & CQ_BAR_VALID)) { + memory_region_set_size(&xive->nvc_mmio, + pnv_xive2_bar_size(val)); + memory_region_add_subregion(sysmem, xive->nvc_base, + &xive->nvc_mmio); + } + } + break; + + case CQ_NVPG_BAR: + xive->nvpg_shift = val & CQ_BAR_64K ? 16 : 12; + if (!(val & CQ_BAR_VALID)) { + xive->nvpg_base = 0; + if (xive->cq_regs[reg] & CQ_BAR_VALID) { + memory_region_del_subregion(sysmem, &xive->nvpg_mmio); + } + } else { + xive->nvpg_base = val & CQ_BAR_ADDR; + if (!(xive->cq_regs[reg] & CQ_BAR_VALID)) { + memory_region_set_size(&xive->nvpg_mmio, + pnv_xive2_bar_size(val)); + memory_region_add_subregion(sysmem, xive->nvpg_base, + &xive->nvpg_mmio); + } + } + break; + + case CQ_TAR: /* Set Translation Table Address */ + break; + case CQ_TDR: /* Set Translation Table Data */ + pnv_xive2_stt_set_data(xive, val); + break; + case CQ_FIRMASK_OR: /* FIR error reporting */ + break; + default: + xive2_error(xive, "CQ: invalid write 0x%"HWADDR_PRIx, offset); + return; + } + + xive->cq_regs[reg] = val; +} + +static const MemoryRegionOps pnv_xive2_ic_cq_ops = { + .read = pnv_xive2_ic_cq_read, + .write = pnv_xive2_ic_cq_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint64_t val = 0; + uint32_t reg = offset >> 3; + + switch (offset) { + /* + * VSD table settings. + */ + case VC_VSD_TABLE_ADDR: + case VC_VSD_TABLE_DATA: + val = xive->vc_regs[reg]; + break; + + /* + * ESB cache updates (not modeled) + */ + case VC_ESBC_FLUSH_CTRL: + xive->vc_regs[reg] &= ~VC_ESBC_FLUSH_CTRL_POLL_VALID; + val = xive->vc_regs[reg]; + break; + + /* + * EAS cache updates (not modeled) + */ + case VC_EASC_FLUSH_CTRL: + xive->vc_regs[reg] &= ~VC_EASC_FLUSH_CTRL_POLL_VALID; + val = xive->vc_regs[reg]; + break; + + /* + * END cache updates + */ + case VC_ENDC_WATCH0_SPEC: + xive->vc_regs[reg] &= ~(VC_ENDC_WATCH_FULL | VC_ENDC_WATCH_CONFLICT); + val = xive->vc_regs[reg]; + break; + + case VC_ENDC_WATCH0_DATA0: + /* + * Load DATA registers from cache with data requested by the + * SPEC register + */ + pnv_xive2_end_cache_load(xive); + val = xive->vc_regs[reg]; + break; + + case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + val = xive->vc_regs[reg]; + break; + + case VC_ENDC_FLUSH_CTRL: + xive->vc_regs[reg] &= ~VC_ENDC_FLUSH_CTRL_POLL_VALID; + val = xive->vc_regs[reg]; + break; + + /* + * Indirect invalidation + */ + case VC_AT_MACRO_KILL_MASK: + val = xive->vc_regs[reg]; + break; + + case VC_AT_MACRO_KILL: + xive->vc_regs[reg] &= ~VC_AT_MACRO_KILL_VALID; + val = xive->vc_regs[reg]; + break; + + /* + * Interrupt fifo overflow in memory backing store (Not modeled) + */ + case VC_QUEUES_CFG_REM0 ... VC_QUEUES_CFG_REM6: + val = xive->vc_regs[reg]; + break; + + /* + * Synchronisation + */ + case VC_ENDC_SYNC_DONE: + val = VC_ENDC_SYNC_POLL_DONE; + break; + default: + xive2_error(xive, "VC: invalid read @%"HWADDR_PRIx, offset); + } + + return val; +} + +static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t reg = offset >> 3; + + switch (offset) { + /* + * VSD table settings. + */ + case VC_VSD_TABLE_ADDR: + break; + case VC_VSD_TABLE_DATA: + pnv_xive2_vst_set_data(xive, val); + break; + + /* + * ESB cache updates (not modeled) + */ + /* case VC_ESBC_FLUSH_CTRL: */ + case VC_ESBC_FLUSH_POLL: + xive->vc_regs[VC_ESBC_FLUSH_CTRL >> 3] |= VC_ESBC_FLUSH_CTRL_POLL_VALID; + /* ESB update */ + break; + + /* + * EAS cache updates (not modeled) + */ + /* case VC_EASC_FLUSH_CTRL: */ + case VC_EASC_FLUSH_POLL: + xive->vc_regs[VC_EASC_FLUSH_CTRL >> 3] |= VC_EASC_FLUSH_CTRL_POLL_VALID; + /* EAS update */ + break; + + /* + * END cache updates + */ + case VC_ENDC_WATCH0_SPEC: + val &= ~VC_ENDC_WATCH_CONFLICT; /* HW will set this bit */ + break; + + case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + break; + case VC_ENDC_WATCH0_DATA0: + /* writing to DATA0 triggers the cache write */ + xive->vc_regs[reg] = val; + pnv_xive2_end_update(xive); + break; + + + /* case VC_ENDC_FLUSH_CTRL: */ + case VC_ENDC_FLUSH_POLL: + xive->vc_regs[VC_ENDC_FLUSH_CTRL >> 3] |= VC_ENDC_FLUSH_CTRL_POLL_VALID; + break; + + /* + * Indirect invalidation + */ + case VC_AT_MACRO_KILL: + case VC_AT_MACRO_KILL_MASK: + break; + + /* + * Interrupt fifo overflow in memory backing store (Not modeled) + */ + case VC_QUEUES_CFG_REM0 ... VC_QUEUES_CFG_REM6: + break; + + /* + * Synchronisation + */ + case VC_ENDC_SYNC_DONE: + break; + + default: + xive2_error(xive, "VC: invalid write @%"HWADDR_PRIx, offset); + return; + } + + xive->vc_regs[reg] = val; +} + +static const MemoryRegionOps pnv_xive2_ic_vc_ops = { + .read = pnv_xive2_ic_vc_read, + .write = pnv_xive2_ic_vc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint64_t val = -1; + uint32_t reg = offset >> 3; + + switch (offset) { + /* + * VSD table settings. + */ + case PC_VSD_TABLE_ADDR: + case PC_VSD_TABLE_DATA: + val = xive->pc_regs[reg]; + break; + + /* + * cache updates + */ + case PC_NXC_WATCH0_SPEC: + xive->pc_regs[reg] &= ~(PC_NXC_WATCH_FULL | PC_NXC_WATCH_CONFLICT); + val = xive->pc_regs[reg]; + break; + + case PC_NXC_WATCH0_DATA0: + /* + * Load DATA registers from cache with data requested by the + * SPEC register + */ + pnv_xive2_nvp_cache_load(xive); + val = xive->pc_regs[reg]; + break; + + case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + val = xive->pc_regs[reg]; + break; + + case PC_NXC_FLUSH_CTRL: + xive->pc_regs[reg] &= ~PC_NXC_FLUSH_CTRL_POLL_VALID; + val = xive->pc_regs[reg]; + break; + + /* + * Indirect invalidation + */ + case PC_AT_KILL: + xive->pc_regs[reg] &= ~PC_AT_KILL_VALID; + val = xive->pc_regs[reg]; + break; + + default: + xive2_error(xive, "PC: invalid read @%"HWADDR_PRIx, offset); + } + + return val; +} + +static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t reg = offset >> 3; + + switch (offset) { + + /* + * VSD table settings. Only taken into account in the VC + * sub-engine because the Xive2Router model combines both VC and PC + * sub-engines + */ + case PC_VSD_TABLE_ADDR: + case PC_VSD_TABLE_DATA: + break; + + /* + * cache updates + */ + case PC_NXC_WATCH0_SPEC: + val &= ~PC_NXC_WATCH_CONFLICT; /* HW will set this bit */ + break; + + case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + break; + case PC_NXC_WATCH0_DATA0: + /* writing to DATA0 triggers the cache write */ + xive->pc_regs[reg] = val; + pnv_xive2_nvp_update(xive); + break; + + /* case PC_NXC_FLUSH_CTRL: */ + case PC_NXC_FLUSH_POLL: + xive->pc_regs[PC_NXC_FLUSH_CTRL >> 3] |= PC_NXC_FLUSH_CTRL_POLL_VALID; + break; + + /* + * Indirect invalidation + */ + case PC_AT_KILL: + case PC_AT_KILL_MASK: + break; + + default: + xive2_error(xive, "PC: invalid write @%"HWADDR_PRIx, offset); + return; + } + + xive->pc_regs[reg] = val; +} + +static const MemoryRegionOps pnv_xive2_ic_pc_ops = { + .read = pnv_xive2_ic_pc_read, + .write = pnv_xive2_ic_pc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + + +static uint64_t pnv_xive2_ic_tctxt_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint64_t val = -1; + uint32_t reg = offset >> 3; + + switch (offset) { + /* + * XIVE2 hardware thread enablement + */ + case TCTXT_EN0: + case TCTXT_EN1: + val = xive->tctxt_regs[reg]; + break; + + case TCTXT_EN0_SET: + case TCTXT_EN0_RESET: + val = xive->tctxt_regs[TCTXT_EN0 >> 3]; + break; + case TCTXT_EN1_SET: + case TCTXT_EN1_RESET: + val = xive->tctxt_regs[TCTXT_EN1 >> 3]; + break; + default: + xive2_error(xive, "TCTXT: invalid read @%"HWADDR_PRIx, offset); + } + + return val; +} + +static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t reg = offset >> 3; + + switch (offset) { + /* + * XIVE2 hardware thread enablement + */ + case TCTXT_EN0: /* Physical Thread Enable */ + case TCTXT_EN1: /* Physical Thread Enable (fused core) */ + break; + + case TCTXT_EN0_SET: + xive->tctxt_regs[TCTXT_EN0 >> 3] |= val; + break; + case TCTXT_EN1_SET: + xive->tctxt_regs[TCTXT_EN1 >> 3] |= val; + break; + case TCTXT_EN0_RESET: + xive->tctxt_regs[TCTXT_EN0 >> 3] &= ~val; + break; + case TCTXT_EN1_RESET: + xive->tctxt_regs[TCTXT_EN1 >> 3] &= ~val; + break; + + default: + xive2_error(xive, "TCTXT: invalid write @%"HWADDR_PRIx, offset); + return; + } + + xive->pc_regs[reg] = val; +} + +static const MemoryRegionOps pnv_xive2_ic_tctxt_ops = { + .read = pnv_xive2_ic_tctxt_read, + .write = pnv_xive2_ic_tctxt_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * Redirect XSCOM to MMIO handlers + */ +static uint64_t pnv_xive2_xscom_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint64_t val = -1; + uint32_t xscom_reg = offset >> 3; + uint32_t mmio_offset = (xscom_reg & 0xFF) << 3; + + switch (xscom_reg) { + case 0x000 ... 0x0FF: + val = pnv_xive2_ic_cq_read(opaque, mmio_offset, size); + break; + case 0x100 ... 0x1FF: + val = pnv_xive2_ic_vc_read(opaque, mmio_offset, size); + break; + case 0x200 ... 0x2FF: + val = pnv_xive2_ic_pc_read(opaque, mmio_offset, size); + break; + case 0x300 ... 0x3FF: + val = pnv_xive2_ic_tctxt_read(opaque, mmio_offset, size); + break; + default: + xive2_error(xive, "XSCOM: invalid read @%"HWADDR_PRIx, offset); + } + + return val; +} + +static void pnv_xive2_xscom_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t xscom_reg = offset >> 3; + uint32_t mmio_offset = (xscom_reg & 0xFF) << 3; + + switch (xscom_reg) { + case 0x000 ... 0x0FF: + pnv_xive2_ic_cq_write(opaque, mmio_offset, val, size); + break; + case 0x100 ... 0x1FF: + pnv_xive2_ic_vc_write(opaque, mmio_offset, val, size); + break; + case 0x200 ... 0x2FF: + pnv_xive2_ic_pc_write(opaque, mmio_offset, val, size); + break; + case 0x300 ... 0x3FF: + pnv_xive2_ic_tctxt_write(opaque, mmio_offset, val, size); + break; + default: + xive2_error(xive, "XSCOM: invalid write @%"HWADDR_PRIx, offset); + } +} + +static const MemoryRegionOps pnv_xive2_xscom_ops = { + .read = pnv_xive2_xscom_read, + .write = pnv_xive2_xscom_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * Notify port page. The layout is compatible between 4K and 64K pages : + * + * Page 1 Notify page (writes only) + * 0x000 - 0x7FF IPI interrupt (NPU) + * 0x800 - 0xFFF HW interrupt triggers (PSI, PHB) + */ + +static void pnv_xive2_ic_hw_trigger(PnvXive2 *xive, hwaddr addr, + uint64_t val) +{ + uint8_t blk; + uint32_t idx; + + if (val & XIVE_TRIGGER_END) { + xive2_error(xive, "IC: END trigger at @0x%"HWADDR_PRIx" data 0x%"PRIx64, + addr, val); + return; + } + + /* + * Forward the source event notification directly to the Router. + * The source interrupt number should already be correctly encoded + * with the chip block id by the sending device (PHB, PSI). + */ + blk = XIVE_EAS_BLOCK(val); + idx = XIVE_EAS_INDEX(val); + + xive2_router_notify(XIVE_NOTIFIER(xive), XIVE_EAS(blk, idx), + !!(val & XIVE_TRIGGER_PQ)); +} + +static void pnv_xive2_ic_notify_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + /* VC: IPI triggers */ + switch (offset) { + case 0x000 ... 0x7FF: + /* TODO: check IPI notify sub-page routing */ + pnv_xive2_ic_hw_trigger(opaque, offset, val); + break; + + /* VC: HW triggers */ + case 0x800 ... 0xFFF: + pnv_xive2_ic_hw_trigger(opaque, offset, val); + break; + + default: + xive2_error(xive, "NOTIFY: invalid write @%"HWADDR_PRIx, offset); + } +} + +static uint64_t pnv_xive2_ic_notify_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + /* loads are invalid */ + xive2_error(xive, "NOTIFY: invalid read @%"HWADDR_PRIx, offset); + return -1; +} + +static const MemoryRegionOps pnv_xive2_ic_notify_ops = { + .read = pnv_xive2_ic_notify_read, + .write = pnv_xive2_ic_notify_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_xive2_ic_lsi_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + xive2_error(xive, "LSI: invalid read @%"HWADDR_PRIx, offset); + return -1; +} + +static void pnv_xive2_ic_lsi_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + xive2_error(xive, "LSI: invalid write @%"HWADDR_PRIx, offset); +} + +static const MemoryRegionOps pnv_xive2_ic_lsi_ops = { + .read = pnv_xive2_ic_lsi_read, + .write = pnv_xive2_ic_lsi_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * Sync MMIO page (write only) + */ +#define PNV_XIVE2_SYNC_IPI 0x000 +#define PNV_XIVE2_SYNC_HW 0x080 +#define PNV_XIVE2_SYNC_NxC 0x100 +#define PNV_XIVE2_SYNC_INT 0x180 +#define PNV_XIVE2_SYNC_OS_ESC 0x200 +#define PNV_XIVE2_SYNC_POOL_ESC 0x280 +#define PNV_XIVE2_SYNC_HARD_ESC 0x300 + +static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + /* loads are invalid */ + xive2_error(xive, "SYNC: invalid read @%"HWADDR_PRIx, offset); + return -1; +} + +static void pnv_xive2_ic_sync_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + switch (offset) { + case PNV_XIVE2_SYNC_IPI: + case PNV_XIVE2_SYNC_HW: + case PNV_XIVE2_SYNC_NxC: + case PNV_XIVE2_SYNC_INT: + case PNV_XIVE2_SYNC_OS_ESC: + case PNV_XIVE2_SYNC_POOL_ESC: + case PNV_XIVE2_SYNC_HARD_ESC: + break; + default: + xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx, offset); + } +} + +static const MemoryRegionOps pnv_xive2_ic_sync_ops = { + .read = pnv_xive2_ic_sync_read, + .write = pnv_xive2_ic_sync_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * When the TM direct pages of the IC controller are accessed, the + * target HW thread is deduced from the page offset. + */ +static XiveTCTX *pnv_xive2_get_indirect_tctx(PnvXive2 *xive, uint32_t pir) +{ + PnvChip *chip = xive->chip; + PowerPCCPU *cpu = NULL; + + cpu = pnv_chip_find_cpu(chip, pir); + if (!cpu) { + xive2_error(xive, "IC: invalid PIR %x for indirect access", pir); + return NULL; + } + + if (!pnv_xive2_is_cpu_enabled(xive, cpu)) { + xive2_error(xive, "IC: CPU %x is not enabled", pir); + } + + return XIVE_TCTX(pnv_cpu_state(cpu)->intc); +} + +static uint64_t pnv_xive2_ic_tm_indirect_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t pir = offset >> xive->ic_shift; + XiveTCTX *tctx = pnv_xive2_get_indirect_tctx(xive, pir); + uint64_t val = -1; + + if (tctx) { + val = xive_tctx_tm_read(NULL, tctx, offset, size); + } + + return val; +} + +static void pnv_xive2_ic_tm_indirect_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t pir = offset >> xive->ic_shift; + XiveTCTX *tctx = pnv_xive2_get_indirect_tctx(xive, pir); + + if (tctx) { + xive_tctx_tm_write(NULL, tctx, offset, val, size); + } +} + +static const MemoryRegionOps pnv_xive2_ic_tm_indirect_ops = { + .read = pnv_xive2_ic_tm_indirect_read, + .write = pnv_xive2_ic_tm_indirect_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * TIMA ops + */ + +/* + * Special TIMA offsets to handle accesses in a POWER10 way. + * + * Only the CAM line updates done by the hypervisor should be handled + * specifically. + */ +#define HV_PAGE_OFFSET (XIVE_TM_HV_PAGE << TM_SHIFT) +#define HV_PUSH_OS_CTX_OFFSET (HV_PAGE_OFFSET | (TM_QW1_OS + TM_WORD2)) +#define HV_PULL_OS_CTX_OFFSET (HV_PAGE_OFFSET | TM_SPC_PULL_OS_CTX) + +static void pnv_xive2_tm_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu); + XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + XivePresenter *xptr = XIVE_PRESENTER(xive); + bool gen1_tima_os = + xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + + /* TODO: should we switch the TM ops table instead ? */ + if (!gen1_tima_os && offset == HV_PUSH_OS_CTX_OFFSET) { + xive2_tm_push_os_ctx(xptr, tctx, offset, value, size); + return; + } + + /* Other TM ops are the same as XIVE1 */ + xive_tctx_tm_write(xptr, tctx, offset, value, size); +} + +static uint64_t pnv_xive2_tm_read(void *opaque, hwaddr offset, unsigned size) +{ + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu); + XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + XivePresenter *xptr = XIVE_PRESENTER(xive); + bool gen1_tima_os = + xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + + /* TODO: should we switch the TM ops table instead ? */ + if (!gen1_tima_os && offset == HV_PULL_OS_CTX_OFFSET) { + return xive2_tm_pull_os_ctx(xptr, tctx, offset, size); + } + + /* Other TM ops are the same as XIVE1 */ + return xive_tctx_tm_read(xptr, tctx, offset, size); +} + +static const MemoryRegionOps pnv_xive2_tm_ops = { + .read = pnv_xive2_tm_read, + .write = pnv_xive2_tm_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + xive2_error(xive, "NVC: invalid read @%"HWADDR_PRIx, offset); + return -1; +} + +static void pnv_xive2_nvc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + xive2_error(xive, "NVC: invalid write @%"HWADDR_PRIx, offset); +} + +static const MemoryRegionOps pnv_xive2_nvc_ops = { + .read = pnv_xive2_nvc_read, + .write = pnv_xive2_nvc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + xive2_error(xive, "NVPG: invalid read @%"HWADDR_PRIx, offset); + return -1; +} + +static void pnv_xive2_nvpg_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive2 *xive = PNV_XIVE2(opaque); + + xive2_error(xive, "NVPG: invalid write @%"HWADDR_PRIx, offset); +} + +static const MemoryRegionOps pnv_xive2_nvpg_ops = { + .read = pnv_xive2_nvpg_read, + .write = pnv_xive2_nvpg_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * POWER10 default capabilities: 0x2000120076f000FC + */ +#define PNV_XIVE2_CAPABILITIES 0x2000120076f000FC + +/* + * POWER10 default configuration: 0x0030000033000000 + * + * 8bits thread id was dropped for P10 + */ +#define PNV_XIVE2_CONFIGURATION 0x0030000033000000 + +static void pnv_xive2_reset(void *dev) +{ + PnvXive2 *xive = PNV_XIVE2(dev); + XiveSource *xsrc = &xive->ipi_source; + Xive2EndSource *end_xsrc = &xive->end_source; + + xive->cq_regs[CQ_XIVE_CAP >> 3] = xive->capabilities; + xive->cq_regs[CQ_XIVE_CFG >> 3] = xive->config; + + /* HW hardwires the #Topology of the chip in the block field */ + xive->cq_regs[CQ_XIVE_CFG >> 3] |= + SETFIELD(CQ_XIVE_CFG_HYP_HARD_BLOCK_ID, 0ull, xive->chip->chip_id); + + /* Set default page size to 64k */ + xive->ic_shift = xive->esb_shift = xive->end_shift = 16; + xive->nvc_shift = xive->nvpg_shift = xive->tm_shift = 16; + + /* Clear source MMIOs */ + if (memory_region_is_mapped(&xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->esb_mmio, &xsrc->esb_mmio); + } + + if (memory_region_is_mapped(&end_xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->end_mmio, &end_xsrc->esb_mmio); + } +} + +/* + * Maximum number of IRQs and ENDs supported by HW. Will be tuned by + * software. + */ +#define PNV_XIVE2_NR_IRQS (PNV10_XIVE2_ESB_SIZE / (1ull << XIVE_ESB_64K_2PAGE)) +#define PNV_XIVE2_NR_ENDS (PNV10_XIVE2_END_SIZE / (1ull << XIVE_ESB_64K_2PAGE)) + +static void pnv_xive2_realize(DeviceState *dev, Error **errp) +{ + PnvXive2 *xive = PNV_XIVE2(dev); + PnvXive2Class *pxc = PNV_XIVE2_GET_CLASS(dev); + XiveSource *xsrc = &xive->ipi_source; + Xive2EndSource *end_xsrc = &xive->end_source; + Error *local_err = NULL; + int i; + + pxc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + assert(xive->chip); + + /* + * The XiveSource and Xive2EndSource objects are realized with the + * maximum allowed HW configuration. The ESB MMIO regions will be + * resized dynamically when the controller is configured by the FW + * to limit accesses to resources not provisioned. + */ + object_property_set_int(OBJECT(xsrc), "flags", XIVE_SRC_STORE_EOI, + &error_fatal); + object_property_set_int(OBJECT(xsrc), "nr-irqs", PNV_XIVE2_NR_IRQS, + &error_fatal); + object_property_set_link(OBJECT(xsrc), "xive", OBJECT(xive), + &error_fatal); + qdev_realize(DEVICE(xsrc), NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + object_property_set_int(OBJECT(end_xsrc), "nr-ends", PNV_XIVE2_NR_ENDS, + &error_fatal); + object_property_set_link(OBJECT(end_xsrc), "xive", OBJECT(xive), + &error_abort); + qdev_realize(DEVICE(end_xsrc), NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* XSCOM region, used for initial configuration of the BARs */ + memory_region_init_io(&xive->xscom_regs, OBJECT(dev), + &pnv_xive2_xscom_ops, xive, "xscom-xive", + PNV10_XSCOM_XIVE2_SIZE << 3); + + /* Interrupt controller MMIO regions */ + xive->ic_shift = 16; + memory_region_init(&xive->ic_mmio, OBJECT(dev), "xive-ic", + PNV10_XIVE2_IC_SIZE); + + for (i = 0; i < ARRAY_SIZE(xive->ic_mmios); i++) { + memory_region_init_io(&xive->ic_mmios[i], OBJECT(dev), + pnv_xive2_ic_regions[i].ops, xive, + pnv_xive2_ic_regions[i].name, + pnv_xive2_ic_regions[i].pgsize << xive->ic_shift); + } + + /* + * VC MMIO regions. + */ + xive->esb_shift = 16; + xive->end_shift = 16; + memory_region_init(&xive->esb_mmio, OBJECT(xive), "xive-esb", + PNV10_XIVE2_ESB_SIZE); + memory_region_init(&xive->end_mmio, OBJECT(xive), "xive-end", + PNV10_XIVE2_END_SIZE); + + /* Presenter Controller MMIO region (not modeled) */ + xive->nvc_shift = 16; + xive->nvpg_shift = 16; + memory_region_init_io(&xive->nvc_mmio, OBJECT(dev), + &pnv_xive2_nvc_ops, xive, + "xive-nvc", PNV10_XIVE2_NVC_SIZE); + + memory_region_init_io(&xive->nvpg_mmio, OBJECT(dev), + &pnv_xive2_nvpg_ops, xive, + "xive-nvpg", PNV10_XIVE2_NVPG_SIZE); + + /* Thread Interrupt Management Area (Direct) */ + xive->tm_shift = 16; + memory_region_init_io(&xive->tm_mmio, OBJECT(dev), &pnv_xive2_tm_ops, + xive, "xive-tima", PNV10_XIVE2_TM_SIZE); + + qemu_register_reset(pnv_xive2_reset, dev); +} + +static Property pnv_xive2_properties[] = { + DEFINE_PROP_UINT64("ic-bar", PnvXive2, ic_base, 0), + DEFINE_PROP_UINT64("esb-bar", PnvXive2, esb_base, 0), + DEFINE_PROP_UINT64("end-bar", PnvXive2, end_base, 0), + DEFINE_PROP_UINT64("nvc-bar", PnvXive2, nvc_base, 0), + DEFINE_PROP_UINT64("nvpg-bar", PnvXive2, nvpg_base, 0), + DEFINE_PROP_UINT64("tm-bar", PnvXive2, tm_base, 0), + DEFINE_PROP_UINT64("capabilities", PnvXive2, capabilities, + PNV_XIVE2_CAPABILITIES), + DEFINE_PROP_UINT64("config", PnvXive2, config, + PNV_XIVE2_CONFIGURATION), + DEFINE_PROP_LINK("chip", PnvXive2, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_xive2_instance_init(Object *obj) +{ + PnvXive2 *xive = PNV_XIVE2(obj); + + object_initialize_child(obj, "ipi_source", &xive->ipi_source, + TYPE_XIVE_SOURCE); + object_initialize_child(obj, "end_source", &xive->end_source, + TYPE_XIVE2_END_SOURCE); +} + +static int pnv_xive2_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat_p10[] = "ibm,power10-xive-x"; + char *name; + int offset; + uint32_t reg[] = { + cpu_to_be32(PNV10_XSCOM_XIVE2_BASE), + cpu_to_be32(PNV10_XSCOM_XIVE2_SIZE) + }; + + name = g_strdup_printf("xive@%x", PNV10_XSCOM_XIVE2_BASE); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT(fdt_setprop(fdt, offset, "compatible", compat_p10, + sizeof(compat_p10))); + return 0; +} + +static void pnv_xive2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + Xive2RouterClass *xrc = XIVE2_ROUTER_CLASS(klass); + XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); + XivePresenterClass *xpc = XIVE_PRESENTER_CLASS(klass); + PnvXive2Class *pxc = PNV_XIVE2_CLASS(klass); + + xdc->dt_xscom = pnv_xive2_dt_xscom; + + dc->desc = "PowerNV XIVE2 Interrupt Controller (POWER10)"; + device_class_set_parent_realize(dc, pnv_xive2_realize, + &pxc->parent_realize); + device_class_set_props(dc, pnv_xive2_properties); + + xrc->get_eas = pnv_xive2_get_eas; + xrc->get_pq = pnv_xive2_get_pq; + xrc->set_pq = pnv_xive2_set_pq; + xrc->get_end = pnv_xive2_get_end; + xrc->write_end = pnv_xive2_write_end; + xrc->get_nvp = pnv_xive2_get_nvp; + xrc->write_nvp = pnv_xive2_write_nvp; + xrc->get_config = pnv_xive2_get_config; + xrc->get_block_id = pnv_xive2_get_block_id; + + xnc->notify = pnv_xive2_notify; + + xpc->match_nvt = pnv_xive2_match_nvt; +}; + +static const TypeInfo pnv_xive2_info = { + .name = TYPE_PNV_XIVE2, + .parent = TYPE_XIVE2_ROUTER, + .instance_init = pnv_xive2_instance_init, + .instance_size = sizeof(PnvXive2), + .class_init = pnv_xive2_class_init, + .class_size = sizeof(PnvXive2Class), + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_xive2_register_types(void) +{ + type_register_static(&pnv_xive2_info); +} + +type_init(pnv_xive2_register_types) + +static void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, + Monitor *mon) +{ + uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); + uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); + + if (!xive2_nvp_is_valid(nvp)) { + return; + } + + monitor_printf(mon, " %08x end:%02x/%04x IPB:%02x", + nvp_idx, eq_blk, eq_idx, + xive_get_field32(NVP2_W2_IPB, nvp->w2)); + /* + * When the NVP is HW controlled, more fields are updated + */ + if (xive2_nvp_is_hw(nvp)) { + monitor_printf(mon, " CPPR:%02x", + xive_get_field32(NVP2_W2_CPPR, nvp->w2)); + if (xive2_nvp_is_co(nvp)) { + monitor_printf(mon, " CO:%04x", + xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); + } + } + monitor_printf(mon, "\n"); +} + +/* + * If the table is direct, we can compute the number of PQ entries + * provisioned by FW. + */ +static uint32_t pnv_xive2_nr_esbs(PnvXive2 *xive) +{ + uint8_t blk = pnv_xive2_block_id(xive); + uint64_t vsd = xive->vsds[VST_ESB][blk]; + uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12); + + return VSD_INDIRECT & vsd ? 0 : vst_tsize * SBE_PER_BYTE; +} + +/* + * Compute the number of entries per indirect subpage. + */ +static uint64_t pnv_xive2_vst_per_subpage(PnvXive2 *xive, uint32_t type) +{ + uint8_t blk = pnv_xive2_block_id(xive); + uint64_t vsd = xive->vsds[type][blk]; + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vsd_addr; + uint32_t page_shift; + + /* For direct tables, fake a valid value */ + if (!(VSD_INDIRECT & vsd)) { + return 1; + } + + /* Get the page size of the indirect table. */ + vsd_addr = vsd & VSD_ADDRESS_MASK; + ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED); + + if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE2_DEBUG + xive2_error(xive, "VST: invalid %s entry!?", info->name); +#endif + return 0; + } + + page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + + if (!pnv_xive2_vst_page_size_allowed(page_shift)) { + xive2_error(xive, "VST: invalid %s page shift %d", info->name, + page_shift); + return 0; + } + + return (1ull << page_shift) / info->size; +} + +void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xive); + uint8_t blk = pnv_xive2_block_id(xive); + uint8_t chip_id = xive->chip->chip_id; + uint32_t srcno0 = XIVE_EAS(blk, 0); + uint32_t nr_esbs = pnv_xive2_nr_esbs(xive); + Xive2Eas eas; + Xive2End end; + Xive2Nvp nvp; + int i; + uint64_t xive_nvp_per_subpage; + + monitor_printf(mon, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0, + srcno0 + nr_esbs - 1); + xive_source_pic_print_info(&xive->ipi_source, srcno0, mon); + + monitor_printf(mon, "XIVE[%x] EAT %08x .. %08x\n", blk, srcno0, + srcno0 + nr_esbs - 1); + for (i = 0; i < nr_esbs; i++) { + if (xive2_router_get_eas(xrtr, blk, i, &eas)) { + break; + } + if (!xive2_eas_is_masked(&eas)) { + xive2_eas_pic_print_info(&eas, i, mon); + } + } + + monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); + i = 0; + while (!xive2_router_get_end(xrtr, blk, i, &end)) { + xive2_end_eas_pic_print_info(&end, i++, mon); + } + + monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); + i = 0; + while (!xive2_router_get_end(xrtr, blk, i, &end)) { + xive2_end_pic_print_info(&end, i++, mon); + } + + monitor_printf(mon, "XIVE[%x] #%d NVPT %08x .. %08x\n", chip_id, blk, + 0, XIVE2_NVP_COUNT - 1); + xive_nvp_per_subpage = pnv_xive2_vst_per_subpage(xive, VST_NVP); + for (i = 0; i < XIVE2_NVP_COUNT; i += xive_nvp_per_subpage) { + while (!xive2_router_get_nvp(xrtr, blk, i, &nvp)) { + xive2_nvp_pic_print_info(&nvp, i++, mon); + } + } +} diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h new file mode 100644 index 0000000000..0c096e4adb --- /dev/null +++ b/hw/intc/pnv_xive2_regs.h @@ -0,0 +1,442 @@ +/* + * QEMU PowerPC XIVE2 interrupt controller model (POWER10) + * + * Copyright (c) 2019-2022, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PPC_PNV_XIVE2_REGS_H +#define PPC_PNV_XIVE2_REGS_H + +/* + * CQ Common Queue (PowerBus bridge) Registers + */ + +/* XIVE2 Capabilities */ +#define X_CQ_XIVE_CAP 0x02 +#define CQ_XIVE_CAP 0x010 +#define CQ_XIVE_CAP_VERSION PPC_BITMASK(0, 3) +/* 4:6 reserved */ +#define CQ_XIVE_CAP_USER_INT_PRIO PPC_BITMASK(8, 9) +#define CQ_XIVE_CAP_USER_INT_PRIO_1 0 +#define CQ_XIVE_CAP_USER_INT_PRIO_1_2 1 +#define CQ_XIVE_CAP_USER_INT_PRIO_1_4 2 +#define CQ_XIVE_CAP_USER_INT_PRIO_1_8 3 +#define CQ_XIVE_CAP_VP_INT_PRIO PPC_BITMASK(10, 11) +#define CQ_XIVE_CAP_VP_INT_PRIO_1_8 0 +#define CQ_XIVE_CAP_VP_INT_PRIO_2_8 1 +#define CQ_XIVE_CAP_VP_INT_PRIO_4_8 2 +#define CQ_XIVE_CAP_VP_INT_PRIO_8 3 +#define CQ_XIVE_CAP_BLOCK_ID_WIDTH PPC_BITMASK(12, 13) +#define CQ_XIVE_CAP_VP_SAVE_RESTORE PPC_BIT(38) + +#define CQ_XIVE_CAP_PHB_PQ_DISABLE PPC_BIT(56) +#define CQ_XIVE_CAP_PHB_ABT PPC_BIT(57) +#define CQ_XIVE_CAP_EXPLOITATION_MODE PPC_BIT(58) +#define CQ_XIVE_CAP_STORE_EOI PPC_BIT(59) + +/* XIVE2 Configuration */ +#define X_CQ_XIVE_CFG 0x03 +#define CQ_XIVE_CFG 0x018 + +/* 0:7 reserved */ +#define CQ_XIVE_CFG_USER_INT_PRIO PPC_BITMASK(8, 9) +#define CQ_XIVE_CFG_VP_INT_PRIO PPC_BITMASK(10, 11) +#define CQ_XIVE_CFG_INT_PRIO_1 0 +#define CQ_XIVE_CFG_INT_PRIO_2 1 +#define CQ_XIVE_CFG_INT_PRIO_4 2 +#define CQ_XIVE_CFG_INT_PRIO_8 3 +#define CQ_XIVE_CFG_BLOCK_ID_WIDTH PPC_BITMASK(12, 13) +#define CQ_XIVE_CFG_BLOCK_ID_4BITS 0 +#define CQ_XIVE_CFG_BLOCK_ID_5BITS 1 +#define CQ_XIVE_CFG_BLOCK_ID_6BITS 2 +#define CQ_XIVE_CFG_BLOCK_ID_7BITS 3 +#define CQ_XIVE_CFG_HYP_HARD_RANGE PPC_BITMASK(14, 15) +#define CQ_XIVE_CFG_THREADID_7BITS 0 +#define CQ_XIVE_CFG_THREADID_8BITS 1 +#define CQ_XIVE_CFG_THREADID_9BITS 2 +#define CQ_XIVE_CFG_THREADID_10BITs 3 +#define CQ_XIVE_CFG_HYP_HARD_BLKID_OVERRIDE PPC_BIT(16) +#define CQ_XIVE_CFG_HYP_HARD_BLOCK_ID PPC_BITMASK(17, 23) + +#define CQ_XIVE_CFG_GEN1_TIMA_OS PPC_BIT(24) +#define CQ_XIVE_CFG_GEN1_TIMA_HYP PPC_BIT(25) +#define CQ_XIVE_CFG_GEN1_TIMA_HYP_BLK0 PPC_BIT(26) /* 0 if bit[25]=0 */ +#define CQ_XIVE_CFG_GEN1_TIMA_CROWD_DIS PPC_BIT(27) /* 0 if bit[25]=0 */ +#define CQ_XIVE_CFG_GEN1_END_ESX PPC_BIT(28) +#define CQ_XIVE_CFG_EN_VP_SAVE_RESTORE PPC_BIT(38) /* 0 if bit[25]=1 */ +#define CQ_XIVE_CFG_EN_VP_SAVE_REST_STRICT PPC_BIT(39) /* 0 if bit[25]=1 */ + +/* Interrupt Controller Base Address Register - 512 pages (32M) */ +#define X_CQ_IC_BAR 0x08 +#define CQ_IC_BAR 0x040 +#define CQ_IC_BAR_VALID PPC_BIT(0) +#define CQ_IC_BAR_64K PPC_BIT(1) +/* 2:7 reserved */ +#define CQ_IC_BAR_ADDR PPC_BITMASK(8, 42) +/* 43:63 reserved */ + +/* Thread Management Base Address Register - 4 pages */ +#define X_CQ_TM_BAR 0x09 +#define CQ_TM_BAR 0x048 +#define CQ_TM_BAR_VALID PPC_BIT(0) +#define CQ_TM_BAR_64K PPC_BIT(1) +#define CQ_TM_BAR_ADDR PPC_BITMASK(8, 49) + +/* ESB Base Address Register */ +#define X_CQ_ESB_BAR 0x0A +#define CQ_ESB_BAR 0x050 +#define CQ_BAR_VALID PPC_BIT(0) +#define CQ_BAR_64K PPC_BIT(1) +/* 2:7 reserved */ +#define CQ_BAR_ADDR PPC_BITMASK(8, 39) +#define CQ_BAR_SET_DIV PPC_BITMASK(56, 58) +#define CQ_BAR_RANGE PPC_BITMASK(59, 63) + /* 0 (16M) - 16 (16T) */ + +/* END Base Address Register */ +#define X_CQ_END_BAR 0x0B +#define CQ_END_BAR 0x058 + +/* NVPG Base Address Register */ +#define X_CQ_NVPG_BAR 0x0C +#define CQ_NVPG_BAR 0x060 + +/* NVC Base Address Register */ +#define X_CQ_NVC_BAR 0x0D +#define CQ_NVC_BAR 0x068 + +/* Table Address Register */ +#define X_CQ_TAR 0x0E +#define CQ_TAR 0x070 +#define CQ_TAR_AUTOINC PPC_BIT(0) +#define CQ_TAR_SELECT PPC_BITMASK(12, 15) +#define CQ_TAR_ESB 0 /* 0 - 15 */ +#define CQ_TAR_END 2 /* 0 - 15 */ +#define CQ_TAR_NVPG 3 /* 0 - 15 */ +#define CQ_TAR_NVC 5 /* 0 - 15 */ +#define CQ_TAR_ENTRY_SELECT PPC_BITMASK(28, 31) + +/* Table Data Register */ +#define X_CQ_TDR 0x0F +#define CQ_TDR 0x078 +/* for the NVPG, NVC, ESB, END Set Translation Tables */ +#define CQ_TDR_VALID PPC_BIT(0) +#define CQ_TDR_BLOCK_ID PPC_BITMASK(60, 63) + +/* + * Processor Cores Enabled for MsgSnd + * Identifies which of the 32 possible core chiplets are enabled and + * available to receive the MsgSnd command + */ +#define X_CQ_MSGSND 0x10 +#define CQ_MSGSND 0x080 + +/* Interrupt Unit Reset Control */ +#define X_CQ_RST_CTL 0x12 +#define CQ_RST_CTL 0x090 +#define CQ_RST_SYNC_RESET PPC_BIT(0) /* Write Only */ +#define CQ_RST_QUIESCE_PB PPC_BIT(1) /* RW */ +#define CQ_RST_MASTER_IDLE PPC_BIT(2) /* Read Only */ +#define CQ_RST_SAVE_IDLE PPC_BIT(3) /* Read Only */ +#define CQ_RST_PB_BAR_RESET PPC_BIT(4) /* Write Only */ + +/* PowerBus General Configuration */ +#define X_CQ_CFG_PB_GEN 0x14 +#define CQ_CFG_PB_GEN 0x0A0 +#define CQ_CFG_PB_GEN_PB_INIT PPC_BIT(45) + +/* + * FIR + * (And-Mask) + * (Or-Mask) + */ +#define X_CQ_FIR 0x30 +#define X_CQ_FIR_AND 0x31 +#define X_CQ_FIR_OR 0x32 +#define CQ_FIR 0x180 +#define CQ_FIR_AND 0x188 +#define CQ_FIR_OR 0x190 +#define CQ_FIR_PB_RCMDX_CI_ERR1 PPC_BIT(19) +#define CQ_FIR_VC_INFO_ERROR_0_2 PPC_BITMASK(61, 63) + +/* + * FIR Mask + * (And-Mask) + * (Or-Mask) + */ +#define X_CQ_FIRMASK 0x33 +#define X_CQ_FIRMASK_AND 0x34 +#define X_CQ_FIRMASK_OR 0x35 +#define CQ_FIRMASK 0x198 +#define CQ_FIRMASK_AND 0x1A0 +#define CQ_FIRMASK_OR 0x1A8 + +/* + * VC0 + */ + +/* VSD table address */ +#define X_VC_VSD_TABLE_ADDR 0x100 +#define VC_VSD_TABLE_ADDR 0x000 +#define VC_VSD_TABLE_AUTOINC PPC_BIT(0) +#define VC_VSD_TABLE_SELECT PPC_BITMASK(12, 15) +#define VC_VSD_TABLE_ADDRESS PPC_BITMASK(28, 31) + +/* VSD table data */ +#define X_VC_VSD_TABLE_DATA 0x101 +#define VC_VSD_TABLE_DATA 0x008 + +/* AIB AT macro indirect kill */ +#define X_VC_AT_MACRO_KILL 0x102 +#define VC_AT_MACRO_KILL 0x010 +#define VC_AT_MACRO_KILL_VALID PPC_BIT(0) +#define VC_AT_MACRO_KILL_VSD PPC_BITMASK(12, 15) +#define VC_AT_MACRO_KILL_BLOCK_ID PPC_BITMASK(28, 31) +#define VC_AT_MACRO_KILL_OFFSET PPC_BITMASK(48, 60) + +/* AIB AT macro indirect kill mask (same bit definitions) */ +#define X_VC_AT_MACRO_KILL_MASK 0x103 +#define VC_AT_MACRO_KILL_MASK 0x018 + +/* Remote IRQs and ERQs configuration [n] (n = 0:6) */ +#define X_VC_QUEUES_CFG_REM0 0x117 + +#define VC_QUEUES_CFG_REM0 0x0B8 +#define VC_QUEUES_CFG_REM1 0x0C0 +#define VC_QUEUES_CFG_REM2 0x0C8 +#define VC_QUEUES_CFG_REM3 0x0D0 +#define VC_QUEUES_CFG_REM4 0x0D8 +#define VC_QUEUES_CFG_REM5 0x0E0 +#define VC_QUEUES_CFG_REM6 0x0E8 +#define VC_QUEUES_CFG_MEMB_EN PPC_BIT(38) +#define VC_QUEUES_CFG_MEMB_SZ PPC_BITMASK(42, 47) + +/* + * VC1 + */ + +/* ESBC cache flush control trigger */ +#define X_VC_ESBC_FLUSH_CTRL 0x140 +#define VC_ESBC_FLUSH_CTRL 0x200 +#define VC_ESBC_FLUSH_CTRL_POLL_VALID PPC_BIT(0) +#define VC_ESBC_FLUSH_CTRL_WANT_CACHE_DISABLE PPC_BIT(2) + +/* ESBC cache flush poll trigger */ +#define X_VC_ESBC_FLUSH_POLL 0x141 +#define VC_ESBC_FLUSH_POLL 0x208 +#define VC_ESBC_FLUSH_POLL_BLOCK_ID PPC_BITMASK(0, 3) +#define VC_ESBC_FLUSH_POLL_OFFSET PPC_BITMASK(4, 31) /* 28-bit */ +#define VC_ESBC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) +#define VC_ESBC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ + +/* EASC flush control register */ +#define X_VC_EASC_FLUSH_CTRL 0x160 +#define VC_EASC_FLUSH_CTRL 0x300 +#define VC_EASC_FLUSH_CTRL_POLL_VALID PPC_BIT(0) +#define VC_EASC_FLUSH_CTRL_WANT_CACHE_DISABLE PPC_BIT(2) + +/* EASC flush poll register */ +#define X_VC_EASC_FLUSH_POLL 0x161 +#define VC_EASC_FLUSH_POLL 0x308 +#define VC_EASC_FLUSH_POLL_BLOCK_ID PPC_BITMASK(0, 3) +#define VC_EASC_FLUSH_POLL_OFFSET PPC_BITMASK(4, 31) /* 28-bit */ +#define VC_EASC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) +#define VC_EASC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ + +/* + * VC2 + */ + +/* ENDC flush control register */ +#define X_VC_ENDC_FLUSH_CTRL 0x180 +#define VC_ENDC_FLUSH_CTRL 0x400 +#define VC_ENDC_FLUSH_CTRL_POLL_VALID PPC_BIT(0) +#define VC_ENDC_FLUSH_CTRL_WANT_CACHE_DISABLE PPC_BIT(2) +#define VC_ENDC_FLUSH_CTRL_WANT_INVALIDATE PPC_BIT(3) +#define VC_ENDC_FLUSH_CTRL_INJECT_INVALIDATE PPC_BIT(7) + +/* ENDC flush poll register */ +#define X_VC_ENDC_FLUSH_POLL 0x181 +#define VC_ENDC_FLUSH_POLL 0x408 +#define VC_ENDC_FLUSH_POLL_BLOCK_ID PPC_BITMASK(4, 7) +#define VC_ENDC_FLUSH_POLL_OFFSET PPC_BITMASK(8, 31) /* 24-bit */ +#define VC_ENDC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) +#define VC_ENDC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ + +/* ENDC Sync done */ +#define X_VC_ENDC_SYNC_DONE 0x184 +#define VC_ENDC_SYNC_DONE 0x420 +#define VC_ENDC_SYNC_POLL_DONE PPC_BITMASK(0, 6) +#define VC_ENDC_SYNC_QUEUE_IPI PPC_BIT(0) +#define VC_ENDC_SYNC_QUEUE_HWD PPC_BIT(1) +#define VC_ENDC_SYNC_QUEUE_NXC PPC_BIT(2) +#define VC_ENDC_SYNC_QUEUE_INT PPC_BIT(3) +#define VC_ENDC_SYNC_QUEUE_OS PPC_BIT(4) +#define VC_ENDC_SYNC_QUEUE_POOL PPC_BIT(5) +#define VC_ENDC_SYNC_QUEUE_HARD PPC_BIT(6) +#define VC_QUEUE_COUNT 7 + +/* ENDC cache watch specification 0 */ +#define X_VC_ENDC_WATCH0_SPEC 0x1A0 +#define VC_ENDC_WATCH0_SPEC 0x500 +#define VC_ENDC_WATCH_CONFLICT PPC_BIT(0) +#define VC_ENDC_WATCH_FULL PPC_BIT(8) +#define VC_ENDC_WATCH_BLOCK_ID PPC_BITMASK(28, 31) +#define VC_ENDC_WATCH_INDEX PPC_BITMASK(40, 63) + +/* ENDC cache watch data 0 */ +#define X_VC_ENDC_WATCH0_DATA0 0x1A4 +#define X_VC_ENDC_WATCH0_DATA1 0x1A5 +#define X_VC_ENDC_WATCH0_DATA2 0x1A6 +#define X_VC_ENDC_WATCH0_DATA3 0x1A7 + +#define VC_ENDC_WATCH0_DATA0 0x520 +#define VC_ENDC_WATCH0_DATA1 0x528 +#define VC_ENDC_WATCH0_DATA2 0x530 +#define VC_ENDC_WATCH0_DATA3 0x538 + +/* + * PC LSB1 + */ + +/* VSD table address register */ +#define X_PC_VSD_TABLE_ADDR 0x200 +#define PC_VSD_TABLE_ADDR 0x000 +#define PC_VSD_TABLE_AUTOINC PPC_BIT(0) +#define PC_VSD_TABLE_SELECT PPC_BITMASK(12, 15) +#define PC_VSD_TABLE_ADDRESS PPC_BITMASK(28, 31) + +/* VSD table data register */ +#define X_PC_VSD_TABLE_DATA 0x201 +#define PC_VSD_TABLE_DATA 0x008 + +/* AT indirect kill register */ +#define X_PC_AT_KILL 0x202 +#define PC_AT_KILL 0x010 +#define PC_AT_KILL_VALID PPC_BIT(0) +#define PC_AT_KILL_VSD_TYPE PPC_BITMASK(24, 27) +/* Only NVP, NVG, NVC */ +#define PC_AT_KILL_BLOCK_ID PPC_BITMASK(28, 31) +#define PC_AT_KILL_OFFSET PPC_BITMASK(48, 60) + +/* AT indirect kill mask register */ +#define X_PC_AT_KILL_MASK 0x203 +#define PC_AT_KILL_MASK 0x018 +#define PC_AT_KILL_MASK_VSD_TYPE PPC_BITMASK(24, 27) +#define PC_AT_KILL_MASK_BLOCK_ID PPC_BITMASK(28, 31) +#define PC_AT_KILL_MASK_OFFSET PPC_BITMASK(48, 60) + +/* + * PC LSB2 + */ + +/* NxC Cache flush control */ +#define X_PC_NXC_FLUSH_CTRL 0x280 +#define PC_NXC_FLUSH_CTRL 0x400 +#define PC_NXC_FLUSH_CTRL_POLL_VALID PPC_BIT(0) +#define PC_NXC_FLUSH_CTRL_WANT_CACHE_DISABLE PPC_BIT(2) +#define PC_NXC_FLUSH_CTRL_WANT_INVALIDATE PPC_BIT(3) +#define PC_NXC_FLUSH_CTRL_INJECT_INVALIDATE PPC_BIT(7) + +/* NxC Cache flush poll */ +#define X_PC_NXC_FLUSH_POLL 0x281 +#define PC_NXC_FLUSH_POLL 0x408 +#define PC_NXC_FLUSH_POLL_NXC_TYPE PPC_BITMASK(2, 3) +#define PC_NXC_FLUSH_POLL_NXC_TYPE_NVP 0 +#define PC_NXC_FLUSH_POLL_NXC_TYPE_NVG 2 +#define PC_NXC_FLUSH_POLL_NXC_TYPE_NVC 3 +#define PC_NXC_FLUSH_POLL_BLOCK_ID PPC_BITMASK(4, 7) +#define PC_NXC_FLUSH_POLL_OFFSET PPC_BITMASK(8, 31) /* 24-bit */ +#define PC_NXC_FLUSH_POLL_NXC_TYPE_MASK PPC_BITMASK(34, 35) /* 0: Ign */ +#define PC_NXC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) +#define PC_NXC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ + +/* NxC Cache Watch 0 Specification */ +#define X_PC_NXC_WATCH0_SPEC 0x2A0 +#define PC_NXC_WATCH0_SPEC 0x500 +#define PC_NXC_WATCH_CONFLICT PPC_BIT(0) +#define PC_NXC_WATCH_FULL PPC_BIT(8) +#define PC_NXC_WATCH_NXC_TYPE PPC_BITMASK(26, 27) +#define PC_NXC_WATCH_NXC_NVP 0 +#define PC_NXC_WATCH_NXC_NVG 2 +#define PC_NXC_WATCH_NXC_NVC 3 +#define PC_NXC_WATCH_BLOCK_ID PPC_BITMASK(28, 31) +#define PC_NXC_WATCH_INDEX PPC_BITMASK(40, 63) + +/* NxC Cache Watch 0 Data */ +#define X_PC_NXC_WATCH0_DATA0 0x2A4 +#define X_PC_NXC_WATCH0_DATA1 0x2A5 +#define X_PC_NXC_WATCH0_DATA2 0x2A6 +#define X_PC_NXC_WATCH0_DATA3 0x2A7 + +#define PC_NXC_WATCH0_DATA0 0x520 +#define PC_NXC_WATCH0_DATA1 0x528 +#define PC_NXC_WATCH0_DATA2 0x530 +#define PC_NXC_WATCH0_DATA3 0x538 + +/* + * TCTXT Registers + */ + +/* Physical Thread Enable0 register */ +#define X_TCTXT_EN0 0x300 +#define TCTXT_EN0 0x000 + +/* Physical Thread Enable0 Set register */ +#define X_TCTXT_EN0_SET 0x302 +#define TCTXT_EN0_SET 0x010 + +/* Physical Thread Enable0 Reset register */ +#define X_TCTXT_EN0_RESET 0x303 +#define TCTXT_EN0_RESET 0x018 + +/* Physical Thread Enable1 register */ +#define X_TCTXT_EN1 0x304 +#define TCTXT_EN1 0x020 + +/* Physical Thread Enable1 Set register */ +#define X_TCTXT_EN1_SET 0x306 +#define TCTXT_EN1_SET 0x030 + +/* Physical Thread Enable1 Reset register */ +#define X_TCTXT_EN1_RESET 0x307 +#define TCTXT_EN1_RESET 0x038 + +/* + * VSD Tables + */ +#define VST_ESB 0 +#define VST_EAS 1 /* No used by PC */ +#define VST_END 2 +#define VST_NVP 3 +#define VST_NVG 4 +#define VST_NVC 5 +#define VST_IC 6 /* No used by PC */ +#define VST_SYNC 7 +#define VST_ERQ 8 /* No used by PC */ + +/* + * Bits in a VSD entry. + * + * Note: the address is naturally aligned, we don't use a PPC_BITMASK, + * but just a mask to apply to the address before OR'ing it in. + * + * Note: VSD_FIRMWARE is a SW bit ! It hijacks an unused bit in the + * VSD and is only meant to be used in indirect mode ! + */ +#define VSD_MODE PPC_BITMASK(0, 1) +#define VSD_MODE_SHARED 1 +#define VSD_MODE_EXCLUSIVE 2 +#define VSD_MODE_FORWARD 3 +#define VSD_FIRMWARE PPC_BIT(2) /* Read warning */ +#define VSD_FIRMWARE2 PPC_BIT(3) /* unused */ +#define VSD_RESERVED PPC_BITMASK(4, 7) /* P10 reserved */ +#define VSD_ADDRESS_MASK 0x00fffffffffff000ull +#define VSD_MIGRATION_REG PPC_BITMASK(52, 55) +#define VSD_INDIRECT PPC_BIT(56) +#define VSD_TSIZE PPC_BITMASK(59, 63) + +#endif /* PPC_PNV_XIVE2_REGS_H */ diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index f1a5d3d284..0412edc982 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -38,12 +38,18 @@ typedef struct riscv_aclint_mtimer_callback { int num; } riscv_aclint_mtimer_callback; -static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) +static uint64_t cpu_riscv_read_rtc_raw(uint32_t timebase_freq) { return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), timebase_freq, NANOSECONDS_PER_SECOND); } +static uint64_t cpu_riscv_read_rtc(void *opaque) +{ + RISCVAclintMTimerState *mtimer = opaque; + return cpu_riscv_read_rtc_raw(mtimer->timebase_freq) + mtimer->time_delta; +} + /* * Called when timecmp is written to update the QEMU timer or immediately * trigger timer interrupt if mtimecmp <= current timer value. @@ -51,13 +57,13 @@ static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer, RISCVCPU *cpu, int hartid, - uint64_t value, - uint32_t timebase_freq) + uint64_t value) { + uint32_t timebase_freq = mtimer->timebase_freq; uint64_t next; uint64_t diff; - uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq); + uint64_t rtc_r = cpu_riscv_read_rtc(mtimer); cpu->env.timecmp = value; if (cpu->env.timecmp <= rtc_r) { @@ -126,9 +132,9 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr, qemu_log_mask(LOG_GUEST_ERROR, "aclint-mtimer: invalid hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { - /* timecmp_lo */ + /* timecmp_lo for RV32/RV64 or timecmp for RV64 */ uint64_t timecmp = env->timecmp; - return timecmp & 0xFFFFFFFF; + return (size == 4) ? (timecmp & 0xFFFFFFFF) : timecmp; } else if ((addr & 0x7) == 4) { /* timecmp_hi */ uint64_t timecmp = env->timecmp; @@ -139,11 +145,12 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr, return 0; } } else if (addr == mtimer->time_base) { - /* time_lo */ - return cpu_riscv_read_rtc(mtimer->timebase_freq) & 0xFFFFFFFF; + /* time_lo for RV32/RV64 or timecmp for RV64 */ + uint64_t rtc = cpu_riscv_read_rtc(mtimer); + return (size == 4) ? (rtc & 0xFFFFFFFF) : rtc; } else if (addr == mtimer->time_base + 4) { /* time_hi */ - return (cpu_riscv_read_rtc(mtimer->timebase_freq) >> 32) & 0xFFFFFFFF; + return (cpu_riscv_read_rtc(mtimer) >> 32) & 0xFFFFFFFF; } qemu_log_mask(LOG_UNIMP, @@ -156,6 +163,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { RISCVAclintMTimerState *mtimer = opaque; + int i; if (addr >= mtimer->timecmp_base && addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) { @@ -167,33 +175,66 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, qemu_log_mask(LOG_GUEST_ERROR, "aclint-mtimer: invalid hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { - /* timecmp_lo */ - uint64_t timecmp_hi = env->timecmp >> 32; - riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, - timecmp_hi << 32 | (value & 0xFFFFFFFF), - mtimer->timebase_freq); - return; + if (size == 4) { + /* timecmp_lo for RV32/RV64 */ + uint64_t timecmp_hi = env->timecmp >> 32; + riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, + timecmp_hi << 32 | (value & 0xFFFFFFFF)); + } else { + /* timecmp for RV64 */ + riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, + value); + } } else if ((addr & 0x7) == 4) { - /* timecmp_hi */ - uint64_t timecmp_lo = env->timecmp; - riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, - value << 32 | (timecmp_lo & 0xFFFFFFFF), - mtimer->timebase_freq); + if (size == 4) { + /* timecmp_hi for RV32/RV64 */ + uint64_t timecmp_lo = env->timecmp; + riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, + value << 32 | (timecmp_lo & 0xFFFFFFFF)); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "aclint-mtimer: invalid timecmp_hi write: %08x", + (uint32_t)addr); + } } else { qemu_log_mask(LOG_UNIMP, "aclint-mtimer: invalid timecmp write: %08x", (uint32_t)addr); } return; - } else if (addr == mtimer->time_base) { - /* time_lo */ - qemu_log_mask(LOG_UNIMP, - "aclint-mtimer: time_lo write not implemented"); - return; - } else if (addr == mtimer->time_base + 4) { - /* time_hi */ - qemu_log_mask(LOG_UNIMP, - "aclint-mtimer: time_hi write not implemented"); + } else if (addr == mtimer->time_base || addr == mtimer->time_base + 4) { + uint64_t rtc_r = cpu_riscv_read_rtc_raw(mtimer->timebase_freq); + + if (addr == mtimer->time_base) { + if (size == 4) { + /* time_lo for RV32/RV64 */ + mtimer->time_delta = ((rtc_r & ~0xFFFFFFFFULL) | value) - rtc_r; + } else { + /* time for RV64 */ + mtimer->time_delta = value - rtc_r; + } + } else { + if (size == 4) { + /* time_hi for RV32/RV64 */ + mtimer->time_delta = (value << 32 | (rtc_r & 0xFFFFFFFF)) - rtc_r; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "aclint-mtimer: invalid time_hi write: %08x", + (uint32_t)addr); + return; + } + } + + /* Check if timer interrupt is triggered for each hart. */ + for (i = 0; i < mtimer->num_harts; i++) { + CPUState *cpu = qemu_get_cpu(mtimer->hartid_base + i); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + continue; + } + riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), + i, env->timecmp); + } return; } @@ -208,6 +249,10 @@ static const MemoryRegionOps riscv_aclint_mtimer_ops = { .valid = { .min_access_size = 4, .max_access_size = 8 + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, } }; @@ -235,7 +280,7 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) s, TYPE_RISCV_ACLINT_MTIMER, s->aperture_size); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); - s->timer_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); + s->timer_irqs = g_new(qemu_irq, s->num_harts); qdev_init_gpio_out(dev, s->timer_irqs, s->num_harts); /* Claim timer interrupt bits */ @@ -248,11 +293,29 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) } } +static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type) +{ + /* + * According to RISC-V ACLINT spec: + * - On MTIMER device reset, the MTIME register is cleared to zero. + * - On MTIMER device reset, the MTIMECMP registers are in unknown state. + */ + RISCVAclintMTimerState *mtimer = RISCV_ACLINT_MTIMER(obj); + + /* + * Clear mtime register by writing to 0 it. + * Pending mtime interrupts will also be cleared at the same time. + */ + riscv_aclint_mtimer_write(mtimer, mtimer->time_base, 0, 8); +} + static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = riscv_aclint_mtimer_realize; device_class_set_props(dc, riscv_aclint_mtimer_properties); + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.enter = riscv_aclint_mtimer_reset_enter; } static const TypeInfo riscv_aclint_mtimer_info = { @@ -292,14 +355,14 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, RISCVCPU *rvcpu = RISCV_CPU(cpu); CPURISCVState *env = cpu ? cpu->env_ptr : NULL; riscv_aclint_mtimer_callback *cb = - g_malloc0(sizeof(riscv_aclint_mtimer_callback)); + g_new0(riscv_aclint_mtimer_callback, 1); if (!env) { g_free(cb); continue; } if (provide_rdtime) { - riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq); + riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, dev); } cb->s = RISCV_ACLINT_MTIMER(dev); @@ -393,7 +456,7 @@ static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp) TYPE_RISCV_ACLINT_SWI, RISCV_ACLINT_SWI_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &swi->mmio); - swi->soft_irqs = g_malloc(sizeof(qemu_irq) * swi->num_harts); + swi->soft_irqs = g_new(qemu_irq, swi->num_harts); qdev_init_gpio_out(dev, swi->soft_irqs, swi->num_harts); /* Claim software interrupt bits */ @@ -407,11 +470,32 @@ static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp) } } +static void riscv_aclint_swi_reset_enter(Object *obj, ResetType type) +{ + /* + * According to RISC-V ACLINT spec: + * - On MSWI device reset, each MSIP register is cleared to zero. + * + * p.s. SSWI device reset does nothing since SETSIP register always reads 0. + */ + RISCVAclintSwiState *swi = RISCV_ACLINT_SWI(obj); + int i; + + if (!swi->sswi) { + for (i = 0; i < swi->num_harts; i++) { + /* Clear MSIP registers by lowering software interrupts. */ + qemu_irq_lower(swi->soft_irqs[i]); + } + } +} + static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = riscv_aclint_swi_realize; device_class_set_props(dc, riscv_aclint_swi_properties); + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.enter = riscv_aclint_swi_reset_enter; } static const TypeInfo riscv_aclint_swi_info = { diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c new file mode 100644 index 0000000000..8615e4cc1d --- /dev/null +++ b/hw/intc/riscv_imsic.c @@ -0,0 +1,448 @@ +/* + * RISC-V IMSIC (Incoming Message Signaled Interrupt Controller) + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/bswap.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" +#include "hw/pci/msi.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/intc/riscv_imsic.h" +#include "hw/irq.h" +#include "target/riscv/cpu.h" +#include "target/riscv/cpu_bits.h" +#include "sysemu/sysemu.h" +#include "migration/vmstate.h" + +#define IMSIC_MMIO_PAGE_LE 0x00 +#define IMSIC_MMIO_PAGE_BE 0x04 + +#define IMSIC_MIN_ID ((IMSIC_EIPx_BITS * 2) - 1) +#define IMSIC_MAX_ID (IMSIC_TOPEI_IID_MASK) + +#define IMSIC_EISTATE_PENDING (1U << 0) +#define IMSIC_EISTATE_ENABLED (1U << 1) +#define IMSIC_EISTATE_ENPEND (IMSIC_EISTATE_ENABLED | \ + IMSIC_EISTATE_PENDING) + +static uint32_t riscv_imsic_topei(RISCVIMSICState *imsic, uint32_t page) +{ + uint32_t i, max_irq, base; + + base = page * imsic->num_irqs; + max_irq = (imsic->eithreshold[page] && + (imsic->eithreshold[page] <= imsic->num_irqs)) ? + imsic->eithreshold[page] : imsic->num_irqs; + for (i = 1; i < max_irq; i++) { + if ((imsic->eistate[base + i] & IMSIC_EISTATE_ENPEND) == + IMSIC_EISTATE_ENPEND) { + return (i << IMSIC_TOPEI_IID_SHIFT) | i; + } + } + + return 0; +} + +static void riscv_imsic_update(RISCVIMSICState *imsic, uint32_t page) +{ + if (imsic->eidelivery[page] && riscv_imsic_topei(imsic, page)) { + qemu_irq_raise(imsic->external_irqs[page]); + } else { + qemu_irq_lower(imsic->external_irqs[page]); + } +} + +static int riscv_imsic_eidelivery_rmw(RISCVIMSICState *imsic, uint32_t page, + target_ulong *val, + target_ulong new_val, + target_ulong wr_mask) +{ + target_ulong old_val = imsic->eidelivery[page]; + + if (val) { + *val = old_val; + } + + wr_mask &= 0x1; + imsic->eidelivery[page] = (old_val & ~wr_mask) | (new_val & wr_mask); + + riscv_imsic_update(imsic, page); + return 0; +} + +static int riscv_imsic_eithreshold_rmw(RISCVIMSICState *imsic, uint32_t page, + target_ulong *val, + target_ulong new_val, + target_ulong wr_mask) +{ + target_ulong old_val = imsic->eithreshold[page]; + + if (val) { + *val = old_val; + } + + wr_mask &= IMSIC_MAX_ID; + imsic->eithreshold[page] = (old_val & ~wr_mask) | (new_val & wr_mask); + + riscv_imsic_update(imsic, page); + return 0; +} + +static int riscv_imsic_topei_rmw(RISCVIMSICState *imsic, uint32_t page, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + uint32_t base, topei = riscv_imsic_topei(imsic, page); + + /* Read pending and enabled interrupt with highest priority */ + if (val) { + *val = topei; + } + + /* Writes ignore value and clear top pending interrupt */ + if (topei && wr_mask) { + topei >>= IMSIC_TOPEI_IID_SHIFT; + base = page * imsic->num_irqs; + if (topei) { + imsic->eistate[base + topei] &= ~IMSIC_EISTATE_PENDING; + } + + riscv_imsic_update(imsic, page); + } + + return 0; +} + +static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic, + uint32_t xlen, uint32_t page, + uint32_t num, bool pend, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + uint32_t i, base; + target_ulong mask; + uint32_t state = (pend) ? IMSIC_EISTATE_PENDING : IMSIC_EISTATE_ENABLED; + + if (xlen != 32) { + if (num & 0x1) { + return -EINVAL; + } + num >>= 1; + } + if (num >= (imsic->num_irqs / xlen)) { + return -EINVAL; + } + + base = (page * imsic->num_irqs) + (num * xlen); + + if (val) { + *val = 0; + for (i = 0; i < xlen; i++) { + mask = (target_ulong)1 << i; + *val |= (imsic->eistate[base + i] & state) ? mask : 0; + } + } + + for (i = 0; i < xlen; i++) { + /* Bit0 of eip0 and eie0 are read-only zero */ + if (!num && !i) { + continue; + } + + mask = (target_ulong)1 << i; + if (wr_mask & mask) { + if (new_val & mask) { + imsic->eistate[base + i] |= state; + } else { + imsic->eistate[base + i] &= ~state; + } + } + } + + riscv_imsic_update(imsic, page); + return 0; +} + +static int riscv_imsic_rmw(void *arg, target_ulong reg, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + RISCVIMSICState *imsic = arg; + uint32_t isel, priv, virt, vgein, xlen, page; + + priv = AIA_IREG_PRIV(reg); + virt = AIA_IREG_VIRT(reg); + isel = AIA_IREG_ISEL(reg); + vgein = AIA_IREG_VGEIN(reg); + xlen = AIA_IREG_XLEN(reg); + + if (imsic->mmode) { + if (priv == PRV_M && !virt) { + page = 0; + } else { + goto err; + } + } else { + if (priv == PRV_S) { + if (virt) { + if (vgein && vgein < imsic->num_pages) { + page = vgein; + } else { + goto err; + } + } else { + page = 0; + } + } else { + goto err; + } + } + + switch (isel) { + case ISELECT_IMSIC_EIDELIVERY: + return riscv_imsic_eidelivery_rmw(imsic, page, val, + new_val, wr_mask); + case ISELECT_IMSIC_EITHRESHOLD: + return riscv_imsic_eithreshold_rmw(imsic, page, val, + new_val, wr_mask); + case ISELECT_IMSIC_TOPEI: + return riscv_imsic_topei_rmw(imsic, page, val, new_val, wr_mask); + case ISELECT_IMSIC_EIP0 ... ISELECT_IMSIC_EIP63: + return riscv_imsic_eix_rmw(imsic, xlen, page, + isel - ISELECT_IMSIC_EIP0, + true, val, new_val, wr_mask); + case ISELECT_IMSIC_EIE0 ... ISELECT_IMSIC_EIE63: + return riscv_imsic_eix_rmw(imsic, xlen, page, + isel - ISELECT_IMSIC_EIE0, + false, val, new_val, wr_mask); + default: + break; + }; + +err: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid register priv=%d virt=%d isel=%d vgein=%d\n", + __func__, priv, virt, isel, vgein); + return -EINVAL; +} + +static uint64_t riscv_imsic_read(void *opaque, hwaddr addr, unsigned size) +{ + RISCVIMSICState *imsic = opaque; + + /* Reads must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + /* Reads cannot be out of range */ + if (addr > IMSIC_MMIO_SIZE(imsic->num_pages)) { + goto err; + } + + return 0; + +err: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid register read 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; +} + +static void riscv_imsic_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + RISCVIMSICState *imsic = opaque; + uint32_t page; + + /* Writes must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + /* Writes cannot be out of range */ + if (addr > IMSIC_MMIO_SIZE(imsic->num_pages)) { + goto err; + } + + /* Writes only supported for MSI little-endian registers */ + page = addr >> IMSIC_MMIO_PAGE_SHIFT; + if ((addr & (IMSIC_MMIO_PAGE_SZ - 1)) == IMSIC_MMIO_PAGE_LE) { + if (value && (value < imsic->num_irqs)) { + imsic->eistate[(page * imsic->num_irqs) + value] |= + IMSIC_EISTATE_PENDING; + } + } + + /* Update CPU external interrupt status */ + riscv_imsic_update(imsic, page); + + return; + +err: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid register write 0x%" HWADDR_PRIx "\n", + __func__, addr); +} + +static const MemoryRegionOps riscv_imsic_ops = { + .read = riscv_imsic_read, + .write = riscv_imsic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void riscv_imsic_realize(DeviceState *dev, Error **errp) +{ + RISCVIMSICState *imsic = RISCV_IMSIC(dev); + RISCVCPU *rcpu = RISCV_CPU(qemu_get_cpu(imsic->hartid)); + CPUState *cpu = qemu_get_cpu(imsic->hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + + imsic->num_eistate = imsic->num_pages * imsic->num_irqs; + imsic->eidelivery = g_new0(uint32_t, imsic->num_pages); + imsic->eithreshold = g_new0(uint32_t, imsic->num_pages); + imsic->eistate = g_new0(uint32_t, imsic->num_eistate); + + memory_region_init_io(&imsic->mmio, OBJECT(dev), &riscv_imsic_ops, + imsic, TYPE_RISCV_IMSIC, + IMSIC_MMIO_SIZE(imsic->num_pages)); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &imsic->mmio); + + /* Claim the CPU interrupt to be triggered by this IMSIC */ + if (riscv_cpu_claim_interrupts(rcpu, + (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) { + error_setg(errp, "%s already claimed", + (imsic->mmode) ? "MEIP" : "SEIP"); + return; + } + + /* Create output IRQ lines */ + imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages); + qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages); + + /* Force select AIA feature and setup CSR read-modify-write callback */ + if (env) { + riscv_set_feature(env, RISCV_FEATURE_AIA); + if (!imsic->mmode) { + riscv_cpu_set_geilen(env, imsic->num_pages - 1); + } + riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, + riscv_imsic_rmw, imsic); + } + + msi_nonbroken = true; +} + +static Property riscv_imsic_properties[] = { + DEFINE_PROP_BOOL("mmode", RISCVIMSICState, mmode, 0), + DEFINE_PROP_UINT32("hartid", RISCVIMSICState, hartid, 0), + DEFINE_PROP_UINT32("num-pages", RISCVIMSICState, num_pages, 0), + DEFINE_PROP_UINT32("num-irqs", RISCVIMSICState, num_irqs, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_riscv_imsic = { + .name = "riscv_imsic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT32(eidelivery, RISCVIMSICState, + num_pages, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(eithreshold, RISCVIMSICState, + num_pages, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(eistate, RISCVIMSICState, + num_eistate, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_END_OF_LIST() + } +}; + +static void riscv_imsic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, riscv_imsic_properties); + dc->realize = riscv_imsic_realize; + dc->vmsd = &vmstate_riscv_imsic; +} + +static const TypeInfo riscv_imsic_info = { + .name = TYPE_RISCV_IMSIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RISCVIMSICState), + .class_init = riscv_imsic_class_init, +}; + +static void riscv_imsic_register_types(void) +{ + type_register_static(&riscv_imsic_info); +} + +type_init(riscv_imsic_register_types) + +/* + * Create IMSIC device. + */ +DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode, + uint32_t num_pages, uint32_t num_ids) +{ + DeviceState *dev = qdev_new(TYPE_RISCV_IMSIC); + CPUState *cpu = qemu_get_cpu(hartid); + uint32_t i; + + assert(!(addr & (IMSIC_MMIO_PAGE_SZ - 1))); + if (mmode) { + assert(num_pages == 1); + } else { + assert(num_pages >= 1 && num_pages <= (IRQ_LOCAL_GUEST_MAX + 1)); + } + assert(IMSIC_MIN_ID <= num_ids); + assert(num_ids <= IMSIC_MAX_ID); + assert((num_ids & IMSIC_MIN_ID) == IMSIC_MIN_ID); + + qdev_prop_set_bit(dev, "mmode", mmode); + qdev_prop_set_uint32(dev, "hartid", hartid); + qdev_prop_set_uint32(dev, "num-pages", num_pages); + qdev_prop_set_uint32(dev, "num-irqs", num_ids + 1); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + + for (i = 0; i < num_pages; i++) { + if (!i) { + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), + (mmode) ? IRQ_M_EXT : IRQ_S_EXT)); + } else { + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), + IRQ_LOCAL_MAX + i - 1)); + } + } + + return dev; +} diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index efe5054182..4e86d2d436 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -24,7 +24,7 @@ #include "trace.h" #include "qom/object.h" -#define FLIC_SAVE_INITIAL_SIZE qemu_real_host_page_size +#define FLIC_SAVE_INITIAL_SIZE qemu_real_host_page_size() #define FLIC_FAILED (-1UL) #define FLIC_SAVEVM_VERSION 1 diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index eae95c716f..dc641cc604 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -480,6 +480,29 @@ static uint8_t spapr_xive_get_block_id(XiveRouter *xrtr) return SPAPR_XIVE_BLOCK_ID; } +static int spapr_xive_get_pq(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + uint8_t *pq) +{ + SpaprXive *xive = SPAPR_XIVE(xrtr); + + assert(SPAPR_XIVE_BLOCK_ID == blk); + + *pq = xive_source_esb_get(&xive->source, idx); + return 0; +} + +static int spapr_xive_set_pq(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + uint8_t *pq) +{ + SpaprXive *xive = SPAPR_XIVE(xrtr); + + assert(SPAPR_XIVE_BLOCK_ID == blk); + + *pq = xive_source_esb_set(&xive->source, idx, *pq); + return 0; +} + + static const VMStateDescription vmstate_spapr_xive_end = { .name = TYPE_SPAPR_XIVE "/end", .version_id = 1, @@ -788,6 +811,8 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_spapr_xive; xrc->get_eas = spapr_xive_get_eas; + xrc->get_pq = spapr_xive_get_pq; + xrc->set_pq = spapr_xive_set_pq; xrc->get_end = spapr_xive_get_end; xrc->write_end = spapr_xive_write_end; xrc->get_nvt = spapr_xive_get_nvt; diff --git a/hw/intc/trace-events b/hw/intc/trace-events index b28cda4e08..5271590304 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -151,8 +151,9 @@ gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d rea gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x value 0x%" PRIx64 gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64 gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64 -gicv3_cpuif_virt_update(uint32_t cpuid, int idx) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d" -gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d maintenance-irq %d" +gicv3_cpuif_virt_update(uint32_t cpuid, int idx, int hppvlpi, int grp, int prio) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d HPPVLPI %d grp %d prio %d" +gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d" +gicv3_cpuif_virt_set_maint_irq(uint32_t cpuid, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting maintenance-irq %d" # arm_gicv3_dist.c gicv3_dist_read(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" @@ -176,6 +177,38 @@ gicv3_its_write(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: gicv3_its_badwrite(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u: error" gicv3_its_translation_write(uint64_t offset, uint64_t data, unsigned size, uint32_t requester_id) "GICv3 ITS TRANSLATER write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u requester_id 0x%x" gicv3_its_process_command(uint32_t rd_offset, uint8_t cmd) "GICv3 ITS: processing command at offset 0x%x: 0x%x" +gicv3_its_cmd_int(uint32_t devid, uint32_t eventid) "GICv3 ITS: command INT DeviceID 0x%x EventID 0x%x" +gicv3_its_cmd_clear(uint32_t devid, uint32_t eventid) "GICv3 ITS: command CLEAR DeviceID 0x%x EventID 0x%x" +gicv3_its_cmd_discard(uint32_t devid, uint32_t eventid) "GICv3 ITS: command DISCARD DeviceID 0x%x EventID 0x%x" +gicv3_its_cmd_sync(void) "GICv3 ITS: command SYNC" +gicv3_its_cmd_mapd(uint32_t devid, uint32_t size, uint64_t ittaddr, int valid) "GICv3 ITS: command MAPD DeviceID 0x%x Size 0x%x ITT_addr 0x%" PRIx64 " V %d" +gicv3_its_cmd_mapc(uint32_t icid, uint64_t rdbase, int valid) "GICv3 ITS: command MAPC ICID 0x%x RDbase 0x%" PRIx64 " V %d" +gicv3_its_cmd_mapi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MAPI DeviceID 0x%x EventID 0x%x ICID 0x%x" +gicv3_its_cmd_mapti(uint32_t devid, uint32_t eventid, uint32_t icid, uint32_t intid) "GICv3 ITS: command MAPTI DeviceID 0x%x EventID 0x%x ICID 0x%x pINTID 0x%x" +gicv3_its_cmd_inv(uint32_t devid, uint32_t eventid) "GICv3 ITS: command INV DeviceID 0x%x EventID 0x%x" +gicv3_its_cmd_invall(void) "GICv3 ITS: command INVALL" +gicv3_its_cmd_movall(uint64_t rd1, uint64_t rd2) "GICv3 ITS: command MOVALL RDbase1 0x%" PRIx64 " RDbase2 0x%" PRIx64 +gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MOVI DeviceID 0x%x EventID 0x%x ICID 0x%x" +gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x" +gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x" +gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x" +gicv3_its_cmd_vmovp(uint32_t vpeid, uint64_t rdbase) "GICv3 ITS: command VMOVP vPEID 0x%x RDbase 0x%" PRIx64 +gicv3_its_cmd_vsync(void) "GICv3 ITS: command VSYNC" +gicv3_its_cmd_vmovi(uint32_t devid, uint32_t eventid, uint32_t vpeid, int dbvalid, uint32_t doorbell) "GICv3 ITS: command VMOVI DeviceID 0x%x EventID 0x%x vPEID 0x%x D %d Dbell_pINTID 0x%x" +gicv3_its_cmd_vinvall(uint32_t vpeid) "GICv3 ITS: command VINVALL vPEID 0x%x" +gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x" +gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x" +gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x" +gicv3_its_cte_read_fault(uint32_t icid) "GICv3 ITS: Collection Table read for ICID 0x%x: faulted" +gicv3_its_ite_read(uint64_t ittaddr, uint32_t eventid, int valid, int inttype, uint32_t intid, uint32_t icid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: Interrupt Table read for ITTaddr 0x%" PRIx64 " EventID 0x%x: valid %d inttype %d intid 0x%x ICID 0x%x vPEID 0x%x doorbell 0x%x" +gicv3_its_ite_read_fault(uint64_t ittaddr, uint32_t eventid) "GICv3 ITS: Interrupt Table read for ITTaddr 0x%" PRIx64 " EventID 0x%x: faulted" +gicv3_its_ite_write(uint64_t ittaddr, uint32_t eventid, int valid, int inttype, uint32_t intid, uint32_t icid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: Interrupt Table write for ITTaddr 0x%" PRIx64 " EventID 0x%x: valid %d inttype %d intid 0x%x ICID 0x%x vPEID 0x%x doorbell 0x%x" +gicv3_its_dte_read(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table read for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64 +gicv3_its_dte_write(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table write for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64 +gicv3_its_dte_read_fault(uint32_t devid) "GICv3 ITS: Device Table read for DeviceID 0x%x: faulted" +gicv3_its_vte_read(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table read for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x" +gicv3_its_vte_read_fault(uint32_t vpeid) "GICv3 ITS: vPE Table read for vPEID 0x%x: faulted" +gicv3_its_vte_write(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table write for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x" # armv7m_nvic.c nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d" diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 48a835eab7..24e67020db 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -604,7 +604,7 @@ static void ics_realize(DeviceState *dev, Error **errp) error_setg(errp, "Number of interrupts needs to be greater 0"); return; } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); + ics->irqs = g_new0(ICSIRQState, ics->nr_irqs); qemu_register_reset(ics_reset_handler, ics); } diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index f5bfc501bc..9719d98a17 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu-common.h" #include "trace.h" #include "sysemu/kvm.h" #include "hw/ppc/spapr.h" diff --git a/hw/intc/xive.c b/hw/intc/xive.c index f15f98588a..b8e4c7294d 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -886,6 +886,16 @@ static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno) } } +/* + * Sources can be configured with PQ offloading in which case the check + * on the PQ state bits of MSIs is disabled + */ +static bool xive_source_esb_disabled(XiveSource *xsrc, uint32_t srcno) +{ + return (xsrc->esb_flags & XIVE_SRC_PQ_DISABLE) && + !xive_source_irq_is_lsi(xsrc, srcno); +} + /* * Returns whether the event notification should be forwarded. */ @@ -895,6 +905,10 @@ static bool xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno) assert(srcno < xsrc->nr_irqs); + if (xive_source_esb_disabled(xsrc, srcno)) { + return true; + } + ret = xive_esb_trigger(&xsrc->status[srcno]); if (xive_source_irq_is_lsi(xsrc, srcno) && @@ -915,6 +929,11 @@ static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno) assert(srcno < xsrc->nr_irqs); + if (xive_source_esb_disabled(xsrc, srcno)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid EOI for IRQ %d\n", srcno); + return false; + } + ret = xive_esb_eoi(&xsrc->status[srcno]); /* @@ -936,9 +955,10 @@ static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno) static void xive_source_notify(XiveSource *xsrc, int srcno) { XiveNotifierClass *xnc = XIVE_NOTIFIER_GET_CLASS(xsrc->xive); + bool pq_checked = !xive_source_esb_disabled(xsrc, srcno); if (xnc->notify) { - xnc->notify(xsrc->xive, srcno); + xnc->notify(xsrc->xive, srcno, pq_checked); } } @@ -1061,6 +1081,15 @@ static void xive_source_esb_write(void *opaque, hwaddr addr, notify = xive_source_esb_eoi(xsrc, srcno); break; + /* + * This is an internal offset used to inject triggers when the PQ + * state bits are not controlled locally. Such as for LSIs when + * under ABT mode. + */ + case XIVE_ESB_INJECT ... XIVE_ESB_INJECT + 0x3FF: + notify = true; + break; + case XIVE_ESB_SET_PQ_00 ... XIVE_ESB_SET_PQ_00 + 0x0FF: case XIVE_ESB_SET_PQ_01 ... XIVE_ESB_SET_PQ_01 + 0x0FF: case XIVE_ESB_SET_PQ_10 ... XIVE_ESB_SET_PQ_10 + 0x0FF: @@ -1361,6 +1390,24 @@ int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, return xrc->get_eas(xrtr, eas_blk, eas_idx, eas); } +static +int xive_router_get_pq(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->get_pq(xrtr, eas_blk, eas_idx, pq); +} + +static +int xive_router_set_pq(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->set_pq(xrtr, eas_blk, eas_idx, pq); +} + int xive_router_get_end(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, XiveEND *end) { @@ -1712,7 +1759,7 @@ do_escalation: xive_get_field32(END_W5_ESC_END_DATA, end.w5)); } -void xive_router_notify(XiveNotifier *xn, uint32_t lisn) +void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) { XiveRouter *xrtr = XIVE_ROUTER(xn); uint8_t eas_blk = XIVE_EAS_BLOCK(lisn); @@ -1725,11 +1772,27 @@ void xive_router_notify(XiveNotifier *xn, uint32_t lisn) return; } - /* - * The IVRE checks the State Bit Cache at this point. We skip the - * SBC lookup because the state bits of the sources are modeled - * internally in QEMU. - */ + if (!pq_checked) { + bool notify; + uint8_t pq; + + /* PQ cache lookup */ + if (xive_router_get_pq(xrtr, eas_blk, eas_idx, &pq)) { + /* Set FIR */ + g_assert_not_reached(); + } + + notify = xive_esb_trigger(&pq); + + if (xive_router_set_pq(xrtr, eas_blk, eas_idx, &pq)) { + /* Set FIR */ + g_assert_not_reached(); + } + + if (!notify) { + return; + } + } if (!xive_eas_is_valid(&eas)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid LISN %x\n", lisn); diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c new file mode 100644 index 0000000000..3aff42a69e --- /dev/null +++ b/hw/intc/xive2.c @@ -0,0 +1,1019 @@ +/* + * QEMU PowerPC XIVE2 interrupt controller model (POWER10) + * + * Copyright (c) 2019-2022, IBM Corporation.. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "target/ppc/cpu.h" +#include "sysemu/cpus.h" +#include "sysemu/dma.h" +#include "hw/qdev-properties.h" +#include "monitor/monitor.h" +#include "hw/ppc/xive.h" +#include "hw/ppc/xive2.h" +#include "hw/ppc/xive2_regs.h" + +uint32_t xive2_router_get_config(Xive2Router *xrtr) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->get_config(xrtr); +} + +void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, Monitor *mon) +{ + if (!xive2_eas_is_valid(eas)) { + return; + } + + monitor_printf(mon, " %08x %s end:%02x/%04x data:%08x\n", + lisn, xive2_eas_is_masked(eas) ? "M" : " ", + (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); +} + +void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, + Monitor *mon) +{ + uint64_t qaddr_base = xive2_end_qaddr(end); + uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); + uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); + uint32_t qentries = 1 << (qsize + 10); + int i; + + /* + * print out the [ (qindex - (width - 1)) .. (qindex + 1)] window + */ + monitor_printf(mon, " [ "); + qindex = (qindex - (width - 1)) & (qentries - 1); + for (i = 0; i < width; i++) { + uint64_t qaddr = qaddr_base + (qindex << 2); + uint32_t qdata = -1; + + if (dma_memory_read(&address_space_memory, qaddr, &qdata, + sizeof(qdata), MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to read EQ @0x%" + HWADDR_PRIx "\n", qaddr); + return; + } + monitor_printf(mon, "%s%08x ", i == width - 1 ? "^" : "", + be32_to_cpu(qdata)); + qindex = (qindex + 1) & (qentries - 1); + } + monitor_printf(mon, "]"); +} + +void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) +{ + uint64_t qaddr_base = xive2_end_qaddr(end); + uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); + uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1); + uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); + uint32_t qentries = 1 << (qsize + 10); + + uint32_t nvp_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6); + uint32_t nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6); + uint8_t priority = xive_get_field32(END2_W7_F0_PRIORITY, end->w7); + uint8_t pq; + + if (!xive2_end_is_valid(end)) { + return; + } + + pq = xive_get_field32(END2_W1_ESn, end->w1); + + monitor_printf(mon, + " %08x %c%c %c%c%c%c%c%c%c%c%c%c prio:%d nvp:%02x/%04x", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive2_end_is_valid(end) ? 'v' : '-', + xive2_end_is_enqueue(end) ? 'q' : '-', + xive2_end_is_notify(end) ? 'n' : '-', + xive2_end_is_backlog(end) ? 'b' : '-', + xive2_end_is_escalate(end) ? 'e' : '-', + xive2_end_is_escalate_end(end) ? 'N' : '-', + xive2_end_is_uncond_escalation(end) ? 'u' : '-', + xive2_end_is_silent_escalation(end) ? 's' : '-', + xive2_end_is_firmware1(end) ? 'f' : '-', + xive2_end_is_firmware2(end) ? 'F' : '-', + priority, nvp_blk, nvp_idx); + + if (qaddr_base) { + monitor_printf(mon, " eq:@%08"PRIx64"% 6d/%5d ^%d", + qaddr_base, qindex, qentries, qgen); + xive2_end_queue_pic_print_info(end, 6, mon); + } + monitor_printf(mon, "\n"); +} + +void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, + Monitor *mon) +{ + Xive2Eas *eas = (Xive2Eas *) &end->w4; + uint8_t pq; + + if (!xive2_end_is_escalate(end)) { + return; + } + + pq = xive_get_field32(END2_W1_ESe, end->w1); + + monitor_printf(mon, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive2_eas_is_valid(eas) ? 'v' : ' ', + xive2_eas_is_masked(eas) ? 'M' : ' ', + (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); +} + +static void xive2_end_enqueue(Xive2End *end, uint32_t data) +{ + uint64_t qaddr_base = xive2_end_qaddr(end); + uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); + uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); + uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1); + + uint64_t qaddr = qaddr_base + (qindex << 2); + uint32_t qdata = cpu_to_be32((qgen << 31) | (data & 0x7fffffff)); + uint32_t qentries = 1 << (qsize + 10); + + if (dma_memory_write(&address_space_memory, qaddr, &qdata, sizeof(qdata), + MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to write END data @0x%" + HWADDR_PRIx "\n", qaddr); + return; + } + + qindex = (qindex + 1) & (qentries - 1); + if (qindex == 0) { + qgen ^= 1; + end->w1 = xive_set_field32(END2_W1_GENERATION, end->w1, qgen); + + /* TODO(PowerNV): reset GF bit on a cache watch operation */ + end->w1 = xive_set_field32(END2_W1_GEN_FLIPPED, end->w1, qgen); + } + end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex); +} + +/* + * XIVE Thread Interrupt Management Area (TIMA) - Gen2 mode + * + * TIMA Gen2 VP “save & restore” (S&R) indicated by H bit next to V bit + * + * - if a context is enabled with the H bit set, the VP context + * information is retrieved from the NVP structure (“check out”) + * and stored back on a context pull (“check in”), the SW receives + * the same context pull information as on P9 + * + * - the H bit cannot be changed while the V bit is set, i.e. a + * context cannot be set up in the TIMA and then be “pushed” into + * the NVP by changing the H bit while the context is enabled + */ + +static void xive2_tctx_save_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t nvp_blk, uint32_t nvp_idx) +{ + CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; + uint32_t pir = env->spr_cb[SPR_PIR].default_value; + Xive2Nvp nvp; + uint8_t *regs = &tctx->regs[TM_QW1_OS]; + + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_hw(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is not HW owned\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_co(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is not checkout\n", + nvp_blk, nvp_idx); + return; + } + + if (xive_get_field32(NVP2_W1_CO_THRID_VALID, nvp.w1) && + xive_get_field32(NVP2_W1_CO_THRID, nvp.w1) != pir) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: NVP %x/%x invalid checkout Thread %x\n", + nvp_blk, nvp_idx, pir); + return; + } + + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, regs[TM_IPB]); + nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, regs[TM_CPPR]); + nvp.w2 = xive_set_field32(NVP2_W2_LSMFB, nvp.w2, regs[TM_LSMFB]); + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + + nvp.w1 = xive_set_field32(NVP2_W1_CO, nvp.w1, 0); + /* NVP2_W1_CO_THRID_VALID only set once */ + nvp.w1 = xive_set_field32(NVP2_W1_CO_THRID, nvp.w1, 0xFFFF); + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 1); +} + +static void xive2_os_cam_decode(uint32_t cam, uint8_t *nvp_blk, + uint32_t *nvp_idx, bool *vo, bool *ho) +{ + *nvp_blk = xive2_nvp_blk(cam); + *nvp_idx = xive2_nvp_idx(cam); + *vo = !!(cam & TM2_QW1W2_VO); + *ho = !!(cam & TM2_QW1W2_HO); +} + +uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); + uint32_t qw1w2_new; + uint32_t cam = be32_to_cpu(qw1w2); + uint8_t nvp_blk; + uint32_t nvp_idx; + bool vo; + bool do_save; + + xive2_os_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_save); + + if (!vo) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n", + nvp_blk, nvp_idx); + } + + /* Invalidate CAM line */ + qw1w2_new = xive_set_field32(TM2_QW1W2_VO, qw1w2, 0); + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2_new, 4); + + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { + xive2_tctx_save_os_ctx(xrtr, tctx, nvp_blk, nvp_idx); + } + + return qw1w2; +} + +static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp) +{ + CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; + uint32_t pir = env->spr_cb[SPR_PIR].default_value; + uint8_t cppr; + + if (!xive2_nvp_is_hw(nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is not HW owned\n", + nvp_blk, nvp_idx); + return 0; + } + + cppr = xive_get_field32(NVP2_W2_CPPR, nvp->w2); + nvp->w2 = xive_set_field32(NVP2_W2_CPPR, nvp->w2, 0); + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 2); + + tctx->regs[TM_QW1_OS + TM_CPPR] = cppr; + /* we don't model LSMFB */ + + nvp->w1 = xive_set_field32(NVP2_W1_CO, nvp->w1, 1); + nvp->w1 = xive_set_field32(NVP2_W1_CO_THRID_VALID, nvp->w1, 1); + nvp->w1 = xive_set_field32(NVP2_W1_CO_THRID, nvp->w1, pir); + + /* + * Checkout privilege: 0:OS, 1:Pool, 2:Hard + * + * TODO: we only support OS push/pull + */ + nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 0); + + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 1); + + /* return restored CPPR to generate a CPU exception if needed */ + return cppr; +} + +static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t nvp_blk, uint32_t nvp_idx, + bool do_restore) +{ + Xive2Nvp nvp; + uint8_t ipb; + uint8_t cppr = 0; + + /* + * Grab the associated thread interrupt context registers in the + * associated NVP + */ + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + /* Automatically restore thread context registers */ + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && + do_restore) { + cppr = xive2_tctx_restore_os_ctx(xrtr, tctx, nvp_blk, nvp_idx, &nvp); + } + + ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); + if (ipb) { + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, 0); + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + } + + /* An IPB or CPPR change can trigger a resend */ + if (ipb || cppr) { + xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb); + } +} + +/* + * Updating the OS CAM line can trigger a resend of interrupt + */ +void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + uint32_t cam = value; + uint32_t qw1w2 = cpu_to_be32(cam); + uint8_t nvp_blk; + uint32_t nvp_idx; + bool vo; + bool do_restore; + + xive2_os_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_restore); + + /* First update the thead context */ + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); + + /* Check the interrupt pending bits */ + if (vo) { + xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, nvp_blk, nvp_idx, + do_restore); + } +} + +/* + * XIVE Router (aka. Virtualization Controller or IVRE) + */ + +int xive2_router_get_eas(Xive2Router *xrtr, uint8_t eas_blk, uint32_t eas_idx, + Xive2Eas *eas) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->get_eas(xrtr, eas_blk, eas_idx, eas); +} + +static +int xive2_router_get_pq(Xive2Router *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->get_pq(xrtr, eas_blk, eas_idx, pq); +} + +static +int xive2_router_set_pq(Xive2Router *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->set_pq(xrtr, eas_blk, eas_idx, pq); +} + +int xive2_router_get_end(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, + Xive2End *end) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->get_end(xrtr, end_blk, end_idx, end); +} + +int xive2_router_write_end(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, + Xive2End *end, uint8_t word_number) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->write_end(xrtr, end_blk, end_idx, end, word_number); +} + +int xive2_router_get_nvp(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->get_nvp(xrtr, nvp_blk, nvp_idx, nvp); +} + +int xive2_router_write_nvp(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp, uint8_t word_number) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->write_nvp(xrtr, nvp_blk, nvp_idx, nvp, word_number); +} + +static int xive2_router_get_block_id(Xive2Router *xrtr) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->get_block_id(xrtr); +} + +/* + * Encode the HW CAM line with 7bit or 8bit thread id. The thread id + * width and block id width is configurable at the IC level. + * + * chipid << 24 | 0000 0000 0000 0000 1 threadid (7Bit) + * chipid << 24 | 0000 0000 0000 0001 threadid (8Bit) + */ +static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; + uint32_t pir = env->spr_cb[SPR_PIR].default_value; + uint8_t blk = xive2_router_get_block_id(xrtr); + uint8_t tid_shift = + xive2_router_get_config(xrtr) & XIVE2_THREADID_8BITS ? 8 : 7; + uint8_t tid_mask = (1 << tid_shift) - 1; + + return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask)); +} + +/* + * The thread context register words are in big-endian format. + */ +int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, + uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint32_t logic_serv) +{ + uint32_t cam = xive2_nvp_cam_line(nvt_blk, nvt_idx); + uint32_t qw3w2 = xive_tctx_word2(&tctx->regs[TM_QW3_HV_PHYS]); + uint32_t qw2w2 = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]); + uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); + uint32_t qw0w2 = xive_tctx_word2(&tctx->regs[TM_QW0_USER]); + + /* + * TODO (PowerNV): ignore mode. The low order bits of the NVT + * identifier are ignored in the "CAM" match. + */ + + if (format == 0) { + if (cam_ignore == true) { + /* + * F=0 & i=1: Logical server notification (bits ignored at + * the end of the NVT identifier) + */ + qemu_log_mask(LOG_UNIMP, "XIVE: no support for LS NVT %x/%x\n", + nvt_blk, nvt_idx); + return -1; + } + + /* F=0 & i=0: Specific NVT notification */ + + /* PHYS ring */ + if ((be32_to_cpu(qw3w2) & TM2_QW3W2_VT) && + cam == xive2_tctx_hw_cam_line(xptr, tctx)) { + return TM_QW3_HV_PHYS; + } + + /* HV POOL ring */ + if ((be32_to_cpu(qw2w2) & TM2_QW2W2_VP) && + cam == xive_get_field32(TM2_QW2W2_POOL_CAM, qw2w2)) { + return TM_QW2_HV_POOL; + } + + /* OS ring */ + if ((be32_to_cpu(qw1w2) & TM2_QW1W2_VO) && + cam == xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2)) { + return TM_QW1_OS; + } + } else { + /* F=1 : User level Event-Based Branch (EBB) notification */ + + /* USER ring */ + if ((be32_to_cpu(qw1w2) & TM2_QW1W2_VO) && + (cam == xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2)) && + (be32_to_cpu(qw0w2) & TM2_QW0W2_VU) && + (logic_serv == xive_get_field32(TM2_QW0W2_LOGIC_SERV, qw0w2))) { + return TM_QW0_USER; + } + } + return -1; +} + +static void xive2_router_realize(DeviceState *dev, Error **errp) +{ + Xive2Router *xrtr = XIVE2_ROUTER(dev); + + assert(xrtr->xfb); +} + +/* + * Notification using the END ESe/ESn bit (Event State Buffer for + * escalation and notification). Profide futher coalescing in the + * Router. + */ +static bool xive2_router_end_es_notify(Xive2Router *xrtr, uint8_t end_blk, + uint32_t end_idx, Xive2End *end, + uint32_t end_esmask) +{ + uint8_t pq = xive_get_field32(end_esmask, end->w1); + bool notify = xive_esb_trigger(&pq); + + if (pq != xive_get_field32(end_esmask, end->w1)) { + end->w1 = xive_set_field32(end_esmask, end->w1, pq); + xive2_router_write_end(xrtr, end_blk, end_idx, end, 1); + } + + /* ESe/n[Q]=1 : end of notification */ + return notify; +} + +/* + * An END trigger can come from an event trigger (IPI or HW) or from + * another chip. We don't model the PowerBus but the END trigger + * message has the same parameters than in the function below. + */ +static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, + uint32_t end_idx, uint32_t end_data) +{ + Xive2End end; + uint8_t priority; + uint8_t format; + bool found; + Xive2Nvp nvp; + uint8_t nvp_blk; + uint32_t nvp_idx; + + /* END cache lookup */ + if (xive2_router_get_end(xrtr, end_blk, end_idx, &end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk, + end_idx); + return; + } + + if (!xive2_end_is_valid(&end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: END %x/%x is invalid\n", + end_blk, end_idx); + return; + } + + if (xive2_end_is_enqueue(&end)) { + xive2_end_enqueue(&end, end_data); + /* Enqueuing event data modifies the EQ toggle and index */ + xive2_router_write_end(xrtr, end_blk, end_idx, &end, 1); + } + + /* + * When the END is silent, we skip the notification part. + */ + if (xive2_end_is_silent_escalation(&end)) { + goto do_escalation; + } + + /* + * The W7 format depends on the F bit in W6. It defines the type + * of the notification : + * + * F=0 : single or multiple NVP notification + * F=1 : User level Event-Based Branch (EBB) notification, no + * priority + */ + format = xive_get_field32(END2_W6_FORMAT_BIT, end.w6); + priority = xive_get_field32(END2_W7_F0_PRIORITY, end.w7); + + /* The END is masked */ + if (format == 0 && priority == 0xff) { + return; + } + + /* + * Check the END ESn (Event State Buffer for notification) for + * even futher coalescing in the Router + */ + if (!xive2_end_is_notify(&end)) { + /* ESn[Q]=1 : end of notification */ + if (!xive2_router_end_es_notify(xrtr, end_blk, end_idx, + &end, END2_W1_ESn)) { + return; + } + } + + /* + * Follows IVPE notification + */ + nvp_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); + nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); + + /* NVP cache lookup */ + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is invalid\n", + nvp_blk, nvp_idx); + return; + } + + found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, + xive_get_field32(END2_W6_IGNORE, end.w7), + priority, + xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7)); + + /* TODO: Auto EOI. */ + + if (found) { + return; + } + + /* + * If no matching NVP is dispatched on a HW thread : + * - specific VP: update the NVP structure if backlog is activated + * - logical server : forward request to IVPE (not supported) + */ + if (xive2_end_is_backlog(&end)) { + uint8_t ipb; + + if (format == 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: END %x/%x invalid config: F1 & backlog\n", + end_blk, end_idx); + return; + } + + /* + * Record the IPB in the associated NVP structure for later + * use. The presenter will resend the interrupt when the vCPU + * is dispatched again on a HW thread. + */ + ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2) | + xive_priority_to_ipb(priority); + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + + /* + * On HW, follows a "Broadcast Backlog" to IVPEs + */ + } + +do_escalation: + /* + * If activated, escalate notification using the ESe PQ bits and + * the EAS in w4-5 + */ + if (!xive2_end_is_escalate(&end)) { + return; + } + + /* + * Check the END ESe (Event State Buffer for escalation) for even + * futher coalescing in the Router + */ + if (!xive2_end_is_uncond_escalation(&end)) { + /* ESe[Q]=1 : end of escalation notification */ + if (!xive2_router_end_es_notify(xrtr, end_blk, end_idx, + &end, END2_W1_ESe)) { + return; + } + } + + /* + * The END trigger becomes an Escalation trigger + */ + xive2_router_end_notify(xrtr, + xive_get_field32(END2_W4_END_BLOCK, end.w4), + xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), + xive_get_field32(END2_W5_ESC_END_DATA, end.w5)); +} + +void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xn); + uint8_t eas_blk = XIVE_EAS_BLOCK(lisn); + uint32_t eas_idx = XIVE_EAS_INDEX(lisn); + Xive2Eas eas; + + /* EAS cache lookup */ + if (xive2_router_get_eas(xrtr, eas_blk, eas_idx, &eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN %x\n", lisn); + return; + } + + if (!pq_checked) { + bool notify; + uint8_t pq; + + /* PQ cache lookup */ + if (xive2_router_get_pq(xrtr, eas_blk, eas_idx, &pq)) { + /* Set FIR */ + g_assert_not_reached(); + } + + notify = xive_esb_trigger(&pq); + + if (xive2_router_set_pq(xrtr, eas_blk, eas_idx, &pq)) { + /* Set FIR */ + g_assert_not_reached(); + } + + if (!notify) { + return; + } + } + + if (!xive2_eas_is_valid(&eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid LISN %x\n", lisn); + return; + } + + if (xive2_eas_is_masked(&eas)) { + /* Notification completed */ + return; + } + + /* + * The event trigger becomes an END trigger + */ + xive2_router_end_notify(xrtr, + xive_get_field64(EAS2_END_BLOCK, eas.w), + xive_get_field64(EAS2_END_INDEX, eas.w), + xive_get_field64(EAS2_END_DATA, eas.w)); +} + +static Property xive2_router_properties[] = { + DEFINE_PROP_LINK("xive-fabric", Xive2Router, xfb, + TYPE_XIVE_FABRIC, XiveFabric *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xive2_router_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); + + dc->desc = "XIVE2 Router Engine"; + device_class_set_props(dc, xive2_router_properties); + /* Parent is SysBusDeviceClass. No need to call its realize hook */ + dc->realize = xive2_router_realize; + xnc->notify = xive2_router_notify; +} + +static const TypeInfo xive2_router_info = { + .name = TYPE_XIVE2_ROUTER, + .parent = TYPE_SYS_BUS_DEVICE, + .abstract = true, + .instance_size = sizeof(Xive2Router), + .class_size = sizeof(Xive2RouterClass), + .class_init = xive2_router_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_NOTIFIER }, + { TYPE_XIVE_PRESENTER }, + { } + } +}; + +static inline bool addr_is_even(hwaddr addr, uint32_t shift) +{ + return !((addr >> shift) & 1); +} + +static uint64_t xive2_end_source_read(void *opaque, hwaddr addr, unsigned size) +{ + Xive2EndSource *xsrc = XIVE2_END_SOURCE(opaque); + uint32_t offset = addr & 0xFFF; + uint8_t end_blk; + uint32_t end_idx; + Xive2End end; + uint32_t end_esmask; + uint8_t pq; + uint64_t ret; + + /* + * The block id should be deduced from the load address on the END + * ESB MMIO but our model only supports a single block per XIVE chip. + */ + end_blk = xive2_router_get_block_id(xsrc->xrtr); + end_idx = addr >> (xsrc->esb_shift + 1); + + if (xive2_router_get_end(xsrc->xrtr, end_blk, end_idx, &end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk, + end_idx); + return -1; + } + + if (!xive2_end_is_valid(&end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: END %x/%x is invalid\n", + end_blk, end_idx); + return -1; + } + + end_esmask = addr_is_even(addr, xsrc->esb_shift) ? END2_W1_ESn : + END2_W1_ESe; + pq = xive_get_field32(end_esmask, end.w1); + + switch (offset) { + case XIVE_ESB_LOAD_EOI ... XIVE_ESB_LOAD_EOI + 0x7FF: + ret = xive_esb_eoi(&pq); + + /* Forward the source event notification for routing ?? */ + break; + + case XIVE_ESB_GET ... XIVE_ESB_GET + 0x3FF: + ret = pq; + break; + + case XIVE_ESB_SET_PQ_00 ... XIVE_ESB_SET_PQ_00 + 0x0FF: + case XIVE_ESB_SET_PQ_01 ... XIVE_ESB_SET_PQ_01 + 0x0FF: + case XIVE_ESB_SET_PQ_10 ... XIVE_ESB_SET_PQ_10 + 0x0FF: + case XIVE_ESB_SET_PQ_11 ... XIVE_ESB_SET_PQ_11 + 0x0FF: + ret = xive_esb_set(&pq, (offset >> 8) & 0x3); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid END ESB load addr %d\n", + offset); + return -1; + } + + if (pq != xive_get_field32(end_esmask, end.w1)) { + end.w1 = xive_set_field32(end_esmask, end.w1, pq); + xive2_router_write_end(xsrc->xrtr, end_blk, end_idx, &end, 1); + } + + return ret; +} + +static void xive2_end_source_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + Xive2EndSource *xsrc = XIVE2_END_SOURCE(opaque); + uint32_t offset = addr & 0xFFF; + uint8_t end_blk; + uint32_t end_idx; + Xive2End end; + uint32_t end_esmask; + uint8_t pq; + bool notify = false; + + /* + * The block id should be deduced from the load address on the END + * ESB MMIO but our model only supports a single block per XIVE chip. + */ + end_blk = xive2_router_get_block_id(xsrc->xrtr); + end_idx = addr >> (xsrc->esb_shift + 1); + + if (xive2_router_get_end(xsrc->xrtr, end_blk, end_idx, &end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk, + end_idx); + return; + } + + if (!xive2_end_is_valid(&end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: END %x/%x is invalid\n", + end_blk, end_idx); + return; + } + + end_esmask = addr_is_even(addr, xsrc->esb_shift) ? END2_W1_ESn : + END2_W1_ESe; + pq = xive_get_field32(end_esmask, end.w1); + + switch (offset) { + case 0 ... 0x3FF: + notify = xive_esb_trigger(&pq); + break; + + case XIVE_ESB_STORE_EOI ... XIVE_ESB_STORE_EOI + 0x3FF: + /* TODO: can we check StoreEOI availability from the router ? */ + notify = xive_esb_eoi(&pq); + break; + + case XIVE_ESB_INJECT ... XIVE_ESB_INJECT + 0x3FF: + if (end_esmask == END2_W1_ESe) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: END %x/%x can not EQ inject on ESe\n", + end_blk, end_idx); + return; + } + notify = true; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid END ESB write addr %d\n", + offset); + return; + } + + if (pq != xive_get_field32(end_esmask, end.w1)) { + end.w1 = xive_set_field32(end_esmask, end.w1, pq); + xive2_router_write_end(xsrc->xrtr, end_blk, end_idx, &end, 1); + } + + /* TODO: Forward the source event notification for routing */ + if (notify) { + ; + } +} + +static const MemoryRegionOps xive2_end_source_ops = { + .read = xive2_end_source_read, + .write = xive2_end_source_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static void xive2_end_source_realize(DeviceState *dev, Error **errp) +{ + Xive2EndSource *xsrc = XIVE2_END_SOURCE(dev); + + assert(xsrc->xrtr); + + if (!xsrc->nr_ends) { + error_setg(errp, "Number of interrupt needs to be greater than 0"); + return; + } + + if (xsrc->esb_shift != XIVE_ESB_4K && + xsrc->esb_shift != XIVE_ESB_64K) { + error_setg(errp, "Invalid ESB shift setting"); + return; + } + + /* + * Each END is assigned an even/odd pair of MMIO pages, the even page + * manages the ESn field while the odd page manages the ESe field. + */ + memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc), + &xive2_end_source_ops, xsrc, "xive.end", + (1ull << (xsrc->esb_shift + 1)) * xsrc->nr_ends); +} + +static Property xive2_end_source_properties[] = { + DEFINE_PROP_UINT32("nr-ends", Xive2EndSource, nr_ends, 0), + DEFINE_PROP_UINT32("shift", Xive2EndSource, esb_shift, XIVE_ESB_64K), + DEFINE_PROP_LINK("xive", Xive2EndSource, xrtr, TYPE_XIVE2_ROUTER, + Xive2Router *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xive2_end_source_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "XIVE END Source"; + device_class_set_props(dc, xive2_end_source_properties); + dc->realize = xive2_end_source_realize; + dc->user_creatable = false; +} + +static const TypeInfo xive2_end_source_info = { + .name = TYPE_XIVE2_END_SOURCE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(Xive2EndSource), + .class_init = xive2_end_source_class_init, +}; + +static void xive2_register_types(void) +{ + type_register_static(&xive2_router_info); + type_register_static(&xive2_end_source_info); +} + +type_init(xive2_register_types) diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 02625eb94e..88aa734e9e 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -92,7 +92,7 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) } if (iib->isairq > 0) { - isa_init_irq(isadev, &iib->irq, iib->isairq); + iib->irq = isa_get_irq(isadev, iib->isairq); iib->bt.use_irq = 1; iib->bt.raise_irq = isa_ipmi_bt_raise_irq; iib->bt.lower_irq = isa_ipmi_bt_lower_irq; diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 3b23ad08b3..afabb95ebe 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -91,7 +91,7 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) } if (iik->isairq > 0) { - isa_init_irq(isadev, &iik->irq, iik->isairq); + iik->irq = isa_get_irq(isadev, iik->isairq); iik->kcs.use_irq = 1; iik->kcs.raise_irq = isa_ipmi_kcs_raise_irq; iik->kcs.lower_irq = isa_ipmi_kcs_lower_irq; diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 6c31398dda..0ad1c5fd65 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -21,21 +21,18 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" -#include "monitor/monitor.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/isa/isa.h" static ISABus *isabus; -static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *isabus_get_fw_dev_path(DeviceState *dev); static void isa_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); - k->print_dev = isabus_dev_print; k->get_fw_dev_path = isabus_get_fw_dev_path; } @@ -88,19 +85,9 @@ qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq) return isabus->irqs[isairq]; } -void isa_init_irq(ISADevice *dev, qemu_irq *p, unsigned isairq) -{ - assert(dev->nirqs < ARRAY_SIZE(dev->isairq)); - assert(isairq < ISA_NUM_IRQS); - dev->isairq[dev->nirqs] = isairq; - *p = isa_get_irq(dev, isairq); - dev->nirqs++; -} - void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq) { - qemu_irq irq; - isa_init_irq(isadev, &irq, isairq); + qemu_irq irq = isa_get_irq(isadev, isairq); qdev_connect_gpio_out(DEVICE(isadev), gpioirq, irq); } @@ -153,14 +140,6 @@ int isa_register_portio_list(ISADevice *dev, return 0; } -static void isa_device_init(Object *obj) -{ - ISADevice *dev = ISA_DEVICE(obj); - - dev->isairq[0] = -1; - dev->isairq[1] = -1; -} - ISADevice *isa_new(const char *name) { return ISA_DEVICE(qdev_new(name)); @@ -222,19 +201,6 @@ void isa_build_aml(ISABus *bus, Aml *scope) } } -static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent) -{ - ISADevice *d = ISA_DEVICE(dev); - - if (d->isairq[1] != -1) { - monitor_printf(mon, "%*sisa irqs %d,%d\n", indent, "", - d->isairq[0], d->isairq[1]); - } else if (d->isairq[0] != -1) { - monitor_printf(mon, "%*sisa irq %d\n", indent, "", - d->isairq[0]); - } -} - static void isabus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -260,7 +226,6 @@ static const TypeInfo isa_device_type_info = { .name = TYPE_ISA_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(ISADevice), - .instance_init = isa_device_init, .abstract = true, .class_size = sizeof(ISADeviceClass), .class_init = isa_device_class_init, diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index 0fe7b69bc4..8607e0ac36 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -39,8 +39,6 @@ #include "sysemu/runstate.h" #include "qom/object.h" -PCIDevice *piix4_dev; - struct PIIX4State { PCIDevice dev; qemu_irq cpu_intr; @@ -54,6 +52,27 @@ struct PIIX4State { OBJECT_DECLARE_SIMPLE_TYPE(PIIX4State, PIIX4_PCI_DEVICE) +static void piix4_set_irq(void *opaque, int irq_num, int level) +{ + int i, pic_irq, pic_level; + PIIX4State *s = opaque; + PCIBus *bus = pci_get_bus(&s->dev); + + /* now we change the pic irq level according to the piix irq mappings */ + /* XXX: optimize */ + pic_irq = s->dev.config[PIIX_PIRQCA + irq_num]; + if (pic_irq < ISA_NUM_IRQS) { + /* The pic level is the logical OR of all the PCI irqs mapped to it. */ + pic_level = 0; + for (i = 0; i < PIIX_NUM_PIRQS; i++) { + if (pic_irq == s->dev.config[PIIX_PIRQCA + i]) { + pic_level |= pci_bus_get_irq_level(bus, i); + } + } + qemu_set_irq(s->isa[pic_irq], pic_level); + } +} + static void piix4_isa_reset(DeviceState *dev) { PIIX4State *d = PIIX4_PCI_DEVICE(dev); @@ -197,9 +216,7 @@ static void piix4_realize(PCIDevice *dev, Error **errp) if (!qdev_realize(DEVICE(&s->rtc), BUS(isa_bus), errp)) { return; } - isa_init_irq(ISA_DEVICE(&s->rtc), &s->rtc.irq, RTC_ISA_IRQ); - - piix4_dev = dev; + s->rtc.irq = isa_get_irq(ISA_DEVICE(&s->rtc), s->rtc.isairq); } static void piix4_init(Object *obj) @@ -248,8 +265,34 @@ static void piix4_register_types(void) type_init(piix4_register_types) +static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +{ + int slot; + + slot = PCI_SLOT(pci_dev->devfn); + + switch (slot) { + /* PIIX4 USB */ + case 10: + return 3; + /* AMD 79C973 Ethernet */ + case 11: + return 1; + /* Crystal 4281 Sound */ + case 12: + return 2; + /* PCI slot 1 to 4 */ + case 18 ... 21: + return ((slot - 18) + irq_num) & 0x03; + /* Unknown device, don't do any translation */ + default: + return irq_num; + } +} + DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus) { + PIIX4State *s; PCIDevice *pci; DeviceState *dev; int devfn = PCI_DEVFN(10, 0); @@ -257,6 +300,7 @@ DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus) pci = pci_create_simple_multifunction(pci_bus, devfn, true, TYPE_PIIX4_PCI_DEVICE); dev = DEVICE(pci); + s = PIIX4_PCI_DEVICE(pci); if (isa_bus) { *isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); } @@ -271,5 +315,7 @@ DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus) NULL, 0, NULL); } + pci_bus_irqs(pci_bus, piix4_set_irq, pci_slot_get_pirq, s, PIIX_NUM_PIRQS); + return dev; } diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 93812ee206..655207e393 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -11,7 +11,6 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "cpu.h" #include "hw/irq.h" diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 55dfe5036f..099a758c6f 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -22,7 +22,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "sysemu/sysemu.h" #include "cpu.h" @@ -533,10 +532,11 @@ static void q800_init(MachineState *machine) sysbus = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(via2_dev, - VIA2_IRQ_SCSI_BIT)); - sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(via2_dev, - VIA2_IRQ_SCSI_DATA_BIT)); + /* SCSI and SCSI data IRQs are negative edge triggered */ + sysbus_connect_irq(sysbus, 0, qemu_irq_invert(qdev_get_gpio_in(via2_dev, + VIA2_IRQ_SCSI_BIT))); + sysbus_connect_irq(sysbus, 1, qemu_irq_invert(qdev_get_gpio_in(via2_dev, + VIA2_IRQ_SCSI_DATA_BIT))); sysbus_mmio_map(sysbus, 0, ESP_BASE); sysbus_mmio_map(sysbus, 1, ESP_PDMA); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index bbaf630bbf..e215aa3d42 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu-common.h" #include "sysemu/sysemu.h" #include "cpu.h" #include "hw/boards.h" @@ -132,7 +131,7 @@ static void virt_init(MachineState *machine) exit(1); } - reset_info = g_malloc0(sizeof(ResetInfo)); + reset_info = g_new0(ResetInfo, 1); /* init CPUs */ cpu = M68K_CPU(cpu_create(machine->cpu_type)); @@ -316,10 +315,17 @@ type_init(virt_machine_register_types) } \ type_init(machvirt_machine_##major##_##minor##_init); -static void virt_machine_7_0_options(MachineClass *mc) +static void virt_machine_7_1_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE(7, 0, true) +DEFINE_VIRT_MACHINE(7, 1, true) + +static void virt_machine_7_0_options(MachineClass *mc) +{ + virt_machine_7_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); +} +DEFINE_VIRT_MACHINE(7, 0, false) static void virt_machine_6_2_options(MachineClass *mc) { diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 8821d009f1..8b92a9801a 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -25,7 +25,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "cpu.h" #include "qemu/option.h" @@ -138,7 +137,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, uint32_t base32; int big_endian = 0; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN big_endian = 1; #endif diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index c1b8066a13..7b13098f9b 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -19,7 +19,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" @@ -72,7 +71,7 @@ static struct _loaderparams { const char *initrd_filename; } loaderparams; -static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index, +static void G_GNUC_PRINTF(3, 4) prom_set(uint32_t *prom_buf, int index, const char *string, ...) { va_list ap; diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c index c7480bd019..19d0d9889f 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/mips/gt64xxx_pci.c @@ -26,10 +26,8 @@ #include "qapi/error.h" #include "qemu/units.h" #include "qemu/log.h" -#include "hw/mips/mips.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" -#include "hw/southbridge/piix.h" #include "migration/vmstate.h" #include "hw/intc/i8259.h" #include "hw/irq.h" @@ -981,56 +979,6 @@ static const MemoryRegionOps isd_mem_ops = { }, }; -static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int slot; - - slot = PCI_SLOT(pci_dev->devfn); - - switch (slot) { - /* PIIX4 USB */ - case 10: - return 3; - /* AMD 79C973 Ethernet */ - case 11: - return 1; - /* Crystal 4281 Sound */ - case 12: - return 2; - /* PCI slot 1 to 4 */ - case 18 ... 21: - return ((slot - 18) + irq_num) & 0x03; - /* Unknown device, don't do any translation */ - default: - return irq_num; - } -} - -static int pci_irq_levels[4]; - -static void gt64120_pci_set_irq(void *opaque, int irq_num, int level) -{ - int i, pic_irq, pic_level; - qemu_irq *pic = opaque; - - pci_irq_levels[irq_num] = level; - - /* now we change the pic irq level according to the piix irq mappings */ - /* XXX: optimize */ - pic_irq = piix4_dev->config[PIIX_PIRQCA + irq_num]; - if (pic_irq < 16) { - /* The pic level is the logical OR of all the PCI irqs mapped to it. */ - pic_level = 0; - for (i = 0; i < 4; i++) { - if (pic_irq == piix4_dev->config[PIIX_PIRQCA + i]) { - pic_level |= pci_irq_levels[i]; - } - } - qemu_set_irq(pic[pic_irq], pic_level); - } -} - - static void gt64120_reset(DeviceState *dev) { GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); @@ -1038,7 +986,7 @@ static void gt64120_reset(DeviceState *dev) /* FIXME: Malta specific hw assumptions ahead */ /* CPU Configuration */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN s->regs[GT_CPU] = 0x00000000; #else s->regs[GT_CPU] = 0x00001000; @@ -1149,7 +1097,7 @@ static void gt64120_reset(DeviceState *dev) s->regs[GT_TC_CONTROL] = 0x00000000; /* PCI Internal */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN s->regs[GT_PCI0_CMD] = 0x00000000; #else s->regs[GT_PCI0_CMD] = 0x00010001; @@ -1170,7 +1118,7 @@ static void gt64120_reset(DeviceState *dev) s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000; s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000; s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN s->regs[GT_PCI1_CMD] = 0x00000000; #else s->regs[GT_PCI1_CMD] = 0x00010001; @@ -1202,32 +1150,18 @@ static void gt64120_reset(DeviceState *dev) static void gt64120_realize(DeviceState *dev, Error **errp) { GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(dev); memory_region_init_io(&s->ISD_mem, OBJECT(dev), &isd_mem_ops, s, "gt64120-isd", 0x1000); -} - -PCIBus *gt64120_register(qemu_irq *pic) -{ - GT64120State *d; - PCIHostState *phb; - DeviceState *dev; - - dev = qdev_new(TYPE_GT64120_PCI_HOST_BRIDGE); - d = GT64120_PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(dev); - memory_region_init(&d->pci0_mem, OBJECT(dev), "pci0-mem", 4 * GiB); - address_space_init(&d->pci0_mem_as, &d->pci0_mem, "pci0-mem"); - phb->bus = pci_register_root_bus(dev, "pci", - gt64120_pci_set_irq, gt64120_pci_map_irq, - pic, - &d->pci0_mem, - get_system_io(), - PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_init(&s->pci0_mem, OBJECT(dev), "pci0-mem", 4 * GiB); + address_space_init(&s->pci0_mem_as, &s->pci0_mem, "pci0-mem"); + phb->bus = pci_root_bus_new(dev, "pci", + &s->pci0_mem, + get_system_io(), + PCI_DEVFN(18, 0), TYPE_PCI_BUS); pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); - return phb->bus; } static void gt64120_pci_realize(PCIDevice *d, Error **errp) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 44f0d48bfd..6598d7dddd 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "hw/clock.h" #include "hw/mips/mips.h" @@ -158,7 +157,7 @@ static void mips_jazz_init(MachineState *machine, [JAZZ_PICA61] = {33333333, 4}, }; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN big_endian = 1; #else big_endian = 0; diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index ae192db0c8..25534288dd 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qemu/datadir.h" diff --git a/hw/mips/malta.c b/hw/mips/malta.c index b770b8d367..9ffdc5b8f1 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/bitops.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "hw/clock.h" #include "hw/southbridge/piix.h" @@ -97,7 +96,6 @@ struct MaltaState { Clock *cpuclk; MIPSCPSState cps; - qemu_irq i8259[ISA_NUM_IRQS]; }; static struct _loaderparams { @@ -368,7 +366,7 @@ static uint64_t malta_fpga_read(void *opaque, hwaddr addr, /* STATUS Register */ case 0x00208: -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN val = 0x00000012; #else val = 0x00000010; @@ -696,7 +694,7 @@ static void write_bootloader_nanomips(uint8_t *base, uint64_t run_addr, stw_p(p++, 0xe040); stw_p(p++, 0x0681); /* lui t1, %hi(0xb4000000) */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stw_p(p++, 0xe020); stw_p(p++, 0x0be1); /* lui t0, %hi(0xdf000000) */ @@ -895,7 +893,7 @@ static void write_bootloader(uint8_t *base, uint64_t run_addr, /* Load BAR registers as done by YAMON */ stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stl_p(p++, 0x3c08df00); /* lui t0, 0xdf00 */ #else stl_p(p++, 0x340800df); /* ori t0, r0, 0x00df */ @@ -904,39 +902,39 @@ static void write_bootloader(uint8_t *base, uint64_t run_addr, stl_p(p++, 0x3c09bbe0); /* lui t1, 0xbbe0 */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stl_p(p++, 0x3c08c000); /* lui t0, 0xc000 */ #else stl_p(p++, 0x340800c0); /* ori t0, r0, 0x00c0 */ #endif stl_p(p++, 0xad280048); /* sw t0, 0x0048(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stl_p(p++, 0x3c084000); /* lui t0, 0x4000 */ #else stl_p(p++, 0x34080040); /* ori t0, r0, 0x0040 */ #endif stl_p(p++, 0xad280050); /* sw t0, 0x0050(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stl_p(p++, 0x3c088000); /* lui t0, 0x8000 */ #else stl_p(p++, 0x34080080); /* ori t0, r0, 0x0080 */ #endif stl_p(p++, 0xad280058); /* sw t0, 0x0058(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stl_p(p++, 0x3c083f00); /* lui t0, 0x3f00 */ #else stl_p(p++, 0x3408003f); /* ori t0, r0, 0x003f */ #endif stl_p(p++, 0xad280060); /* sw t0, 0x0060(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stl_p(p++, 0x3c08c100); /* lui t0, 0xc100 */ #else stl_p(p++, 0x340800c1); /* ori t0, r0, 0x00c1 */ #endif stl_p(p++, 0xad280080); /* sw t0, 0x0080(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN stl_p(p++, 0x3c085e00); /* lui t0, 0x5e00 */ #else stl_p(p++, 0x3408005e); /* ori t0, r0, 0x005e */ @@ -996,7 +994,7 @@ static void write_bootloader(uint8_t *base, uint64_t run_addr, } -static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index, +static void G_GNUC_PRINTF(3, 4) prom_set(uint32_t *prom_buf, int index, const char *string, ...) { va_list ap; @@ -1031,7 +1029,7 @@ static uint64_t load_kernel(void) int prom_index = 0; uint64_t (*xlate_to_kseg0) (void *opaque, uint64_t addr); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN big_endian = 1; #else big_endian = 0; @@ -1273,7 +1271,7 @@ void mips_malta_init(MachineState *machine) ram_low_postio); } -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN be = 1; #else be = 0; @@ -1354,7 +1352,7 @@ void mips_malta_init(MachineState *machine) * In little endian mode the 32bit words in the bios are swapped, * a neat trick which allows bi-endian firmware. */ -#ifndef TARGET_WORDS_BIGENDIAN +#if !TARGET_BIG_ENDIAN { uint32_t *end, *addr; const size_t swapsize = MIN(bios_size, 0x3e0000); @@ -1391,7 +1389,8 @@ void mips_malta_init(MachineState *machine) stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420); /* Northbridge */ - pci_bus = gt64120_register(s->i8259); + dev = sysbus_create_simple("gt64120", -1, NULL); + pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci")); /* * The whole address space decoded by the GT-64120A doesn't generate * exception when accessing invalid memory. Create an empty slot to @@ -1404,9 +1403,6 @@ void mips_malta_init(MachineState *machine) /* Interrupt controller */ qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq); - for (int i = 0; i < ISA_NUM_IRQS; i++) { - s->i8259[i] = qdev_get_gpio_in_named(dev, "isa", i); - } /* generate SPD EEPROM data */ generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size); diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index 2325e7e05a..39f64448f2 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "hw/clock.h" #include "hw/mips/mips.h" @@ -65,7 +64,7 @@ static uint64_t load_kernel(void) ram_addr_t initrd_offset; int big_endian; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN big_endian = 1; #else big_endian = 0; @@ -162,7 +161,7 @@ mips_mipssim_init(MachineState *machine) cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); env = &cpu->env; - reset_info = g_malloc0(sizeof(ResetData)); + reset_info = g_new0(ResetData, 1); reset_info->cpu = cpu; reset_info->vector = env->active_tc.PC; qemu_register_reset(main_cpu_reset, reset_info); diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 1b9acaf1d3..81cd6b6423 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -253,7 +253,7 @@ static void applesmc_add_key(AppleSMCState *s, const char *key, { struct AppleSMCData *def; - def = g_malloc0(sizeof(struct AppleSMCData)); + def = g_new0(struct AppleSMCData, 1); def->key = key; def->len = len; def->data = data; diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 10f00e65f4..4b5997e18f 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -27,6 +27,7 @@ #define R_HASH_SRC (0x20 / 4) #define R_HASH_DEST (0x24 / 4) +#define R_HASH_KEY_BUFF (0x28 / 4) #define R_HASH_SRC_LEN (0x2c / 4) #define R_HASH_CMD (0x30 / 4) @@ -64,7 +65,6 @@ #define SG_LIST_ADDR_SIZE 4 #define SG_LIST_ADDR_MASK 0x7FFFFFFF #define SG_LIST_ENTRY_SIZE (SG_LIST_LEN_SIZE + SG_LIST_ADDR_SIZE) -#define ASPEED_HACE_MAX_SG 256 /* max number of entries */ static const struct { uint32_t mask; @@ -94,11 +94,104 @@ static int hash_algo_lookup(uint32_t reg) return -1; } -static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode) +/** + * Check whether the request contains padding message. + * + * @param s aspeed hace state object + * @param iov iov of current request + * @param req_len length of the current request + * @param total_msg_len length of all acc_mode requests(excluding padding msg) + * @param pad_offset start offset of padding message + */ +static bool has_padding(AspeedHACEState *s, struct iovec *iov, + hwaddr req_len, uint32_t *total_msg_len, + uint32_t *pad_offset) +{ + *total_msg_len = (uint32_t)(ldq_be_p(iov->iov_base + req_len - 8) / 8); + /* + * SG_LIST_LEN_LAST asserted in the request length doesn't mean it is the + * last request. The last request should contain padding message. + * We check whether message contains padding by + * 1. Get total message length. If the current message contains + * padding, the last 8 bytes are total message length. + * 2. Check whether the total message length is valid. + * If it is valid, the value should less than or equal to + * total_req_len. + * 3. Current request len - padding_size to get padding offset. + * The padding message's first byte should be 0x80 + */ + if (*total_msg_len <= s->total_req_len) { + uint32_t padding_size = s->total_req_len - *total_msg_len; + uint8_t *padding = iov->iov_base; + *pad_offset = req_len - padding_size; + if (padding[*pad_offset] == 0x80) { + return true; + } + } + + return false; +} + +static int reconstruct_iov(AspeedHACEState *s, struct iovec *iov, int id, + uint32_t *pad_offset) +{ + int i, iov_count; + if (*pad_offset != 0) { + s->iov_cache[s->iov_count].iov_base = iov[id].iov_base; + s->iov_cache[s->iov_count].iov_len = *pad_offset; + ++s->iov_count; + } + for (i = 0; i < s->iov_count; i++) { + iov[i].iov_base = s->iov_cache[i].iov_base; + iov[i].iov_len = s->iov_cache[i].iov_len; + } + iov_count = s->iov_count; + s->iov_count = 0; + s->total_req_len = 0; + return iov_count; +} + +/** + * Generate iov for accumulative mode. + * + * @param s aspeed hace state object + * @param iov iov of the current request + * @param id index of the current iov + * @param req_len length of the current request + * + * @return count of iov + */ +static int gen_acc_mode_iov(AspeedHACEState *s, struct iovec *iov, int id, + hwaddr *req_len) +{ + uint32_t pad_offset; + uint32_t total_msg_len; + s->total_req_len += *req_len; + + if (has_padding(s, &iov[id], *req_len, &total_msg_len, &pad_offset)) { + if (s->iov_count) { + return reconstruct_iov(s, iov, id, &pad_offset); + } + + *req_len -= s->total_req_len - total_msg_len; + s->total_req_len = 0; + iov[id].iov_len = *req_len; + } else { + s->iov_cache[s->iov_count].iov_base = iov->iov_base; + s->iov_cache[s->iov_count].iov_len = *req_len; + ++s->iov_count; + } + + return id + 1; +} + +static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, + bool acc_mode) { struct iovec iov[ASPEED_HACE_MAX_SG]; g_autofree uint8_t *digest_buf; size_t digest_len = 0; + int niov = 0; int i; if (sg_mode) { @@ -123,10 +216,16 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode) MEMTXATTRS_UNSPECIFIED, NULL); addr &= SG_LIST_ADDR_MASK; - iov[i].iov_len = len & SG_LIST_LEN_MASK; - plen = iov[i].iov_len; + plen = len & SG_LIST_LEN_MASK; iov[i].iov_base = address_space_map(&s->dram_as, addr, &plen, false, MEMTXATTRS_UNSPECIFIED); + + if (acc_mode) { + niov = gen_acc_mode_iov(s, iov, i, &plen); + + } else { + iov[i].iov_len = plen; + } } } else { hwaddr len = s->regs[R_HASH_SRC_LEN]; @@ -136,6 +235,25 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode) &len, false, MEMTXATTRS_UNSPECIFIED); i = 1; + + if (s->iov_count) { + /* + * In aspeed sdk kernel driver, sg_mode is disabled in hash_final(). + * Thus if we received a request with sg_mode disabled, it is + * required to check whether cache is empty. If no, we should + * combine cached iov and the current iov. + */ + uint32_t total_msg_len; + uint32_t pad_offset; + s->total_req_len += len; + if (has_padding(s, iov, len, &total_msg_len, &pad_offset)) { + niov = reconstruct_iov(s, iov, 0, &pad_offset); + } + } + } + + if (niov) { + i = niov; } if (qcrypto_hash_bytesv(algo, iov, i, &digest_buf, &digest_len, NULL) < 0) { @@ -210,6 +328,9 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, case R_HASH_DEST: data &= ahc->dest_mask; break; + case R_HASH_KEY_BUFF: + data &= ahc->key_mask; + break; case R_HASH_SRC_LEN: data &= 0x0FFFFFFF; break; @@ -234,7 +355,8 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, __func__, data & ahc->hash_mask); break; } - do_hash_operation(s, algo, data & HASH_SG_EN); + do_hash_operation(s, algo, data & HASH_SG_EN, + ((data & HASH_HMAC_MASK) == HASH_DIGEST_ACCUM)); if (data & HASH_IRQ_EN) { qemu_irq_raise(s->irq); @@ -267,6 +389,8 @@ static void aspeed_hace_reset(DeviceState *dev) struct AspeedHACEState *s = ASPEED_HACE(dev); memset(s->regs, 0, sizeof(s->regs)); + s->iov_count = 0; + s->total_req_len = 0; } static void aspeed_hace_realize(DeviceState *dev, Error **errp) @@ -302,6 +426,8 @@ static const VMStateDescription vmstate_aspeed_hace = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS), + VMSTATE_UINT32(total_req_len, AspeedHACEState), + VMSTATE_UINT32(iov_count, AspeedHACEState), VMSTATE_END_OF_LIST(), } }; @@ -333,6 +459,7 @@ static void aspeed_ast2400_hace_class_init(ObjectClass *klass, void *data) ahc->src_mask = 0x0FFFFFFF; ahc->dest_mask = 0x0FFFFFF8; + ahc->key_mask = 0x0FFFFFC0; ahc->hash_mask = 0x000003ff; /* No SG or SHA512 modes */ } @@ -351,6 +478,7 @@ static void aspeed_ast2500_hace_class_init(ObjectClass *klass, void *data) ahc->src_mask = 0x3fffffff; ahc->dest_mask = 0x3ffffff8; + ahc->key_mask = 0x3FFFFFC0; ahc->hash_mask = 0x000003ff; /* No SG or SHA512 modes */ } @@ -369,6 +497,7 @@ static void aspeed_ast2600_hace_class_init(ObjectClass *klass, void *data) ahc->src_mask = 0x7FFFFFFF; ahc->dest_mask = 0x7FFFFFF8; + ahc->key_mask = 0x7FFFFFF8; ahc->hash_mask = 0x00147FFF; } @@ -378,11 +507,31 @@ static const TypeInfo aspeed_ast2600_hace_info = { .class_init = aspeed_ast2600_hace_class_init, }; +static void aspeed_ast1030_hace_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); + + dc->desc = "AST1030 Hash and Crypto Engine"; + + ahc->src_mask = 0x7FFFFFFF; + ahc->dest_mask = 0x7FFFFFF8; + ahc->key_mask = 0x7FFFFFF8; + ahc->hash_mask = 0x00147FFF; +} + +static const TypeInfo aspeed_ast1030_hace_info = { + .name = TYPE_ASPEED_AST1030_HACE, + .parent = TYPE_ASPEED_HACE, + .class_init = aspeed_ast1030_hace_class_init, +}; + static void aspeed_hace_register_types(void) { type_register_static(&aspeed_ast2400_hace_info); type_register_static(&aspeed_ast2500_hace_info); type_register_static(&aspeed_ast2600_hace_info); + type_register_static(&aspeed_ast1030_hace_info); type_register_static(&aspeed_hace_info); } diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c new file mode 100644 index 0000000000..bfa8b81d01 --- /dev/null +++ b/hw/misc/aspeed_sbc.c @@ -0,0 +1,144 @@ +/* + * ASPEED Secure Boot Controller + * + * Copyright (C) 2021-2022 IBM Corp. + * + * Joel Stanley + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/misc/aspeed_sbc.h" +#include "qapi/error.h" +#include "migration/vmstate.h" + +#define R_PROT (0x000 / 4) +#define R_STATUS (0x014 / 4) +#define R_QSR (0x040 / 4) + +static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size) +{ + AspeedSBCState *s = ASPEED_SBC(opaque); + + addr >>= 2; + + if (addr >= ASPEED_SBC_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, addr << 2); + return 0; + } + + return s->regs[addr]; +} + +static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSBCState *s = ASPEED_SBC(opaque); + + addr >>= 2; + + if (addr >= ASPEED_SBC_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, addr << 2); + return; + } + + switch (addr) { + case R_STATUS: + case R_QSR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read only register 0x%" HWADDR_PRIx "\n", + __func__, addr << 2); + return; + default: + break; + } + + s->regs[addr] = data; +} + +static const MemoryRegionOps aspeed_sbc_ops = { + .read = aspeed_sbc_read, + .write = aspeed_sbc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void aspeed_sbc_reset(DeviceState *dev) +{ + struct AspeedSBCState *s = ASPEED_SBC(dev); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set secure boot enabled with RSA4096_SHA256 and enable eMMC ABR */ + s->regs[R_STATUS] = 0x000044C6; + s->regs[R_QSR] = 0x07C07C89; +} + +static void aspeed_sbc_realize(DeviceState *dev, Error **errp) +{ + AspeedSBCState *s = ASPEED_SBC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sbc_ops, s, + TYPE_ASPEED_SBC, 0x1000); + + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription vmstate_aspeed_sbc = { + .name = TYPE_ASPEED_SBC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AspeedSBCState, ASPEED_SBC_NR_REGS), + VMSTATE_END_OF_LIST(), + } +}; + +static void aspeed_sbc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_sbc_realize; + dc->reset = aspeed_sbc_reset; + dc->vmsd = &vmstate_aspeed_sbc; +} + +static const TypeInfo aspeed_sbc_info = { + .name = TYPE_ASPEED_SBC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSBCState), + .class_init = aspeed_sbc_class_init, + .class_size = sizeof(AspeedSBCClass) +}; + +static void aspeed_ast2600_sbc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "AST2600 Secure Boot Controller"; +} + +static const TypeInfo aspeed_ast2600_sbc_info = { + .name = TYPE_ASPEED_AST2600_SBC, + .parent = TYPE_ASPEED_SBC, + .class_init = aspeed_ast2600_sbc_class_init, +}; + +static void aspeed_sbc_register_types(void) +{ + type_register_static(&aspeed_ast2600_sbc_info); + type_register_static(&aspeed_sbc_info); +} + +type_init(aspeed_sbc_register_types); diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index d06e179a6e..19b03471fc 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -213,6 +213,11 @@ static uint32_t aspeed_scu_get_random(void) } uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s) +{ + return ASPEED_SCU_GET_CLASS(s)->get_apb(s); +} + +static uint32_t aspeed_2400_scu_get_apb_freq(AspeedSCUState *s) { AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s); uint32_t hpll = asc->calc_hpll(s, s->regs[HPLL_PARAM]); @@ -221,6 +226,24 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s) / asc->apb_divider; } +static uint32_t aspeed_2600_scu_get_apb_freq(AspeedSCUState *s) +{ + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s); + uint32_t hpll = asc->calc_hpll(s, s->regs[AST2600_HPLL_PARAM]); + + return hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[AST2600_CLK_SEL]) + 1) + / asc->apb_divider; +} + +static uint32_t aspeed_1030_scu_get_apb_freq(AspeedSCUState *s) +{ + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s); + uint32_t hpll = asc->calc_hpll(s, s->regs[AST2600_HPLL_PARAM]); + + return hpll / (SCU_AST1030_CLK_GET_PCLK_DIV(s->regs[AST2600_CLK_SEL4]) + 1) + / asc->apb_divider; +} + static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); @@ -357,7 +380,8 @@ static const MemoryRegionOps aspeed_ast2500_scu_ops = { static uint32_t aspeed_scu_get_clkin(AspeedSCUState *s) { - if (s->hw_strap1 & SCU_HW_STRAP_CLK_25M_IN) { + if (s->hw_strap1 & SCU_HW_STRAP_CLK_25M_IN || + ASPEED_SCU_GET_CLASS(s)->clkin_25Mhz) { return 25000000; } else if (s->hw_strap1 & SCU_HW_STRAP_CLK_48M_IN) { return 48000000; @@ -426,6 +450,26 @@ static uint32_t aspeed_2500_scu_calc_hpll(AspeedSCUState *s, uint32_t hpll_reg) return clkin * multiplier; } +static uint32_t aspeed_2600_scu_calc_hpll(AspeedSCUState *s, uint32_t hpll_reg) +{ + uint32_t multiplier = 1; + uint32_t clkin = aspeed_scu_get_clkin(s); + + if (hpll_reg & SCU_AST2600_H_PLL_OFF) { + return 0; + } + + if (!(hpll_reg & SCU_AST2600_H_PLL_BYPASS_EN)) { + uint32_t p = (hpll_reg >> 19) & 0xf; + uint32_t n = (hpll_reg >> 13) & 0x3f; + uint32_t m = hpll_reg & 0x1fff; + + multiplier = ((m + 1) / (n + 1)) / (p + 1); + } + + return clkin * multiplier; +} + static void aspeed_scu_reset(DeviceState *dev) { AspeedSCUState *s = ASPEED_SCU(dev); @@ -447,6 +491,8 @@ static uint32_t aspeed_silicon_revs[] = { AST2600_A1_SILICON_REV, AST2600_A2_SILICON_REV, AST2600_A3_SILICON_REV, + AST1030_A0_SILICON_REV, + AST1030_A1_SILICON_REV, }; bool is_supported_silicon_rev(uint32_t silicon_rev) @@ -525,8 +571,10 @@ static void aspeed_2400_scu_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2400 System Control Unit"; asc->resets = ast2400_a0_resets; asc->calc_hpll = aspeed_2400_scu_calc_hpll; + asc->get_apb = aspeed_2400_scu_get_apb_freq; asc->apb_divider = 2; asc->nr_regs = ASPEED_SCU_NR_REGS; + asc->clkin_25Mhz = false; asc->ops = &aspeed_ast2400_scu_ops; } @@ -545,8 +593,10 @@ static void aspeed_2500_scu_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2500 System Control Unit"; asc->resets = ast2500_a1_resets; asc->calc_hpll = aspeed_2500_scu_calc_hpll; + asc->get_apb = aspeed_2400_scu_get_apb_freq; asc->apb_divider = 4; asc->nr_regs = ASPEED_SCU_NR_REGS; + asc->clkin_25Mhz = false; asc->ops = &aspeed_ast2500_scu_ops; } @@ -716,9 +766,11 @@ static void aspeed_2600_scu_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2600 System Control Unit"; dc->reset = aspeed_ast2600_scu_reset; asc->resets = ast2600_a3_resets; - asc->calc_hpll = aspeed_2500_scu_calc_hpll; /* No change since AST2500 */ + asc->calc_hpll = aspeed_2600_scu_calc_hpll; + asc->get_apb = aspeed_2600_scu_get_apb_freq; asc->apb_divider = 4; asc->nr_regs = ASPEED_AST2600_SCU_NR_REGS; + asc->clkin_25Mhz = true; asc->ops = &aspeed_ast2600_scu_ops; } @@ -729,12 +781,64 @@ static const TypeInfo aspeed_2600_scu_info = { .class_init = aspeed_2600_scu_class_init, }; +static const uint32_t ast1030_a1_resets[ASPEED_AST2600_SCU_NR_REGS] = { + [AST2600_SYS_RST_CTRL] = 0xFFC3FED8, + [AST2600_SYS_RST_CTRL2] = 0x09FFFFFC, + [AST2600_CLK_STOP_CTRL] = 0xFFFF7F8A, + [AST2600_CLK_STOP_CTRL2] = 0xFFF0FFF0, + [AST2600_DEBUG_CTRL2] = 0x00000000, + [AST2600_HPLL_PARAM] = 0x10004077, + [AST2600_HPLL_EXT] = 0x00000031, + [AST2600_CLK_SEL4] = 0x43F90900, + [AST2600_CLK_SEL5] = 0x40000000, + [AST2600_CHIP_ID0] = 0xDEADBEEF, + [AST2600_CHIP_ID1] = 0x0BADCAFE, +}; + +static void aspeed_ast1030_scu_reset(DeviceState *dev) +{ + AspeedSCUState *s = ASPEED_SCU(dev); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); + + memcpy(s->regs, asc->resets, asc->nr_regs * 4); + + s->regs[AST2600_SILICON_REV] = AST1030_A1_SILICON_REV; + s->regs[AST2600_SILICON_REV2] = s->silicon_rev; + s->regs[AST2600_HW_STRAP1] = s->hw_strap1; + s->regs[AST2600_HW_STRAP2] = s->hw_strap2; + s->regs[PROT_KEY] = s->hw_prot_key; +} + +static void aspeed_1030_scu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); + + dc->desc = "ASPEED 1030 System Control Unit"; + dc->reset = aspeed_ast1030_scu_reset; + asc->resets = ast1030_a1_resets; + asc->calc_hpll = aspeed_2600_scu_calc_hpll; + asc->get_apb = aspeed_1030_scu_get_apb_freq; + asc->apb_divider = 2; + asc->nr_regs = ASPEED_AST2600_SCU_NR_REGS; + asc->clkin_25Mhz = true; + asc->ops = &aspeed_ast2600_scu_ops; +} + +static const TypeInfo aspeed_1030_scu_info = { + .name = TYPE_ASPEED_1030_SCU, + .parent = TYPE_ASPEED_SCU, + .instance_size = sizeof(AspeedSCUState), + .class_init = aspeed_1030_scu_class_init, +}; + static void aspeed_scu_register_types(void) { type_register_static(&aspeed_scu_info); type_register_static(&aspeed_2400_scu_info); type_register_static(&aspeed_2500_scu_info); type_register_static(&aspeed_2600_scu_info); + type_register_static(&aspeed_1030_scu_info); } type_init(aspeed_scu_register_types); diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 08f856cbda..d2a3931033 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -130,6 +130,7 @@ static uint64_t aspeed_sdmc_read(void *opaque, hwaddr addr, unsigned size) return 0; } + trace_aspeed_sdmc_read(addr, s->regs[addr]); return s->regs[addr]; } @@ -148,6 +149,7 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data, return; } + trace_aspeed_sdmc_write(addr, data); asc->write(s, addr, data); } diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c index 79f4375911..7b0e968804 100644 --- a/hw/misc/imx6_src.c +++ b/hw/misc/imx6_src.c @@ -151,7 +151,7 @@ static void imx6_defer_clear_reset_bit(int cpuid, return; } - ri = g_malloc(sizeof(struct SRCSCRResetInfo)); + ri = g_new(struct SRCSCRResetInfo, 1); ri->s = s; ri->reset_bit = reset_shift; diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 299837e5c1..8270db53cd 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -411,7 +411,7 @@ static void resize_peers(IVShmemState *s, int nb_peers) assert(nb_peers > old_nb_peers); IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers); - s->peers = g_realloc(s->peers, nb_peers * sizeof(Peer)); + s->peers = g_renew(Peer, s->peers, nb_peers); s->nb_peers = nb_peers; for (i = old_nb_peers; i < nb_peers; i++) { @@ -424,16 +424,19 @@ static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector, Error **errp) { PCIDevice *pdev = PCI_DEVICE(s); + KVMRouteChange c; int ret; IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); assert(!s->msi_vectors[vector].pdev); - ret = kvm_irqchip_add_msi_route(kvm_state, vector, pdev); + c = kvm_irqchip_begin_route_changes(kvm_state); + ret = kvm_irqchip_add_msi_route(&c, vector, pdev); if (ret < 0) { error_setg(errp, "kvm_irqchip_add_msi_route failed"); return; } + kvm_irqchip_commit_route_changes(&c); s->msi_vectors[vector].virq = ret; s->msi_vectors[vector].pdev = pdev; @@ -534,7 +537,7 @@ static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); event_notifier_init_fd(&peer->eventfds[vector], fd); - fcntl_setfl(fd, O_NONBLOCK); /* msix/irqfd poll non block */ + g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */ if (posn == s->vm_id) { setup_interrupt(s, vector, errp); @@ -728,7 +731,7 @@ static void ivshmem_reset(DeviceState *d) static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp) { /* allocate QEMU callback data for receiving interrupts */ - s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector)); + s->msi_vectors = g_new0(MSIVector, s->vectors); if (ivshmem_has_feature(s, IVSHMEM_MSI)) { if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1, errp)) { diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 71b74c3372..525e38ce93 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -325,10 +325,11 @@ static void via1_sixty_hz(void *opaque) { MOS6522Q800VIA1State *v1s = opaque; MOS6522State *s = MOS6522(v1s); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); + qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA1_IRQ_60HZ_BIT); - s->ifr |= VIA1_IRQ_60HZ; - mdc->update_irq(s); + /* Negative edge trigger */ + qemu_irq_lower(irq); + qemu_irq_raise(irq); via1_sixty_hz_update(v1s); } @@ -337,44 +338,15 @@ static void via1_one_second(void *opaque) { MOS6522Q800VIA1State *v1s = opaque; MOS6522State *s = MOS6522(v1s); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); + qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA1_IRQ_ONE_SECOND_BIT); - s->ifr |= VIA1_IRQ_ONE_SECOND; - mdc->update_irq(s); + /* Negative edge trigger */ + qemu_irq_lower(irq); + qemu_irq_raise(irq); via1_one_second_update(v1s); } -static void via1_irq_request(void *opaque, int irq, int level) -{ - MOS6522Q800VIA1State *v1s = opaque; - MOS6522State *s = MOS6522(v1s); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); - - if (level) { - s->ifr |= 1 << irq; - } else { - s->ifr &= ~(1 << irq); - } - - mdc->update_irq(s); -} - -static void via2_irq_request(void *opaque, int irq, int level) -{ - MOS6522Q800VIA2State *v2s = opaque; - MOS6522State *s = MOS6522(v2s); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); - - if (level) { - s->ifr |= 1 << irq; - } else { - s->ifr &= ~(1 << irq); - } - - mdc->update_irq(s); -} - static void pram_update(MOS6522Q800VIA1State *v1s) { @@ -938,9 +910,26 @@ static uint64_t mos6522_q800_via2_read(void *opaque, hwaddr addr, unsigned size) { MOS6522Q800VIA2State *s = MOS6522_Q800_VIA2(opaque); MOS6522State *ms = MOS6522(s); + uint64_t val; addr = (addr >> 9) & 0xf; - return mos6522_read(ms, addr, size); + val = mos6522_read(ms, addr, size); + + switch (addr) { + case VIA_REG_IFR: + /* + * On a Q800 an emulated VIA2 is integrated into the onboard logic. The + * expectation of most OSs is that the DRQ bit is live, rather than + * latched as it would be on a real VIA so do the same here. + * + * Note: DRQ is negative edge triggered + */ + val &= ~VIA2_IRQ_SCSI_DATA; + val |= (~ms->last_irq_levels & VIA2_IRQ_SCSI_DATA); + break; + } + + return val; } static void mos6522_q800_via2_write(void *opaque, hwaddr addr, uint64_t val, @@ -1061,8 +1050,6 @@ static void mos6522_q800_via1_init(Object *obj) qbus_init((BusState *)&v1s->adb_bus, sizeof(v1s->adb_bus), TYPE_ADB_BUS, DEVICE(v1s), "adb.0"); - qdev_init_gpio_in(DEVICE(obj), via1_irq_request, VIA1_IRQ_NB); - /* A/UX mode */ qdev_init_gpio_out(DEVICE(obj), &v1s->auxmode_irq, 1); } @@ -1110,9 +1097,11 @@ static Property mos6522_q800_via1_properties[] = { static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); dc->realize = mos6522_q800_via1_realize; - dc->reset = mos6522_q800_via1_reset; + device_class_set_parent_reset(dc, mos6522_q800_via1_reset, + &mdc->parent_reset); dc->vmsd = &vmstate_q800_via1; device_class_set_props(dc, mos6522_q800_via1_properties); } @@ -1150,22 +1139,21 @@ static void mos6522_q800_via2_reset(DeviceState *dev) ms->a = 0x7f; } -static void via2_nubus_irq_request(void *opaque, int irq, int level) +static void via2_nubus_irq_request(void *opaque, int n, int level) { MOS6522Q800VIA2State *v2s = opaque; MOS6522State *s = MOS6522(v2s); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); + qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA2_IRQ_NUBUS_BIT); if (level) { /* Port A nubus IRQ inputs are active LOW */ - s->a &= ~(1 << irq); - s->ifr |= 1 << VIA2_IRQ_NUBUS_BIT; + s->a &= ~(1 << n); } else { - s->a |= (1 << irq); - s->ifr &= ~(1 << VIA2_IRQ_NUBUS_BIT); + s->a |= (1 << n); } - mdc->update_irq(s); + /* Negative edge trigger */ + qemu_set_irq(irq, !level); } static void mos6522_q800_via2_init(Object *obj) @@ -1177,8 +1165,6 @@ static void mos6522_q800_via2_init(Object *obj) "via2", VIA_SIZE); sysbus_init_mmio(sbd, &v2s->via_mem); - qdev_init_gpio_in(DEVICE(obj), via2_irq_request, VIA2_IRQ_NB); - qdev_init_gpio_in_named(DEVICE(obj), via2_nubus_irq_request, "nubus-irq", VIA2_NUBUS_IRQ_NB); } @@ -1199,7 +1185,8 @@ static void mos6522_q800_via2_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); - dc->reset = mos6522_q800_via2_reset; + device_class_set_parent_reset(dc, mos6522_q800_via2_reset, + &mdc->parent_reset); dc->vmsd = &vmstate_q800_via2; mdc->portB_write = mos6522_q800_via2_portB_write; } diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 233daf1405..1498113cfc 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/ppc/mac.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -96,9 +97,9 @@ static void cuda_set_sr_int(void *opaque) CUDAState *s = opaque; MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); + qemu_irq irq = qdev_get_gpio_in(DEVICE(ms), SR_INT_BIT); - mdc->set_sr_int(ms); + qemu_set_irq(irq, 1); } static void cuda_delay_set_sr_int(CUDAState *s) @@ -605,7 +606,8 @@ static void mos6522_cuda_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); - dc->reset = mos6522_cuda_reset; + device_class_set_parent_reset(dc, mos6522_cuda_reset, + &mdc->parent_reset); mdc->portB_write = mos6522_cuda_portB_write; mdc->get_timer1_counter_value = cuda_get_counter_value; mdc->get_timer2_counter_value = cuda_get_counter_value; diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 76c608ee19..336502a84b 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -57,27 +57,14 @@ #define VIA_TIMER_FREQ (4700000 / 6) -static void via_update_irq(PMUState *s) -{ - MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); - MOS6522State *ms = MOS6522(mps); - - bool new_state = !!(ms->ifr & ms->ier & (SR_INT | T1_INT | T2_INT)); - - if (new_state != s->via_irq_state) { - s->via_irq_state = new_state; - qemu_set_irq(s->via_irq, new_state); - } -} - static void via_set_sr_int(void *opaque) { PMUState *s = opaque; MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); MOS6522State *ms = MOS6522(mps); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); + qemu_irq irq = qdev_get_gpio_in(DEVICE(ms), SR_INT_BIT); - mdc->set_sr_int(ms); + qemu_set_irq(irq, 1); } static void pmu_update_extirq(PMUState *s) @@ -808,28 +795,9 @@ static void mos6522_pmu_portB_write(MOS6522State *s) MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); PMUState *ps = container_of(mps, PMUState, mos6522_pmu); - if ((s->pcr & 0xe0) == 0x20 || (s->pcr & 0xe0) == 0x60) { - s->ifr &= ~CB2_INT; - } - s->ifr &= ~CB1_INT; - - via_update_irq(ps); pmu_update(ps); } -static void mos6522_pmu_portA_write(MOS6522State *s) -{ - MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); - PMUState *ps = container_of(mps, PMUState, mos6522_pmu); - - if ((s->pcr & 0x0e) == 0x02 || (s->pcr & 0x0e) == 0x06) { - s->ifr &= ~CA2_INT; - } - s->ifr &= ~CA1_INT; - - via_update_irq(ps); -} - static void mos6522_pmu_reset(DeviceState *dev) { MOS6522State *ms = MOS6522(dev); @@ -850,9 +818,9 @@ static void mos6522_pmu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); - dc->reset = mos6522_pmu_reset; + device_class_set_parent_reset(dc, mos6522_pmu_reset, + &mdc->parent_reset); mdc->portB_write = mos6522_pmu_portB_write; - mdc->portA_write = mos6522_pmu_portA_write; } static const TypeInfo mos6522_pmu_type_info = { diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 6dcbe044f3..2ff05c7afa 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -84,6 +84,9 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( )) softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) +specific_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-crf.c')) +specific_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-apu-ctrl.c')) +specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-crl.c')) softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( 'xlnx-versal-xramc.c', 'xlnx-versal-pmc-iou-slcr.c', @@ -111,6 +114,7 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_i3c.c', 'aspeed_lpc.c', 'aspeed_scu.c', + 'aspeed_sbc.c', 'aspeed_sdmc.c', 'aspeed_xdma.c')) diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index 80683fed31..badef5c214 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -189,7 +189,8 @@ static void wake_blocked_threads(ITCStorageCell *c) c->blocked_threads = 0; } -static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c) +static G_NORETURN +void block_thread_and_exit(ITCStorageCell *c) { c->blocked_threads |= 1ULL << current_cpu->cpu_index; current_cpu->halted = 1; diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 1c57332b40..f9e646350e 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -30,12 +30,21 @@ #include "hw/misc/mos6522.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" +#include "qapi/type-helpers.h" #include "qemu/timer.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" + +static const char *mos6522_reg_names[MOS6522_NUM_REGS] = { + "ORB", "ORA", "DDRB", "DDRA", "T1CL", "T1CH", "T1LL", "T1LH", + "T2CL", "T2CH", "SR", "ACR", "PCR", "IFR", "IER", "ANH" +}; + /* XXX: implement all timer modes */ static void mos6522_timer1_update(MOS6522State *s, MOS6522Timer *ti, @@ -52,6 +61,73 @@ static void mos6522_update_irq(MOS6522State *s) } } +static void mos6522_set_irq(void *opaque, int n, int level) +{ + MOS6522State *s = MOS6522(opaque); + int last_level = !!(s->last_irq_levels & (1 << n)); + uint8_t last_ifr = s->ifr; + bool positive_edge = true; + int ctrl; + + /* + * SR_INT is managed by mos6522 instances and cleared upon SR + * read. It is only the external CA1/2 and CB1/2 lines that + * are edge-triggered and latched in IFR + */ + if (n != SR_INT_BIT && level == last_level) { + return; + } + + /* Detect negative edge trigger */ + if (last_level == 1 && level == 0) { + positive_edge = false; + } + + switch (n) { + case CA2_INT_BIT: + ctrl = (s->pcr & CA2_CTRL_MASK) >> CA2_CTRL_SHIFT; + if ((positive_edge && (ctrl & C2_POS)) || + (!positive_edge && !(ctrl & C2_POS))) { + s->ifr |= 1 << n; + } + break; + case CA1_INT_BIT: + ctrl = (s->pcr & CA1_CTRL_MASK) >> CA1_CTRL_SHIFT; + if ((positive_edge && (ctrl & C1_POS)) || + (!positive_edge && !(ctrl & C1_POS))) { + s->ifr |= 1 << n; + } + break; + case SR_INT_BIT: + s->ifr |= 1 << n; + break; + case CB2_INT_BIT: + ctrl = (s->pcr & CB2_CTRL_MASK) >> CB2_CTRL_SHIFT; + if ((positive_edge && (ctrl & C2_POS)) || + (!positive_edge && !(ctrl & C2_POS))) { + s->ifr |= 1 << n; + } + break; + case CB1_INT_BIT: + ctrl = (s->pcr & CB1_CTRL_MASK) >> CB1_CTRL_SHIFT; + if ((positive_edge && (ctrl & C1_POS)) || + (!positive_edge && !(ctrl & C1_POS))) { + s->ifr |= 1 << n; + } + break; + } + + if (s->ifr != last_ifr) { + mos6522_update_irq(s); + } + + if (level) { + s->last_irq_levels |= 1 << n; + } else { + s->last_irq_levels &= ~(1 << n); + } +} + static uint64_t get_counter_value(MOS6522State *s, MOS6522Timer *ti) { MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); @@ -195,13 +271,6 @@ static void mos6522_timer2(void *opaque) mos6522_update_irq(s); } -static void mos6522_set_sr_int(MOS6522State *s) -{ - trace_mos6522_set_sr_int(); - s->ifr |= SR_INT; - mos6522_update_irq(s); -} - static uint64_t mos6522_get_counter_value(MOS6522State *s, MOS6522Timer *ti) { return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ti->load_time, @@ -229,6 +298,7 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) { MOS6522State *s = opaque; uint32_t val; + int ctrl; int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (now >= s->timers[0].next_irq_time) { @@ -242,12 +312,24 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) switch (addr) { case VIA_REG_B: val = s->b; + ctrl = (s->pcr & CB2_CTRL_MASK) >> CB2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CB2_INT; + } + s->ifr &= ~CB1_INT; + mos6522_update_irq(s); break; case VIA_REG_A: qemu_log_mask(LOG_UNIMP, "Read access to register A with handshake"); /* fall through */ case VIA_REG_ANH: val = s->a; + ctrl = (s->pcr & CA2_CTRL_MASK) >> CA2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CA2_INT; + } + s->ifr &= ~CA1_INT; + mos6522_update_irq(s); break; case VIA_REG_DIRB: val = s->dirb; @@ -304,7 +386,7 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) } if (addr != VIA_REG_IFR || val != 0) { - trace_mos6522_read(addr, val); + trace_mos6522_read(addr, mos6522_reg_names[addr], val); } return val; @@ -314,13 +396,20 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { MOS6522State *s = opaque; MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); + int ctrl; - trace_mos6522_write(addr, val); + trace_mos6522_write(addr, mos6522_reg_names[addr], val); switch (addr) { case VIA_REG_B: s->b = (s->b & ~s->dirb) | (val & s->dirb); mdc->portB_write(s); + ctrl = (s->pcr & CB2_CTRL_MASK) >> CB2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CB2_INT; + } + s->ifr &= ~CB1_INT; + mos6522_update_irq(s); break; case VIA_REG_A: qemu_log_mask(LOG_UNIMP, "Write access to register A with handshake"); @@ -328,6 +417,12 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) case VIA_REG_ANH: s->a = (s->a & ~s->dira) | (val & s->dira); mdc->portA_write(s); + ctrl = (s->pcr & CA2_CTRL_MASK) >> CA2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CA2_INT; + } + s->ifr &= ~CA1_INT; + mos6522_update_irq(s); break; case VIA_REG_DIRB: s->dirb = val; @@ -403,6 +498,106 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) } } +static int qmp_x_query_via_foreach(Object *obj, void *opaque) +{ + GString *buf = opaque; + + if (object_dynamic_cast(obj, TYPE_MOS6522)) { + MOS6522State *s = MOS6522(obj); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint16_t t1counter = get_counter(s, &s->timers[0]); + uint16_t t2counter = get_counter(s, &s->timers[1]); + + g_string_append_printf(buf, "%s:\n", object_get_typename(obj)); + + g_string_append_printf(buf, " Registers:\n"); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[0], s->b); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[1], s->a); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[2], s->dirb); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[3], s->dira); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[4], t1counter & 0xff); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[5], t1counter >> 8); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[6], + s->timers[0].latch & 0xff); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[7], + s->timers[0].latch >> 8); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[8], t2counter & 0xff); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[9], t2counter >> 8); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[10], s->sr); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[11], s->acr); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[12], s->pcr); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[13], s->ifr); + g_string_append_printf(buf, " %-*s: 0x%x\n", 4, + mos6522_reg_names[14], s->ier); + + g_string_append_printf(buf, " Timers:\n"); + g_string_append_printf(buf, " Using current time now(ns)=%"PRId64 + "\n", now); + g_string_append_printf(buf, " T1 freq(hz)=%"PRId64 + " mode=%s" + " counter=0x%x" + " latch=0x%x\n" + " load_time(ns)=%"PRId64 + " next_irq_time(ns)=%"PRId64 "\n", + s->timers[0].frequency, + ((s->acr & T1MODE) == T1MODE_CONT) ? "continuous" + : "one-shot", + t1counter, + s->timers[0].latch, + s->timers[0].load_time, + get_next_irq_time(s, &s->timers[0], now)); + g_string_append_printf(buf, " T2 freq(hz)=%"PRId64 + " mode=%s" + " counter=0x%x" + " latch=0x%x\n" + " load_time(ns)=%"PRId64 + " next_irq_time(ns)=%"PRId64 "\n", + s->timers[1].frequency, + "one-shot", + t2counter, + s->timers[1].latch, + s->timers[1].load_time, + get_next_irq_time(s, &s->timers[1], now)); + } + + return 0; +} + +static HumanReadableText *qmp_x_query_via(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), + qmp_x_query_via_foreach, buf); + + return human_readable_text_from_str(buf); +} + +void hmp_info_via(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + g_autoptr(HumanReadableText) info = qmp_x_query_via(&err); + + if (hmp_handle_error(mon, err)) { + return; + } + monitor_printf(mon, "%s", info->human_readable_text); +} + static const MemoryRegionOps mos6522_ops = { .read = mos6522_read, .write = mos6522_write, @@ -429,8 +624,8 @@ static const VMStateDescription vmstate_mos6522_timer = { const VMStateDescription vmstate_mos6522 = { .name = "mos6522", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT8(a, MOS6522State), VMSTATE_UINT8(b, MOS6522State), @@ -441,6 +636,7 @@ const VMStateDescription vmstate_mos6522 = { VMSTATE_UINT8(pcr, MOS6522State), VMSTATE_UINT8(ifr, MOS6522State), VMSTATE_UINT8(ier, MOS6522State), + VMSTATE_UINT8(last_irq_levels, MOS6522State), VMSTATE_STRUCT_ARRAY(timers, MOS6522State, 2, 0, vmstate_mos6522_timer, MOS6522Timer), VMSTATE_END_OF_LIST() @@ -478,7 +674,8 @@ static void mos6522_init(Object *obj) MOS6522State *s = MOS6522(obj); int i; - memory_region_init_io(&s->mem, obj, &mos6522_ops, s, "mos6522", 0x10); + memory_region_init_io(&s->mem, obj, &mos6522_ops, s, "mos6522", + MOS6522_NUM_REGS); sysbus_init_mmio(sbd, &s->mem); sysbus_init_irq(sbd, &s->irq); @@ -488,6 +685,8 @@ static void mos6522_init(Object *obj) s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, mos6522_timer1, s); s->timers[1].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, mos6522_timer2, s); + + qdev_init_gpio_in(DEVICE(obj), mos6522_set_irq, VIA_NUM_INTS); } static void mos6522_finalize(Object *obj) @@ -511,11 +710,8 @@ static void mos6522_class_init(ObjectClass *oc, void *data) dc->reset = mos6522_reset; dc->vmsd = &vmstate_mos6522; device_class_set_props(dc, mos6522_properties); - mdc->parent_reset = dc->reset; - mdc->set_sr_int = mos6522_set_sr_int; mdc->portB_write = mos6522_portB_write; mdc->portA_write = mos6522_portA_write; - mdc->update_irq = mos6522_update_irq; mdc->get_timer1_counter_value = mos6522_get_counter_value; mdc->get_timer2_counter_value = mos6522_get_counter_value; mdc->get_timer1_load_time = mos6522_get_load_time; diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c index 0b61070c52..bc2b879feb 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm7xx_clk.c @@ -612,8 +612,8 @@ static void npcm7xx_clk_sel_init(Object *obj) NPCM7xxClockSELState *sel = NPCM7XX_CLOCK_SEL(obj); for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) { - sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel), - g_strdup_printf("clock-in[%d]", i), + g_autofree char *s = g_strdup_printf("clock-in[%d]", i); + sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel), s, npcm7xx_clk_update_sel_cb, sel, ClockUpdate); } sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out"); diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index a39fcdd1fc..b84d4d458d 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -21,6 +21,7 @@ #include "hw/misc/pvpanic.h" #include "qom/object.h" #include "hw/isa/isa.h" +#include "standard-headers/linux/pvpanic.h" OBJECT_DECLARE_SIMPLE_TYPE(PVPanicISAState, PVPANIC_ISA_DEVICE) @@ -64,7 +65,8 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp) static Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505), - DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, PVPANIC_PANICKED | PVPANIC_CRASHLOADED), + DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, + PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 62e1be68c1..99cf7e2041 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -21,6 +21,7 @@ #include "hw/misc/pvpanic.h" #include "qom/object.h" #include "hw/pci/pci.h" +#include "standard-headers/linux/pvpanic.h" OBJECT_DECLARE_SIMPLE_TYPE(PVPanicPCIState, PVPANIC_PCI_DEVICE) @@ -53,7 +54,8 @@ static void pvpanic_pci_realizefn(PCIDevice *dev, Error **errp) } static Property pvpanic_pci_properties[] = { - DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, PVPANIC_PANICKED | PVPANIC_CRASHLOADED), + DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, + PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index e2cb4a5d28..1540e9091a 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -21,12 +21,13 @@ #include "hw/qdev-properties.h" #include "hw/misc/pvpanic.h" #include "qom/object.h" +#include "standard-headers/linux/pvpanic.h" static void handle_event(int event) { static bool logged; - if (event & ~(PVPANIC_PANICKED | PVPANIC_CRASHLOADED) && !logged) { + if (event & ~(PVPANIC_PANICKED | PVPANIC_CRASH_LOADED) && !logged) { qemu_log_mask(LOG_GUEST_ERROR, "pvpanic: unknown event %#x.\n", event); logged = true; } @@ -36,7 +37,7 @@ static void handle_event(int event) return; } - if (event & PVPANIC_CRASHLOADED) { + if (event & PVPANIC_CRASH_LOADED) { qemu_system_guest_crashloaded(NULL); return; } diff --git a/hw/misc/sbsa_ec.c b/hw/misc/sbsa_ec.c index 83020fe9ac..8d939fe31b 100644 --- a/hw/misc/sbsa_ec.c +++ b/hw/misc/sbsa_ec.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/log.h" #include "hw/sysbus.h" #include "sysemu/runstate.h" diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 1c373dd0a4..4e0c7973a4 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -1,7 +1,7 @@ # See docs/devel/tracing.rst for syntax documentation. # allwinner-cpucfg.c -allwinner_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "id %u, reset_addr 0x%" PRIu32 +allwinner_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "id %u, reset_addr 0x%" PRIx32 allwinner_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_cpucfg_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 @@ -93,10 +93,10 @@ imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08 # mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" -mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d counter=0x%"PRId64 " delta_next=0x%"PRId64 +mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d counter=0x%"PRIx64 " delta_next=0x%"PRIx64 mos6522_set_sr_int(void) "set sr_int" -mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 -mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" +mos6522_write(uint64_t addr, const char *name, uint64_t val) "reg=0x%"PRIx64 " [%s] val=0x%"PRIx64 +mos6522_read(uint64_t addr, const char *name, unsigned val) "reg=0x%"PRIx64 " [%s] val=0x%x" # npcm7xx_clk.c npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 @@ -205,6 +205,10 @@ aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" PRIx64 aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64 +# aspeed_sdmc.c +aspeed_sdmc_write(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 +aspeed_sdmc_read(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 + # bcm2835_property.c bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c new file mode 100644 index 0000000000..767106b7a3 --- /dev/null +++ b/hw/misc/xlnx-versal-crl.c @@ -0,0 +1,421 @@ +/* + * QEMU model of the Clock-Reset-LPD (CRL). + * + * Copyright (c) 2022 Advanced Micro Devices, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Written by Edgar E. Iglesias + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/bitops.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/register.h" +#include "hw/resettable.h" + +#include "target/arm/arm-powerctl.h" +#include "hw/misc/xlnx-versal-crl.h" + +#ifndef XLNX_VERSAL_CRL_ERR_DEBUG +#define XLNX_VERSAL_CRL_ERR_DEBUG 0 +#endif + +static void crl_update_irq(XlnxVersalCRL *s) +{ + bool pending = s->regs[R_IR_STATUS] & ~s->regs[R_IR_MASK]; + qemu_set_irq(s->irq, pending); +} + +static void crl_status_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + crl_update_irq(s); +} + +static uint64_t crl_enable_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + uint32_t val = val64; + + s->regs[R_IR_MASK] &= ~val; + crl_update_irq(s); + return 0; +} + +static uint64_t crl_disable_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + uint32_t val = val64; + + s->regs[R_IR_MASK] |= val; + crl_update_irq(s); + return 0; +} + +static void crl_reset_dev(XlnxVersalCRL *s, DeviceState *dev, + bool rst_old, bool rst_new) +{ + device_cold_reset(dev); +} + +static void crl_reset_cpu(XlnxVersalCRL *s, ARMCPU *armcpu, + bool rst_old, bool rst_new) +{ + if (rst_new) { + arm_set_cpu_off(armcpu->mp_affinity); + } else { + arm_set_cpu_on_and_reset(armcpu->mp_affinity); + } +} + +#define REGFIELD_RESET(type, s, reg, f, new_val, dev) { \ + bool old_f = ARRAY_FIELD_EX32((s)->regs, reg, f); \ + bool new_f = FIELD_EX32(new_val, reg, f); \ + \ + /* Detect edges. */ \ + if (dev && old_f != new_f) { \ + crl_reset_ ## type(s, dev, old_f, new_f); \ + } \ +} + +static uint64_t crl_rst_r5_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + + REGFIELD_RESET(cpu, s, RST_CPU_R5, RESET_CPU0, val64, s->cfg.cpu_r5[0]); + REGFIELD_RESET(cpu, s, RST_CPU_R5, RESET_CPU1, val64, s->cfg.cpu_r5[1]); + return val64; +} + +static uint64_t crl_rst_adma_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + int i; + + /* A single register fans out to all ADMA reset inputs. */ + for (i = 0; i < ARRAY_SIZE(s->cfg.adma); i++) { + REGFIELD_RESET(dev, s, RST_ADMA, RESET, val64, s->cfg.adma[i]); + } + return val64; +} + +static uint64_t crl_rst_uart0_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + + REGFIELD_RESET(dev, s, RST_UART0, RESET, val64, s->cfg.uart[0]); + return val64; +} + +static uint64_t crl_rst_uart1_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + + REGFIELD_RESET(dev, s, RST_UART1, RESET, val64, s->cfg.uart[1]); + return val64; +} + +static uint64_t crl_rst_gem0_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + + REGFIELD_RESET(dev, s, RST_GEM0, RESET, val64, s->cfg.gem[0]); + return val64; +} + +static uint64_t crl_rst_gem1_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + + REGFIELD_RESET(dev, s, RST_GEM1, RESET, val64, s->cfg.gem[1]); + return val64; +} + +static uint64_t crl_rst_usb_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(reg->opaque); + + REGFIELD_RESET(dev, s, RST_USB0, RESET, val64, s->cfg.usb); + return val64; +} + +static const RegisterAccessInfo crl_regs_info[] = { + { .name = "ERR_CTRL", .addr = A_ERR_CTRL, + },{ .name = "IR_STATUS", .addr = A_IR_STATUS, + .w1c = 0x1, + .post_write = crl_status_postw, + },{ .name = "IR_MASK", .addr = A_IR_MASK, + .reset = 0x1, + .ro = 0x1, + },{ .name = "IR_ENABLE", .addr = A_IR_ENABLE, + .pre_write = crl_enable_prew, + },{ .name = "IR_DISABLE", .addr = A_IR_DISABLE, + .pre_write = crl_disable_prew, + },{ .name = "WPROT", .addr = A_WPROT, + },{ .name = "PLL_CLK_OTHER_DMN", .addr = A_PLL_CLK_OTHER_DMN, + .reset = 0x1, + .rsvd = 0xe, + },{ .name = "RPLL_CTRL", .addr = A_RPLL_CTRL, + .reset = 0x24809, + .rsvd = 0xf88c00f6, + },{ .name = "RPLL_CFG", .addr = A_RPLL_CFG, + .reset = 0x2000000, + .rsvd = 0x1801210, + },{ .name = "RPLL_FRAC_CFG", .addr = A_RPLL_FRAC_CFG, + .rsvd = 0x7e330000, + },{ .name = "PLL_STATUS", .addr = A_PLL_STATUS, + .reset = R_PLL_STATUS_RPLL_STABLE_MASK | + R_PLL_STATUS_RPLL_LOCK_MASK, + .rsvd = 0xfa, + .ro = 0x5, + },{ .name = "RPLL_TO_XPD_CTRL", .addr = A_RPLL_TO_XPD_CTRL, + .reset = 0x2000100, + .rsvd = 0xfdfc00ff, + },{ .name = "LPD_TOP_SWITCH_CTRL", .addr = A_LPD_TOP_SWITCH_CTRL, + .reset = 0x6000300, + .rsvd = 0xf9fc00f8, + },{ .name = "LPD_LSBUS_CTRL", .addr = A_LPD_LSBUS_CTRL, + .reset = 0x2000800, + .rsvd = 0xfdfc00f8, + },{ .name = "CPU_R5_CTRL", .addr = A_CPU_R5_CTRL, + .reset = 0xe000300, + .rsvd = 0xe1fc00f8, + },{ .name = "IOU_SWITCH_CTRL", .addr = A_IOU_SWITCH_CTRL, + .reset = 0x2000500, + .rsvd = 0xfdfc00f8, + },{ .name = "GEM0_REF_CTRL", .addr = A_GEM0_REF_CTRL, + .reset = 0xe000a00, + .rsvd = 0xf1fc00f8, + },{ .name = "GEM1_REF_CTRL", .addr = A_GEM1_REF_CTRL, + .reset = 0xe000a00, + .rsvd = 0xf1fc00f8, + },{ .name = "GEM_TSU_REF_CTRL", .addr = A_GEM_TSU_REF_CTRL, + .reset = 0x300, + .rsvd = 0xfdfc00f8, + },{ .name = "USB0_BUS_REF_CTRL", .addr = A_USB0_BUS_REF_CTRL, + .reset = 0x2001900, + .rsvd = 0xfdfc00f8, + },{ .name = "UART0_REF_CTRL", .addr = A_UART0_REF_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "UART1_REF_CTRL", .addr = A_UART1_REF_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "SPI0_REF_CTRL", .addr = A_SPI0_REF_CTRL, + .reset = 0x600, + .rsvd = 0xfdfc00f8, + },{ .name = "SPI1_REF_CTRL", .addr = A_SPI1_REF_CTRL, + .reset = 0x600, + .rsvd = 0xfdfc00f8, + },{ .name = "CAN0_REF_CTRL", .addr = A_CAN0_REF_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "CAN1_REF_CTRL", .addr = A_CAN1_REF_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "I2C0_REF_CTRL", .addr = A_I2C0_REF_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "I2C1_REF_CTRL", .addr = A_I2C1_REF_CTRL, + .reset = 0xc00, + .rsvd = 0xfdfc00f8, + },{ .name = "DBG_LPD_CTRL", .addr = A_DBG_LPD_CTRL, + .reset = 0x300, + .rsvd = 0xfdfc00f8, + },{ .name = "TIMESTAMP_REF_CTRL", .addr = A_TIMESTAMP_REF_CTRL, + .reset = 0x2000c00, + .rsvd = 0xfdfc00f8, + },{ .name = "CRL_SAFETY_CHK", .addr = A_CRL_SAFETY_CHK, + },{ .name = "PSM_REF_CTRL", .addr = A_PSM_REF_CTRL, + .reset = 0xf04, + .rsvd = 0xfffc00f8, + },{ .name = "DBG_TSTMP_CTRL", .addr = A_DBG_TSTMP_CTRL, + .reset = 0x300, + .rsvd = 0xfdfc00f8, + },{ .name = "CPM_TOPSW_REF_CTRL", .addr = A_CPM_TOPSW_REF_CTRL, + .reset = 0x300, + .rsvd = 0xfdfc00f8, + },{ .name = "USB3_DUAL_REF_CTRL", .addr = A_USB3_DUAL_REF_CTRL, + .reset = 0x3c00, + .rsvd = 0xfdfc00f8, + },{ .name = "RST_CPU_R5", .addr = A_RST_CPU_R5, + .reset = 0x17, + .rsvd = 0x8, + .pre_write = crl_rst_r5_prew, + },{ .name = "RST_ADMA", .addr = A_RST_ADMA, + .reset = 0x1, + .pre_write = crl_rst_adma_prew, + },{ .name = "RST_GEM0", .addr = A_RST_GEM0, + .reset = 0x1, + .pre_write = crl_rst_gem0_prew, + },{ .name = "RST_GEM1", .addr = A_RST_GEM1, + .reset = 0x1, + .pre_write = crl_rst_gem1_prew, + },{ .name = "RST_SPARE", .addr = A_RST_SPARE, + .reset = 0x1, + },{ .name = "RST_USB0", .addr = A_RST_USB0, + .reset = 0x1, + .pre_write = crl_rst_usb_prew, + },{ .name = "RST_UART0", .addr = A_RST_UART0, + .reset = 0x1, + .pre_write = crl_rst_uart0_prew, + },{ .name = "RST_UART1", .addr = A_RST_UART1, + .reset = 0x1, + .pre_write = crl_rst_uart1_prew, + },{ .name = "RST_SPI0", .addr = A_RST_SPI0, + .reset = 0x1, + },{ .name = "RST_SPI1", .addr = A_RST_SPI1, + .reset = 0x1, + },{ .name = "RST_CAN0", .addr = A_RST_CAN0, + .reset = 0x1, + },{ .name = "RST_CAN1", .addr = A_RST_CAN1, + .reset = 0x1, + },{ .name = "RST_I2C0", .addr = A_RST_I2C0, + .reset = 0x1, + },{ .name = "RST_I2C1", .addr = A_RST_I2C1, + .reset = 0x1, + },{ .name = "RST_DBG_LPD", .addr = A_RST_DBG_LPD, + .reset = 0x33, + .rsvd = 0xcc, + },{ .name = "RST_GPIO", .addr = A_RST_GPIO, + .reset = 0x1, + },{ .name = "RST_TTC", .addr = A_RST_TTC, + .reset = 0xf, + },{ .name = "RST_TIMESTAMP", .addr = A_RST_TIMESTAMP, + .reset = 0x1, + },{ .name = "RST_SWDT", .addr = A_RST_SWDT, + .reset = 0x1, + },{ .name = "RST_OCM", .addr = A_RST_OCM, + },{ .name = "RST_IPI", .addr = A_RST_IPI, + },{ .name = "RST_FPD", .addr = A_RST_FPD, + .reset = 0x3, + },{ .name = "PSM_RST_MODE", .addr = A_PSM_RST_MODE, + .reset = 0x1, + .rsvd = 0xf8, + } +}; + +static void crl_reset_enter(Object *obj, ResetType type) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } +} + +static void crl_reset_hold(Object *obj) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); + + crl_update_irq(s); +} + +static const MemoryRegionOps crl_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void crl_init(Object *obj) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + int i; + + s->reg_array = + register_init_block32(DEVICE(obj), crl_regs_info, + ARRAY_SIZE(crl_regs_info), + s->regs_info, s->regs, + &crl_ops, + XLNX_VERSAL_CRL_ERR_DEBUG, + CRL_R_MAX * 4); + sysbus_init_mmio(sbd, &s->reg_array->mem); + sysbus_init_irq(sbd, &s->irq); + + for (i = 0; i < ARRAY_SIZE(s->cfg.cpu_r5); ++i) { + object_property_add_link(obj, "cpu_r5[*]", TYPE_ARM_CPU, + (Object **)&s->cfg.cpu_r5[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.adma); ++i) { + object_property_add_link(obj, "adma[*]", TYPE_DEVICE, + (Object **)&s->cfg.adma[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.uart); ++i) { + object_property_add_link(obj, "uart[*]", TYPE_DEVICE, + (Object **)&s->cfg.uart[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + for (i = 0; i < ARRAY_SIZE(s->cfg.gem); ++i) { + object_property_add_link(obj, "gem[*]", TYPE_DEVICE, + (Object **)&s->cfg.gem[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + object_property_add_link(obj, "usb", TYPE_DEVICE, + (Object **)&s->cfg.gem[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); +} + +static void crl_finalize(Object *obj) +{ + XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); + register_finalize_block(s->reg_array); +} + +static const VMStateDescription vmstate_crl = { + .name = TYPE_XLNX_VERSAL_CRL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxVersalCRL, CRL_R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static void crl_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_crl; + + rc->phases.enter = crl_reset_enter; + rc->phases.hold = crl_reset_hold; +} + +static const TypeInfo crl_info = { + .name = TYPE_XLNX_VERSAL_CRL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCRL), + .class_init = crl_class_init, + .instance_init = crl_init, + .instance_finalize = crl_finalize, +}; + +static void crl_register_types(void) +{ + type_register_static(&crl_info); +} + +type_init(crl_register_types) diff --git a/hw/misc/xlnx-zynqmp-apu-ctrl.c b/hw/misc/xlnx-zynqmp-apu-ctrl.c new file mode 100644 index 0000000000..20de23cf67 --- /dev/null +++ b/hw/misc/xlnx-zynqmp-apu-ctrl.c @@ -0,0 +1,253 @@ +/* + * QEMU model of the ZynqMP APU Control. + * + * Copyright (c) 2013-2022 Xilinx Inc + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Written by Peter Crosthwaite and + * Edgar E. Iglesias + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/register.h" + +#include "qemu/bitops.h" +#include "qapi/qmp/qerror.h" + +#include "hw/misc/xlnx-zynqmp-apu-ctrl.h" + +#ifndef XILINX_ZYNQMP_APU_ERR_DEBUG +#define XILINX_ZYNQMP_APU_ERR_DEBUG 0 +#endif + +static void update_wfi_out(void *opaque) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(opaque); + unsigned int i, wfi_pending; + + wfi_pending = s->cpu_pwrdwn_req & s->cpu_in_wfi; + for (i = 0; i < APU_MAX_CPU; i++) { + qemu_set_irq(s->wfi_out[i], !!(wfi_pending & (1 << i))); + } +} + +static void zynqmp_apu_rvbar_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque); + int i; + + for (i = 0; i < APU_MAX_CPU; ++i) { + uint64_t rvbar = s->regs[R_RVBARADDR0L + 2 * i] + + ((uint64_t)s->regs[R_RVBARADDR0H + 2 * i] << 32); + if (s->cpus[i]) { + object_property_set_int(OBJECT(s->cpus[i]), "rvbar", rvbar, + &error_abort); + } + } +} + +static void zynqmp_apu_pwrctl_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque); + unsigned int i, new; + + for (i = 0; i < APU_MAX_CPU; i++) { + new = val & (1 << i); + /* Check if CPU's CPUPWRDNREQ has changed. If yes, update GPIOs. */ + if (new != (s->cpu_pwrdwn_req & (1 << i))) { + qemu_set_irq(s->cpu_power_status[i], !!new); + } + s->cpu_pwrdwn_req &= ~(1 << i); + s->cpu_pwrdwn_req |= new; + } + update_wfi_out(s); +} + +static void imr_update_irq(XlnxZynqMPAPUCtrl *s) +{ + bool pending = s->regs[R_ISR] & ~s->regs[R_IMR]; + qemu_set_irq(s->irq_imr, pending); +} + +static void isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque); + imr_update_irq(s); +} + +static uint64_t ien_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque); + uint32_t val = val64; + + s->regs[R_IMR] &= ~val; + imr_update_irq(s); + return 0; +} + +static uint64_t ids_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque); + uint32_t val = val64; + + s->regs[R_IMR] |= val; + imr_update_irq(s); + return 0; +} + +static const RegisterAccessInfo zynqmp_apu_regs_info[] = { +#define RVBAR_REGDEF(n) \ + { .name = "RVBAR CPU " #n " Low", .addr = A_RVBARADDR ## n ## L, \ + .reset = 0xffff0000ul, \ + .post_write = zynqmp_apu_rvbar_post_write, \ + },{ .name = "RVBAR CPU " #n " High", .addr = A_RVBARADDR ## n ## H, \ + .post_write = zynqmp_apu_rvbar_post_write, \ + } + { .name = "ERR_CTRL", .addr = A_APU_ERR_CTRL, + },{ .name = "ISR", .addr = A_ISR, + .w1c = 0x1, + .post_write = isr_postw, + },{ .name = "IMR", .addr = A_IMR, + .reset = 0x1, + .ro = 0x1, + },{ .name = "IEN", .addr = A_IEN, + .pre_write = ien_prew, + },{ .name = "IDS", .addr = A_IDS, + .pre_write = ids_prew, + },{ .name = "CONFIG_0", .addr = A_CONFIG_0, + .reset = 0xf0f, + },{ .name = "CONFIG_1", .addr = A_CONFIG_1, + }, + RVBAR_REGDEF(0), + RVBAR_REGDEF(1), + RVBAR_REGDEF(2), + RVBAR_REGDEF(3), + { .name = "ACE_CTRL", .addr = A_ACE_CTRL, + .reset = 0xf000f, + },{ .name = "SNOOP_CTRL", .addr = A_SNOOP_CTRL, + },{ .name = "PWRCTL", .addr = A_PWRCTL, + .post_write = zynqmp_apu_pwrctl_post_write, + },{ .name = "PWRSTAT", .addr = A_PWRSTAT, + .ro = 0x3000f, + } +}; + +static void zynqmp_apu_reset_enter(Object *obj, ResetType type) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); + int i; + + for (i = 0; i < APU_R_MAX; ++i) { + register_reset(&s->regs_info[i]); + } + + s->cpu_pwrdwn_req = 0; + s->cpu_in_wfi = 0; +} + +static void zynqmp_apu_reset_hold(Object *obj) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); + + update_wfi_out(s); + imr_update_irq(s); +} + +static const MemoryRegionOps zynqmp_apu_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void zynqmp_apu_handle_wfi(void *opaque, int irq, int level) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(opaque); + + s->cpu_in_wfi = deposit32(s->cpu_in_wfi, irq, 1, level); + update_wfi_out(s); +} + +static void zynqmp_apu_init(Object *obj) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); + int i; + + s->reg_array = + register_init_block32(DEVICE(obj), zynqmp_apu_regs_info, + ARRAY_SIZE(zynqmp_apu_regs_info), + s->regs_info, s->regs, + &zynqmp_apu_ops, + XILINX_ZYNQMP_APU_ERR_DEBUG, + APU_R_MAX * 4); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->reg_array->mem); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq_imr); + + for (i = 0; i < APU_MAX_CPU; ++i) { + g_autofree gchar *prop_name = g_strdup_printf("cpu%d", i); + object_property_add_link(obj, prop_name, TYPE_ARM_CPU, + (Object **)&s->cpus[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } + + /* wfi_out is used to connect to PMU GPIs. */ + qdev_init_gpio_out_named(DEVICE(obj), s->wfi_out, "wfi_out", 4); + /* CPU_POWER_STATUS is used to connect to INTC redirect. */ + qdev_init_gpio_out_named(DEVICE(obj), s->cpu_power_status, + "CPU_POWER_STATUS", 4); + /* wfi_in is used as input from CPUs as wfi request. */ + qdev_init_gpio_in_named(DEVICE(obj), zynqmp_apu_handle_wfi, "wfi_in", 4); +} + +static void zynqmp_apu_finalize(Object *obj) +{ + XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); + register_finalize_block(s->reg_array); +} + +static const VMStateDescription vmstate_zynqmp_apu = { + .name = TYPE_XLNX_ZYNQMP_APU_CTRL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPAPUCtrl, APU_R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static void zynqmp_apu_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_zynqmp_apu; + + rc->phases.enter = zynqmp_apu_reset_enter; + rc->phases.hold = zynqmp_apu_reset_hold; +} + +static const TypeInfo zynqmp_apu_info = { + .name = TYPE_XLNX_ZYNQMP_APU_CTRL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZynqMPAPUCtrl), + .class_init = zynqmp_apu_class_init, + .instance_init = zynqmp_apu_init, + .instance_finalize = zynqmp_apu_finalize, +}; + +static void zynqmp_apu_register_types(void) +{ + type_register_static(&zynqmp_apu_info); +} + +type_init(zynqmp_apu_register_types) diff --git a/hw/misc/xlnx-zynqmp-crf.c b/hw/misc/xlnx-zynqmp-crf.c new file mode 100644 index 0000000000..57bc8cf49a --- /dev/null +++ b/hw/misc/xlnx-zynqmp-crf.c @@ -0,0 +1,266 @@ +/* + * QEMU model of the CRF - Clock Reset FPD. + * + * Copyright (c) 2022 Xilinx Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * Written by Edgar E. Iglesias + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/register.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/misc/xlnx-zynqmp-crf.h" +#include "target/arm/arm-powerctl.h" + +#ifndef XLNX_ZYNQMP_CRF_ERR_DEBUG +#define XLNX_ZYNQMP_CRF_ERR_DEBUG 0 +#endif + +#define CRF_MAX_CPU 4 + +static void ir_update_irq(XlnxZynqMPCRF *s) +{ + bool pending = s->regs[R_IR_STATUS] & ~s->regs[R_IR_MASK]; + qemu_set_irq(s->irq_ir, pending); +} + +static void ir_status_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); + ir_update_irq(s); +} + +static uint64_t ir_enable_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); + uint32_t val = val64; + + s->regs[R_IR_MASK] &= ~val; + ir_update_irq(s); + return 0; +} + +static uint64_t ir_disable_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); + uint32_t val = val64; + + s->regs[R_IR_MASK] |= val; + ir_update_irq(s); + return 0; +} + +static uint64_t rst_fpd_apu_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); + uint32_t val = val64; + uint32_t val_old = s->regs[R_RST_FPD_APU]; + unsigned int i; + + for (i = 0; i < CRF_MAX_CPU; i++) { + uint32_t mask = (1 << (R_RST_FPD_APU_ACPU0_RESET_SHIFT + i)); + + if ((val ^ val_old) & mask) { + if (val & mask) { + arm_set_cpu_off(i); + } else { + arm_set_cpu_on_and_reset(i); + } + } + } + return val64; +} + +static const RegisterAccessInfo crf_regs_info[] = { + { .name = "ERR_CTRL", .addr = A_ERR_CTRL, + },{ .name = "IR_STATUS", .addr = A_IR_STATUS, + .w1c = 0x1, + .post_write = ir_status_postw, + },{ .name = "IR_MASK", .addr = A_IR_MASK, + .reset = 0x1, + .ro = 0x1, + },{ .name = "IR_ENABLE", .addr = A_IR_ENABLE, + .pre_write = ir_enable_prew, + },{ .name = "IR_DISABLE", .addr = A_IR_DISABLE, + .pre_write = ir_disable_prew, + },{ .name = "CRF_WPROT", .addr = A_CRF_WPROT, + },{ .name = "APLL_CTRL", .addr = A_APLL_CTRL, + .reset = 0x12c09, + .rsvd = 0xf88c80f6, + },{ .name = "APLL_CFG", .addr = A_APLL_CFG, + .rsvd = 0x1801210, + },{ .name = "APLL_FRAC_CFG", .addr = A_APLL_FRAC_CFG, + .rsvd = 0x7e330000, + },{ .name = "DPLL_CTRL", .addr = A_DPLL_CTRL, + .reset = 0x2c09, + .rsvd = 0xf88c80f6, + },{ .name = "DPLL_CFG", .addr = A_DPLL_CFG, + .rsvd = 0x1801210, + },{ .name = "DPLL_FRAC_CFG", .addr = A_DPLL_FRAC_CFG, + .rsvd = 0x7e330000, + },{ .name = "VPLL_CTRL", .addr = A_VPLL_CTRL, + .reset = 0x12809, + .rsvd = 0xf88c80f6, + },{ .name = "VPLL_CFG", .addr = A_VPLL_CFG, + .rsvd = 0x1801210, + },{ .name = "VPLL_FRAC_CFG", .addr = A_VPLL_FRAC_CFG, + .rsvd = 0x7e330000, + },{ .name = "PLL_STATUS", .addr = A_PLL_STATUS, + .reset = 0x3f, + .rsvd = 0xc0, + .ro = 0x3f, + },{ .name = "APLL_TO_LPD_CTRL", .addr = A_APLL_TO_LPD_CTRL, + .reset = 0x400, + .rsvd = 0xc0ff, + },{ .name = "DPLL_TO_LPD_CTRL", .addr = A_DPLL_TO_LPD_CTRL, + .reset = 0x400, + .rsvd = 0xc0ff, + },{ .name = "VPLL_TO_LPD_CTRL", .addr = A_VPLL_TO_LPD_CTRL, + .reset = 0x400, + .rsvd = 0xc0ff, + },{ .name = "ACPU_CTRL", .addr = A_ACPU_CTRL, + .reset = 0x3000400, + .rsvd = 0xfcffc0f8, + },{ .name = "DBG_TRACE_CTRL", .addr = A_DBG_TRACE_CTRL, + .reset = 0x2500, + .rsvd = 0xfeffc0f8, + },{ .name = "DBG_FPD_CTRL", .addr = A_DBG_FPD_CTRL, + .reset = 0x1002500, + .rsvd = 0xfeffc0f8, + },{ .name = "DP_VIDEO_REF_CTRL", .addr = A_DP_VIDEO_REF_CTRL, + .reset = 0x1002300, + .rsvd = 0xfec0c0f8, + },{ .name = "DP_AUDIO_REF_CTRL", .addr = A_DP_AUDIO_REF_CTRL, + .reset = 0x1032300, + .rsvd = 0xfec0c0f8, + },{ .name = "DP_STC_REF_CTRL", .addr = A_DP_STC_REF_CTRL, + .reset = 0x1203200, + .rsvd = 0xfec0c0f8, + },{ .name = "DDR_CTRL", .addr = A_DDR_CTRL, + .reset = 0x1000500, + .rsvd = 0xfeffc0f8, + },{ .name = "GPU_REF_CTRL", .addr = A_GPU_REF_CTRL, + .reset = 0x1500, + .rsvd = 0xf8ffc0f8, + },{ .name = "SATA_REF_CTRL", .addr = A_SATA_REF_CTRL, + .reset = 0x1001600, + .rsvd = 0xfeffc0f8, + },{ .name = "PCIE_REF_CTRL", .addr = A_PCIE_REF_CTRL, + .reset = 0x1500, + .rsvd = 0xfeffc0f8, + },{ .name = "GDMA_REF_CTRL", .addr = A_GDMA_REF_CTRL, + .reset = 0x1000500, + .rsvd = 0xfeffc0f8, + },{ .name = "DPDMA_REF_CTRL", .addr = A_DPDMA_REF_CTRL, + .reset = 0x1000500, + .rsvd = 0xfeffc0f8, + },{ .name = "TOPSW_MAIN_CTRL", .addr = A_TOPSW_MAIN_CTRL, + .reset = 0x1000400, + .rsvd = 0xfeffc0f8, + },{ .name = "TOPSW_LSBUS_CTRL", .addr = A_TOPSW_LSBUS_CTRL, + .reset = 0x1000800, + .rsvd = 0xfeffc0f8, + },{ .name = "DBG_TSTMP_CTRL", .addr = A_DBG_TSTMP_CTRL, + .reset = 0xa00, + .rsvd = 0xffffc0f8, + }, + { .name = "RST_FPD_TOP", .addr = A_RST_FPD_TOP, + .reset = 0xf9ffe, + .rsvd = 0xf06001, + },{ .name = "RST_FPD_APU", .addr = A_RST_FPD_APU, + .reset = 0x3d0f, + .rsvd = 0xc2f0, + .pre_write = rst_fpd_apu_prew, + },{ .name = "RST_DDR_SS", .addr = A_RST_DDR_SS, + .reset = 0xf, + .rsvd = 0xf3, + } +}; + +static void crf_reset_enter(Object *obj, ResetType type) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } +} + +static void crf_reset_hold(Object *obj) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); + ir_update_irq(s); +} + +static const MemoryRegionOps crf_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void crf_init(Object *obj) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + s->reg_array = + register_init_block32(DEVICE(obj), crf_regs_info, + ARRAY_SIZE(crf_regs_info), + s->regs_info, s->regs, + &crf_ops, + XLNX_ZYNQMP_CRF_ERR_DEBUG, + CRF_R_MAX * 4); + sysbus_init_mmio(sbd, &s->reg_array->mem); + sysbus_init_irq(sbd, &s->irq_ir); +} + +static void crf_finalize(Object *obj) +{ + XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); + register_finalize_block(s->reg_array); +} + +static const VMStateDescription vmstate_crf = { + .name = TYPE_XLNX_ZYNQMP_CRF, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCRF, CRF_R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static void crf_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_crf; + rc->phases.enter = crf_reset_enter; + rc->phases.hold = crf_reset_hold; +} + +static const TypeInfo crf_info = { + .name = TYPE_XLNX_ZYNQMP_CRF, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZynqMPCRF), + .class_init = crf_class_init, + .instance_init = crf_init, + .instance_finalize = crf_finalize, +}; + +static void crf_register_types(void) +{ + type_register_static(&crf_info); +} + +type_init(crf_register_types) diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 3ba803e947..e0f76d3eb3 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -247,21 +247,22 @@ int can_sja_accept_filter(CanSJA1000State *s, static void can_display_msg(const char *prefix, const qemu_can_frame *msg) { int i; - FILE *logfile = qemu_log_lock(); + FILE *logfile = qemu_log_trylock(); - qemu_log("%s%03X [%01d] %s %s", - prefix, - msg->can_id & QEMU_CAN_EFF_MASK, - msg->can_dlc, - msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF", - msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT"); + if (logfile) { + fprintf(logfile, "%s%03X [%01d] %s %s", + prefix, + msg->can_id & QEMU_CAN_EFF_MASK, + msg->can_dlc, + msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF", + msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT"); - for (i = 0; i < msg->can_dlc; i++) { - qemu_log(" %02X", msg->data[i]); + for (i = 0; i < msg->can_dlc; i++) { + fprintf(logfile, " %02X", msg->data[i]); + } + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); } - qemu_log("\n"); - qemu_log_flush(); - qemu_log_unlock(logfile); } static void buff2frame_pel(const uint8_t *buff, qemu_can_frame *frame) diff --git a/hw/net/can/ctucan_core.h b/hw/net/can/ctucan_core.h index bbc09ae067..608307a631 100644 --- a/hw/net/can/ctucan_core.h +++ b/hw/net/can/ctucan_core.h @@ -31,7 +31,7 @@ #include "exec/hwaddr.h" #include "net/can_emu.h" -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN #define __LITTLE_ENDIAN_BITFIELD 1 #endif diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index e7fc082518..6d50c39543 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -27,7 +27,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/ptimer.h" diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index 8f08446415..a32589e33b 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "net/checksum.h" #include "qemu/log.h" #include "etsec.h" diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c index 9278fdce0b..7ccd3e5142 100644 --- a/hw/net/msf2-emac.c +++ b/hw/net/msf2-emac.c @@ -29,7 +29,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/log.h" #include "qapi/error.h" #include "hw/registerfields.h" diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index dd6f6e34d3..6ced6775ff 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -68,7 +68,7 @@ static void isa_ne2000_realizefn(DeviceState *dev, Error **errp) ne2000_setup_io(s, DEVICE(isadev), 0x20); isa_register_ioport(isadev, &s->io, isa->iobase); - isa_init_irq(isadev, &s->irq, isa->isairq); + s->irq = isa_get_irq(isadev, isa->isairq); qemu_macaddr_default_if_unset(&s->c.macaddr); ne2000_reset(s); diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c index 9a2328935c..7c86bb52e5 100644 --- a/hw/net/npcm7xx_emc.c +++ b/hw/net/npcm7xx_emc.c @@ -32,7 +32,6 @@ /* For crc32 */ #include -#include "qemu-common.h" #include "hw/irq.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" diff --git a/hw/net/rocker/rocker.h b/hw/net/rocker/rocker.h index 412fa44d01..f85354d9d1 100644 --- a/hw/net/rocker/rocker.h +++ b/hw/net/rocker/rocker.h @@ -30,7 +30,7 @@ fprintf(stderr, "%s ROCKER: " fmt, nowstr, ## __VA_ARGS__);\ } while (0) #else -static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...) +static inline G_GNUC_PRINTF(1, 2) int DPRINTF(const char *fmt, ...) { return 0; } diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 90b4fc63ce..6b65823b4b 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -86,7 +86,7 @@ # define DPRINTF(fmt, ...) \ do { fprintf(stderr, "RTL8139: " fmt, ## __VA_ARGS__); } while (0) #else -static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...) +static inline G_GNUC_PRINTF(1, 2) int DPRINTF(const char *fmt, ...) { return 0; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index cf8ab0f8af..1067e72b39 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -628,17 +628,20 @@ static int virtio_net_max_tx_queue_size(VirtIONet *n) NetClientState *peer = n->nic_conf.peers.ncs[0]; /* - * Backends other than vhost-user don't support max queue size. + * Backends other than vhost-user or vhost-vdpa don't support max queue + * size. */ if (!peer) { return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; } - if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) { + switch(peer->info->type) { + case NET_CLIENT_DRIVER_VHOST_USER: + case NET_CLIENT_DRIVER_VHOST_VDPA: + return VIRTQUEUE_MAX_SIZE; + default: return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; - } - - return VIRTQUEUE_MAX_SIZE; + }; } static int peer_attach(VirtIONet *n, int index) @@ -1867,6 +1870,7 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, err: for (j = 0; j < i; j++) { + virtqueue_detach_element(q->rx_vq, elems[j], lens[j]); g_free(elems[j]); } @@ -1991,7 +1995,7 @@ static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain, VirtioNetRscSeg *seg; hdr_len = chain->n->guest_hdr_len; - seg = g_malloc(sizeof(VirtioNetRscSeg)); + seg = g_new(VirtioNetRscSeg, 1); seg->buf = g_malloc(hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD); memcpy(seg->buf, buf, size); @@ -3439,7 +3443,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) virtio_cleanup(vdev); return; } - n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queue_pairs); + n->vqs = g_new0(VirtIONetQueue, n->max_queue_pairs); n->curr_queue_pairs = 1; n->tx_timeout = n->net_conf.txtimer; diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h index 5b3b76ba7a..bf4f6de74a 100644 --- a/hw/net/vmxnet3.h +++ b/hw/net/vmxnet3.h @@ -35,7 +35,7 @@ #define __le32 uint32_t #define __le64 uint64_t -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define __BIG_ENDIAN_BITFIELD #else #endif @@ -800,7 +800,7 @@ struct Vmxnet3_DriverShared { #undef __le16 #undef __le32 #undef __le64 -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #undef __BIG_ENDIAN_BITFIELD #endif diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c index 3d1205b8bd..91383fb097 100644 --- a/hw/nios2/10m50_devboard.c +++ b/hw/nios2/10m50_devboard.c @@ -27,6 +27,7 @@ #include "hw/sysbus.h" #include "hw/char/serial.h" +#include "hw/intc/nios2_vic.h" #include "hw/qdev-properties.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -36,17 +37,28 @@ #include "boot.h" +struct Nios2MachineState { + MachineState parent_obj; + + MemoryRegion phys_tcm; + MemoryRegion phys_tcm_alias; + MemoryRegion phys_ram; + MemoryRegion phys_ram_alias; + + bool vic; +}; + +#define TYPE_NIOS2_MACHINE MACHINE_TYPE_NAME("10m50-ghrd") +OBJECT_DECLARE_TYPE(Nios2MachineState, MachineClass, NIOS2_MACHINE) + #define BINARY_DEVICE_TREE_FILE "10m50-devboard.dtb" static void nios2_10m50_ghrd_init(MachineState *machine) { + Nios2MachineState *nms = NIOS2_MACHINE(machine); Nios2CPU *cpu; DeviceState *dev; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_tcm = g_new(MemoryRegion, 1); - MemoryRegion *phys_tcm_alias = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram_alias = g_new(MemoryRegion, 1); ram_addr_t tcm_base = 0x0; ram_addr_t tcm_size = 0x1000; /* 1 kiB, but QEMU limit is 4 kiB */ ram_addr_t ram_base = 0x08000000; @@ -55,27 +67,56 @@ static void nios2_10m50_ghrd_init(MachineState *machine) int i; /* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */ - memory_region_init_ram(phys_tcm, NULL, "nios2.tcm", tcm_size, + memory_region_init_ram(&nms->phys_tcm, NULL, "nios2.tcm", tcm_size, &error_abort); - memory_region_init_alias(phys_tcm_alias, NULL, "nios2.tcm.alias", - phys_tcm, 0, tcm_size); - memory_region_add_subregion(address_space_mem, tcm_base, phys_tcm); + memory_region_init_alias(&nms->phys_tcm_alias, NULL, "nios2.tcm.alias", + &nms->phys_tcm, 0, tcm_size); + memory_region_add_subregion(address_space_mem, tcm_base, &nms->phys_tcm); memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base, - phys_tcm_alias); + &nms->phys_tcm_alias); /* Physical DRAM with alias at 0xc0000000 */ - memory_region_init_ram(phys_ram, NULL, "nios2.ram", ram_size, + memory_region_init_ram(&nms->phys_ram, NULL, "nios2.ram", ram_size, &error_abort); - memory_region_init_alias(phys_ram_alias, NULL, "nios2.ram.alias", - phys_ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); + memory_region_init_alias(&nms->phys_ram_alias, NULL, "nios2.ram.alias", + &nms->phys_ram, 0, ram_size); + memory_region_add_subregion(address_space_mem, ram_base, &nms->phys_ram); memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base, - phys_ram_alias); + &nms->phys_ram_alias); - /* Create CPU -- FIXME */ - cpu = NIOS2_CPU(cpu_create(TYPE_NIOS2_CPU)); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i); + /* Create CPU. We need to set eic_present between init and realize. */ + cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU)); + + /* Enable the External Interrupt Controller within the CPU. */ + cpu->eic_present = nms->vic; + + /* Configure new exception vectors. */ + cpu->reset_addr = 0xd4000000; + cpu->exception_addr = 0xc8000120; + cpu->fast_tlb_miss_addr = 0xc0000100; + + qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + + if (nms->vic) { + DeviceState *dev = qdev_new(TYPE_NIOS2_VIC); + MemoryRegion *dev_mr; + qemu_irq cpu_irq; + + object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + cpu_irq = qdev_get_gpio_in_named(DEVICE(cpu), "EIC", 0); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq); + for (int i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(dev, i); + } + + dev_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(address_space_mem, 0x18002000, dev_mr); + } else { + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i); + } } /* Register: Altera 16550 UART */ @@ -96,20 +137,44 @@ static void nios2_10m50_ghrd_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xe0000880); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[5]); - /* Configure new exception vectors and reset CPU for it to take effect. */ - cpu->reset_addr = 0xd4000000; - cpu->exception_addr = 0xc8000120; - cpu->fast_tlb_miss_addr = 0xc0000100; - nios2_load_kernel(cpu, ram_base, ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, NULL); } -static void nios2_10m50_ghrd_machine_init(struct MachineClass *mc) +static bool get_vic(Object *obj, Error **errp) { + Nios2MachineState *nms = NIOS2_MACHINE(obj); + return nms->vic; +} + +static void set_vic(Object *obj, bool value, Error **errp) +{ + Nios2MachineState *nms = NIOS2_MACHINE(obj); + nms->vic = value; +} + +static void nios2_10m50_ghrd_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "Altera 10M50 GHRD Nios II design"; mc->init = nios2_10m50_ghrd_init; mc->is_default = true; + + object_class_property_add_bool(oc, "vic", get_vic, set_vic); + object_class_property_set_description(oc, "vic", + "Set on/off to enable/disable the Vectored Interrupt Controller"); } -DEFINE_MACHINE("10m50-ghrd", nios2_10m50_ghrd_machine_init); +static const TypeInfo nios2_10m50_ghrd_type_info = { + .name = TYPE_NIOS2_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(Nios2MachineState), + .class_init = nios2_10m50_ghrd_class_init, +}; + +static void nios2_10m50_ghrd_type_init(void) +{ + type_register_static(&nios2_10m50_ghrd_type_info); +} +type_init(nios2_10m50_ghrd_type_init); diff --git a/hw/nios2/Kconfig b/hw/nios2/Kconfig index b10ea640da..4748ae27b6 100644 --- a/hw/nios2/Kconfig +++ b/hw/nios2/Kconfig @@ -3,6 +3,7 @@ config NIOS2_10M50 select NIOS2 select SERIAL select ALTERA_TIMER + select NIOS2_VIC config NIOS2_GENERIC_NOMMU bool diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c index 5b3e4efed5..07b8d87633 100644 --- a/hw/nios2/boot.c +++ b/hw/nios2/boot.c @@ -30,7 +30,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -140,7 +139,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, uint64_t entry, high; int big_endian = 0; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN big_endian = 1; #endif diff --git a/hw/nios2/generic_nommu.c b/hw/nios2/generic_nommu.c index fbc18dbd04..48edb3ae37 100644 --- a/hw/nios2/generic_nommu.c +++ b/hw/nios2/generic_nommu.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu-common.h" #include "hw/char/serial.h" #include "hw/boards.h" diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 98aac98bef..03760ddeae 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -163,6 +163,7 @@ #include "migration/vmstate.h" #include "nvme.h" +#include "dif.h" #include "trace.h" #define NVME_MAX_IOQPAIRS 0xffff @@ -195,6 +196,7 @@ static const bool nvme_feature_support[NVME_FID_MAX] = { [NVME_WRITE_ATOMICITY] = true, [NVME_ASYNCHRONOUS_EVENT_CONF] = true, [NVME_TIMESTAMP] = true, + [NVME_HOST_BEHAVIOR_SUPPORT] = true, [NVME_COMMAND_SET_PROFILE] = true, }; @@ -205,6 +207,7 @@ static const uint32_t nvme_feature_cap[NVME_FID_MAX] = { [NVME_NUMBER_OF_QUEUES] = NVME_FEAT_CAP_CHANGE, [NVME_ASYNCHRONOUS_EVENT_CONF] = NVME_FEAT_CAP_CHANGE, [NVME_TIMESTAMP] = NVME_FEAT_CAP_CHANGE, + [NVME_HOST_BEHAVIOR_SUPPORT] = NVME_FEAT_CAP_CHANGE, [NVME_COMMAND_SET_PROFILE] = NVME_FEAT_CAP_CHANGE, }; @@ -1065,7 +1068,8 @@ static uint16_t nvme_map_data(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req) size_t len = nvme_l2b(ns, nlb); uint16_t status; - if (nvme_ns_ext(ns) && !(pi && pract && ns->lbaf.ms == 8)) { + if (nvme_ns_ext(ns) && + !(pi && pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) { NvmeSg sg; len += nvme_m2b(ns, nlb); @@ -1244,7 +1248,8 @@ uint16_t nvme_bounce_data(NvmeCtrl *n, void *ptr, uint32_t len, bool pi = !!NVME_ID_NS_DPS_TYPE(ns->id_ns.dps); bool pract = !!(le16_to_cpu(rw->control) & NVME_RW_PRINFO_PRACT); - if (nvme_ns_ext(ns) && !(pi && pract && ns->lbaf.ms == 8)) { + if (nvme_ns_ext(ns) && + !(pi && pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) { return nvme_tx_interleaved(n, &req->sg, ptr, len, ns->lbasz, ns->lbaf.ms, 0, dir); } @@ -2045,9 +2050,12 @@ static void nvme_verify_cb(void *opaque, int ret) uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control)); uint16_t apptag = le16_to_cpu(rw->apptag); uint16_t appmask = le16_to_cpu(rw->appmask); - uint32_t reftag = le32_to_cpu(rw->reftag); + uint64_t reftag = le32_to_cpu(rw->reftag); + uint64_t cdw3 = le32_to_cpu(rw->cdw3); uint16_t status; + reftag |= cdw3 << 32; + trace_pci_nvme_verify_cb(nvme_cid(req), prinfo, apptag, appmask, reftag); if (ret) { @@ -2136,7 +2144,8 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control)); uint16_t apptag = le16_to_cpu(rw->apptag); uint16_t appmask = le16_to_cpu(rw->appmask); - uint32_t reftag = le32_to_cpu(rw->reftag); + uint64_t reftag = le32_to_cpu(rw->reftag); + uint64_t cdw3 = le32_to_cpu(rw->cdw3); struct nvme_compare_ctx *ctx = req->opaque; g_autofree uint8_t *buf = NULL; BlockBackend *blk = ns->blkconf.blk; @@ -2144,6 +2153,8 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) BlockAcctStats *stats = blk_get_stats(blk); uint16_t status = NVME_SUCCESS; + reftag |= cdw3 << 32; + trace_pci_nvme_compare_mdata_cb(nvme_cid(req)); if (ret) { @@ -2181,7 +2192,7 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) * tuple. */ if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) { - pil = ns->lbaf.ms - sizeof(NvmeDifTuple); + pil = ns->lbaf.ms - nvme_pi_tuple_size(ns); } for (bufp = buf; mbufp < end; bufp += ns->lbaf.ms, mbufp += ns->lbaf.ms) { @@ -2522,7 +2533,8 @@ typedef struct NvmeCopyAIOCB { QEMUBH *bh; int ret; - NvmeCopySourceRange *ranges; + void *ranges; + unsigned int format; int nr; int idx; @@ -2533,7 +2545,7 @@ typedef struct NvmeCopyAIOCB { BlockAcctCookie write; } acct; - uint32_t reftag; + uint64_t reftag; uint64_t slba; NvmeZone *zone; @@ -2587,13 +2599,101 @@ static void nvme_copy_bh(void *opaque) static void nvme_copy_cb(void *opaque, int ret); +static void nvme_copy_source_range_parse_format0(void *ranges, int idx, + uint64_t *slba, uint32_t *nlb, + uint16_t *apptag, + uint16_t *appmask, + uint64_t *reftag) +{ + NvmeCopySourceRangeFormat0 *_ranges = ranges; + + if (slba) { + *slba = le64_to_cpu(_ranges[idx].slba); + } + + if (nlb) { + *nlb = le16_to_cpu(_ranges[idx].nlb) + 1; + } + + if (apptag) { + *apptag = le16_to_cpu(_ranges[idx].apptag); + } + + if (appmask) { + *appmask = le16_to_cpu(_ranges[idx].appmask); + } + + if (reftag) { + *reftag = le32_to_cpu(_ranges[idx].reftag); + } +} + +static void nvme_copy_source_range_parse_format1(void *ranges, int idx, + uint64_t *slba, uint32_t *nlb, + uint16_t *apptag, + uint16_t *appmask, + uint64_t *reftag) +{ + NvmeCopySourceRangeFormat1 *_ranges = ranges; + + if (slba) { + *slba = le64_to_cpu(_ranges[idx].slba); + } + + if (nlb) { + *nlb = le16_to_cpu(_ranges[idx].nlb) + 1; + } + + if (apptag) { + *apptag = le16_to_cpu(_ranges[idx].apptag); + } + + if (appmask) { + *appmask = le16_to_cpu(_ranges[idx].appmask); + } + + if (reftag) { + *reftag = 0; + + *reftag |= (uint64_t)_ranges[idx].sr[4] << 40; + *reftag |= (uint64_t)_ranges[idx].sr[5] << 32; + *reftag |= (uint64_t)_ranges[idx].sr[6] << 24; + *reftag |= (uint64_t)_ranges[idx].sr[7] << 16; + *reftag |= (uint64_t)_ranges[idx].sr[8] << 8; + *reftag |= (uint64_t)_ranges[idx].sr[9]; + } +} + +static void nvme_copy_source_range_parse(void *ranges, int idx, uint8_t format, + uint64_t *slba, uint32_t *nlb, + uint16_t *apptag, uint16_t *appmask, + uint64_t *reftag) +{ + switch (format) { + case NVME_COPY_FORMAT_0: + nvme_copy_source_range_parse_format0(ranges, idx, slba, nlb, apptag, + appmask, reftag); + break; + + case NVME_COPY_FORMAT_1: + nvme_copy_source_range_parse_format1(ranges, idx, slba, nlb, apptag, + appmask, reftag); + break; + + default: + abort(); + } +} + static void nvme_copy_out_completed_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; - NvmeCopySourceRange *range = &iocb->ranges[iocb->idx]; - uint32_t nlb = le32_to_cpu(range->nlb) + 1; + uint32_t nlb; + + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, + &nlb, NULL, NULL, NULL); if (ret < 0) { iocb->ret = ret; @@ -2617,7 +2717,6 @@ static void nvme_copy_out_cb(void *opaque, int ret) NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; - NvmeCopySourceRange *range; uint32_t nlb; size_t mlen; uint8_t *mbounce; @@ -2634,8 +2733,8 @@ static void nvme_copy_out_cb(void *opaque, int ret) return; } - range = &iocb->ranges[iocb->idx]; - nlb = le32_to_cpu(range->nlb) + 1; + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, + &nlb, NULL, NULL, NULL); mlen = nvme_m2b(ns, nlb); mbounce = iocb->bounce + nvme_l2b(ns, nlb); @@ -2658,8 +2757,10 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; - NvmeCopySourceRange *range; uint32_t nlb; + uint64_t slba; + uint16_t apptag, appmask; + uint64_t reftag; size_t len; uint16_t status; @@ -2670,8 +2771,8 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) goto out; } - range = &iocb->ranges[iocb->idx]; - nlb = le32_to_cpu(range->nlb) + 1; + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, + &nlb, &apptag, &appmask, &reftag); len = nvme_l2b(ns, nlb); trace_pci_nvme_copy_out(iocb->slba, nlb); @@ -2682,11 +2783,6 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); - uint16_t apptag = le16_to_cpu(range->apptag); - uint16_t appmask = le16_to_cpu(range->appmask); - uint32_t reftag = le32_to_cpu(range->reftag); - - uint64_t slba = le64_to_cpu(range->slba); size_t mlen = nvme_m2b(ns, nlb); uint8_t *mbounce = iocb->bounce + nvme_l2b(ns, nlb); @@ -2759,7 +2855,6 @@ static void nvme_copy_in_cb(void *opaque, int ret) NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; - NvmeCopySourceRange *range; uint64_t slba; uint32_t nlb; @@ -2775,9 +2870,8 @@ static void nvme_copy_in_cb(void *opaque, int ret) return; } - range = &iocb->ranges[iocb->idx]; - slba = le64_to_cpu(range->slba); - nlb = le32_to_cpu(range->nlb) + 1; + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, + &nlb, NULL, NULL, NULL); qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(ns, nlb), @@ -2797,7 +2891,6 @@ static void nvme_copy_cb(void *opaque, int ret) NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; - NvmeCopySourceRange *range; uint64_t slba; uint32_t nlb; size_t len; @@ -2814,9 +2907,8 @@ static void nvme_copy_cb(void *opaque, int ret) goto done; } - range = &iocb->ranges[iocb->idx]; - slba = le64_to_cpu(range->slba); - nlb = le32_to_cpu(range->nlb) + 1; + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, + &nlb, NULL, NULL, NULL); len = nvme_l2b(ns, nlb); trace_pci_nvme_copy_source_range(slba, nlb); @@ -2872,6 +2964,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) uint8_t format = copy->control[0] & 0xf; uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); + size_t len = sizeof(NvmeCopySourceRangeFormat0); uint16_t status; @@ -2897,10 +2990,18 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) goto invalid; } - iocb->ranges = g_new(NvmeCopySourceRange, nr); + if (ns->pif && format != 0x1) { + status = NVME_INVALID_FORMAT | NVME_DNR; + goto invalid; + } - status = nvme_h2c(n, (uint8_t *)iocb->ranges, - sizeof(NvmeCopySourceRange) * nr, req); + if (ns->pif) { + len = sizeof(NvmeCopySourceRangeFormat1); + } + + iocb->format = format; + iocb->ranges = g_malloc_n(nr, len); + status = nvme_h2c(n, (uint8_t *)iocb->ranges, len * nr, req); if (status) { goto invalid; } @@ -2926,6 +3027,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) iocb->nr = nr; iocb->idx = 0; iocb->reftag = le32_to_cpu(copy->reftag); + iocb->reftag |= (uint64_t)le32_to_cpu(copy->cdw3) << 32; iocb->bounce = g_malloc_n(le16_to_cpu(ns->id_ns.mssrl), ns->lbasz + ns->lbaf.ms); @@ -3164,7 +3266,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req) if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { bool pract = prinfo & NVME_PRINFO_PRACT; - if (pract && ns->lbaf.ms == 8) { + if (pract && ns->lbaf.ms == nvme_pi_tuple_size(ns)) { mapped_size = data_size; } } @@ -3241,7 +3343,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { bool pract = prinfo & NVME_PRINFO_PRACT; - if (pract && ns->lbaf.ms == 8) { + if (pract && ns->lbaf.ms == nvme_pi_tuple_size(ns)) { mapped_size -= nvme_m2b(ns, nlb); } } @@ -4712,7 +4814,8 @@ static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req, } if (c->csi == NVME_CSI_NVM) { - return nvme_rpt_empty_id_struct(n, req); + return nvme_c2h(n, (uint8_t *)&ns->id_ns_nvm, sizeof(NvmeIdNsNvm), + req); } else if (c->csi == NVME_CSI_ZONED && ns->csi == NVME_CSI_ZONED) { return nvme_c2h(n, (uint8_t *)ns->id_ns_zoned, sizeof(NvmeIdNsZoned), req); @@ -5090,6 +5193,9 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req) goto out; case NVME_TIMESTAMP: return nvme_get_feature_timestamp(n, req); + case NVME_HOST_BEHAVIOR_SUPPORT: + return nvme_c2h(n, (uint8_t *)&n->features.hbs, + sizeof(n->features.hbs), req); default: break; } @@ -5159,6 +5265,7 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) uint32_t nsid = le32_to_cpu(cmd->nsid); uint8_t fid = NVME_GETSETFEAT_FID(dw10); uint8_t save = NVME_SETFEAT_SAVE(dw10); + uint16_t status; int i; trace_pci_nvme_setfeat(nvme_cid(req), nsid, fid, save, dw11); @@ -5280,6 +5387,27 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) break; case NVME_TIMESTAMP: return nvme_set_feature_timestamp(n, req); + case NVME_HOST_BEHAVIOR_SUPPORT: + status = nvme_h2c(n, (uint8_t *)&n->features.hbs, + sizeof(n->features.hbs), req); + if (status) { + return status; + } + + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { + ns = nvme_ns(n, i); + + if (!ns) { + continue; + } + + ns->id_ns.nlbaf = ns->nlbaf - 1; + if (!n->features.hbs.lbafee) { + ns->id_ns.nlbaf = MIN(ns->id_ns.nlbaf, 15); + } + } + + return status; case NVME_COMMAND_SET_PROFILE: if (dw11 & 0x1ff) { trace_pci_nvme_err_invalid_iocsci(dw11 & 0x1ff); @@ -5443,6 +5571,11 @@ typedef struct NvmeFormatAIOCB { uint32_t nsid; bool broadcast; int64_t offset; + + uint8_t lbaf; + uint8_t mset; + uint8_t pi; + uint8_t pil; } NvmeFormatAIOCB; static void nvme_format_bh(void *opaque); @@ -5462,18 +5595,16 @@ static const AIOCBInfo nvme_format_aiocb_info = { .get_aio_context = nvme_get_aio_context, }; -static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd) +static void nvme_format_set(NvmeNamespace *ns, uint8_t lbaf, uint8_t mset, + uint8_t pi, uint8_t pil) { - uint32_t dw10 = le32_to_cpu(cmd->cdw10); - uint8_t lbaf = dw10 & 0xf; - uint8_t pi = (dw10 >> 5) & 0x7; - uint8_t mset = (dw10 >> 4) & 0x1; - uint8_t pil = (dw10 >> 8) & 0x1; + uint8_t lbafl = lbaf & 0xf; + uint8_t lbafu = lbaf >> 4; trace_pci_nvme_format_set(ns->params.nsid, lbaf, mset, pi, pil); ns->id_ns.dps = (pil << 3) | pi; - ns->id_ns.flbas = lbaf | (mset << 4); + ns->id_ns.flbas = (lbafu << 5) | (mset << 4) | lbafl; nvme_ns_init_format(ns); } @@ -5481,7 +5612,6 @@ static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd) static void nvme_format_ns_cb(void *opaque, int ret) { NvmeFormatAIOCB *iocb = opaque; - NvmeRequest *req = iocb->req; NvmeNamespace *ns = iocb->ns; int bytes; @@ -5503,7 +5633,7 @@ static void nvme_format_ns_cb(void *opaque, int ret) return; } - nvme_format_set(ns, &req->cmd); + nvme_format_set(ns, iocb->lbaf, iocb->mset, iocb->pi, iocb->pil); ns->status = 0x0; iocb->ns = NULL; iocb->offset = 0; @@ -5523,7 +5653,7 @@ static uint16_t nvme_format_check(NvmeNamespace *ns, uint8_t lbaf, uint8_t pi) return NVME_INVALID_FORMAT | NVME_DNR; } - if (pi && (ns->id_ns.lbaf[lbaf].ms < sizeof(NvmeDifTuple))) { + if (pi && (ns->id_ns.lbaf[lbaf].ms < nvme_pi_tuple_size(ns))) { return NVME_INVALID_FORMAT | NVME_DNR; } @@ -5586,6 +5716,12 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req) { NvmeFormatAIOCB *iocb; uint32_t nsid = le32_to_cpu(req->cmd.nsid); + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10); + uint8_t lbaf = dw10 & 0xf; + uint8_t mset = (dw10 >> 4) & 0x1; + uint8_t pi = (dw10 >> 5) & 0x7; + uint8_t pil = (dw10 >> 8) & 0x1; + uint8_t lbafu = (dw10 >> 12) & 0x3; uint16_t status; iocb = qemu_aio_get(&nvme_format_aiocb_info, NULL, nvme_misc_cb, req); @@ -5595,9 +5731,17 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req) iocb->ret = 0; iocb->ns = NULL; iocb->nsid = 0; + iocb->lbaf = lbaf; + iocb->mset = mset; + iocb->pi = pi; + iocb->pil = pil; iocb->broadcast = (nsid == NVME_NSID_BROADCAST); iocb->offset = 0; + if (n->features.hbs.lbafee) { + iocb->lbaf |= lbafu << 4; + } + if (!iocb->broadcast) { if (!nvme_nsid_valid(n, nsid)) { status = NVME_INVALID_NSID | NVME_DNR; @@ -6573,6 +6717,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->cntlid = cpu_to_le16(n->cntlid); id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR); + id->ctratt |= cpu_to_le32(NVME_CTRATT_ELBAS); id->rab = 6; @@ -6627,7 +6772,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) */ id->vwc = NVME_VWC_NSID_BROADCAST_SUPPORT | NVME_VWC_PRESENT; - id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0); + id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0 | NVME_OCFS_COPY_FORMAT_1); id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN | NVME_CTRL_SGLS_BITBUCKET); diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c index 5dbd18b2a4..62d885f83e 100644 --- a/hw/nvme/dif.c +++ b/hw/nvme/dif.c @@ -13,13 +13,16 @@ #include "sysemu/block-backend.h" #include "nvme.h" +#include "dif.h" #include "trace.h" uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba, - uint32_t reftag) + uint64_t reftag) { + uint64_t mask = ns->pif ? 0xffffffffffff : 0xffffffff; + if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) && - (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & 0xffffffff) != reftag) { + (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & mask) != reftag) { return NVME_INVALID_PROT_INFO | NVME_DNR; } @@ -27,43 +30,58 @@ uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba, } /* from Linux kernel (crypto/crct10dif_common.c) */ -static uint16_t crc_t10dif(uint16_t crc, const unsigned char *buffer, - size_t len) +static uint16_t crc16_t10dif(uint16_t crc, const unsigned char *buffer, + size_t len) { unsigned int i; for (i = 0; i < len; i++) { - crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff]; + crc = (crc << 8) ^ crc16_t10dif_table[((crc >> 8) ^ buffer[i]) & 0xff]; } return crc; } -void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len, - uint8_t *mbuf, size_t mlen, uint16_t apptag, - uint32_t *reftag) +/* from Linux kernel (lib/crc64.c) */ +static uint64_t crc64_nvme(uint64_t crc, const unsigned char *buffer, + size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + crc = (crc >> 8) ^ crc64_nvme_table[(crc & 0xff) ^ buffer[i]]; + } + + return crc ^ (uint64_t)~0; +} + +static void nvme_dif_pract_generate_dif_crc16(NvmeNamespace *ns, uint8_t *buf, + size_t len, uint8_t *mbuf, + size_t mlen, uint16_t apptag, + uint64_t *reftag) { uint8_t *end = buf + len; int16_t pil = 0; if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) { - pil = ns->lbaf.ms - sizeof(NvmeDifTuple); + pil = ns->lbaf.ms - nvme_pi_tuple_size(ns); } - trace_pci_nvme_dif_pract_generate_dif(len, ns->lbasz, ns->lbasz + pil, - apptag, *reftag); + trace_pci_nvme_dif_pract_generate_dif_crc16(len, ns->lbasz, + ns->lbasz + pil, apptag, + *reftag); for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) { NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil); - uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz); + uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz); if (pil) { - crc = crc_t10dif(crc, mbuf, pil); + crc = crc16_t10dif(crc, mbuf, pil); } - dif->guard = cpu_to_be16(crc); - dif->apptag = cpu_to_be16(apptag); - dif->reftag = cpu_to_be32(*reftag); + dif->g16.guard = cpu_to_be16(crc); + dif->g16.apptag = cpu_to_be16(apptag); + dif->g16.reftag = cpu_to_be32(*reftag); if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) { (*reftag)++; @@ -71,57 +89,114 @@ void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len, } } -static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif, - uint8_t *buf, uint8_t *mbuf, size_t pil, - uint8_t prinfo, uint16_t apptag, - uint16_t appmask, uint32_t reftag) +static void nvme_dif_pract_generate_dif_crc64(NvmeNamespace *ns, uint8_t *buf, + size_t len, uint8_t *mbuf, + size_t mlen, uint16_t apptag, + uint64_t *reftag) +{ + uint8_t *end = buf + len; + int16_t pil = 0; + + if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) { + pil = ns->lbaf.ms - 16; + } + + trace_pci_nvme_dif_pract_generate_dif_crc64(len, ns->lbasz, + ns->lbasz + pil, apptag, + *reftag); + + for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) { + NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil); + uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz); + + if (pil) { + crc = crc64_nvme(crc, mbuf, pil); + } + + dif->g64.guard = cpu_to_be64(crc); + dif->g64.apptag = cpu_to_be16(apptag); + + dif->g64.sr[0] = *reftag >> 40; + dif->g64.sr[1] = *reftag >> 32; + dif->g64.sr[2] = *reftag >> 24; + dif->g64.sr[3] = *reftag >> 16; + dif->g64.sr[4] = *reftag >> 8; + dif->g64.sr[5] = *reftag; + + if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) { + (*reftag)++; + } + } +} + +void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len, + uint8_t *mbuf, size_t mlen, uint16_t apptag, + uint64_t *reftag) +{ + switch (ns->pif) { + case NVME_PI_GUARD_16: + return nvme_dif_pract_generate_dif_crc16(ns, buf, len, mbuf, mlen, + apptag, reftag); + case NVME_PI_GUARD_64: + return nvme_dif_pract_generate_dif_crc64(ns, buf, len, mbuf, mlen, + apptag, reftag); + } + + abort(); +} + +static uint16_t nvme_dif_prchk_crc16(NvmeNamespace *ns, NvmeDifTuple *dif, + uint8_t *buf, uint8_t *mbuf, size_t pil, + uint8_t prinfo, uint16_t apptag, + uint16_t appmask, uint64_t reftag) { switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { case NVME_ID_NS_DPS_TYPE_3: - if (be32_to_cpu(dif->reftag) != 0xffffffff) { + if (be32_to_cpu(dif->g16.reftag) != 0xffffffff) { break; } /* fallthrough */ case NVME_ID_NS_DPS_TYPE_1: case NVME_ID_NS_DPS_TYPE_2: - if (be16_to_cpu(dif->apptag) != 0xffff) { + if (be16_to_cpu(dif->g16.apptag) != 0xffff) { break; } - trace_pci_nvme_dif_prchk_disabled(be16_to_cpu(dif->apptag), - be32_to_cpu(dif->reftag)); + trace_pci_nvme_dif_prchk_disabled_crc16(be16_to_cpu(dif->g16.apptag), + be32_to_cpu(dif->g16.reftag)); return NVME_SUCCESS; } if (prinfo & NVME_PRINFO_PRCHK_GUARD) { - uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz); + uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz); if (pil) { - crc = crc_t10dif(crc, mbuf, pil); + crc = crc16_t10dif(crc, mbuf, pil); } - trace_pci_nvme_dif_prchk_guard(be16_to_cpu(dif->guard), crc); + trace_pci_nvme_dif_prchk_guard_crc16(be16_to_cpu(dif->g16.guard), crc); - if (be16_to_cpu(dif->guard) != crc) { + if (be16_to_cpu(dif->g16.guard) != crc) { return NVME_E2E_GUARD_ERROR; } } if (prinfo & NVME_PRINFO_PRCHK_APP) { - trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->apptag), apptag, + trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g16.apptag), apptag, appmask); - if ((be16_to_cpu(dif->apptag) & appmask) != (apptag & appmask)) { + if ((be16_to_cpu(dif->g16.apptag) & appmask) != (apptag & appmask)) { return NVME_E2E_APP_ERROR; } } if (prinfo & NVME_PRINFO_PRCHK_REF) { - trace_pci_nvme_dif_prchk_reftag(be32_to_cpu(dif->reftag), reftag); + trace_pci_nvme_dif_prchk_reftag_crc16(be32_to_cpu(dif->g16.reftag), + reftag); - if (be32_to_cpu(dif->reftag) != reftag) { + if (be32_to_cpu(dif->g16.reftag) != reftag) { return NVME_E2E_REF_ERROR; } } @@ -129,12 +204,96 @@ static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif, return NVME_SUCCESS; } +static uint16_t nvme_dif_prchk_crc64(NvmeNamespace *ns, NvmeDifTuple *dif, + uint8_t *buf, uint8_t *mbuf, size_t pil, + uint8_t prinfo, uint16_t apptag, + uint16_t appmask, uint64_t reftag) +{ + uint64_t r = 0; + + r |= (uint64_t)dif->g64.sr[0] << 40; + r |= (uint64_t)dif->g64.sr[1] << 32; + r |= (uint64_t)dif->g64.sr[2] << 24; + r |= (uint64_t)dif->g64.sr[3] << 16; + r |= (uint64_t)dif->g64.sr[4] << 8; + r |= (uint64_t)dif->g64.sr[5]; + + switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { + case NVME_ID_NS_DPS_TYPE_3: + if (r != 0xffffffffffff) { + break; + } + + /* fallthrough */ + case NVME_ID_NS_DPS_TYPE_1: + case NVME_ID_NS_DPS_TYPE_2: + if (be16_to_cpu(dif->g64.apptag) != 0xffff) { + break; + } + + trace_pci_nvme_dif_prchk_disabled_crc64(be16_to_cpu(dif->g16.apptag), + r); + + return NVME_SUCCESS; + } + + if (prinfo & NVME_PRINFO_PRCHK_GUARD) { + uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz); + + if (pil) { + crc = crc64_nvme(crc, mbuf, pil); + } + + trace_pci_nvme_dif_prchk_guard_crc64(be64_to_cpu(dif->g64.guard), crc); + + if (be64_to_cpu(dif->g64.guard) != crc) { + return NVME_E2E_GUARD_ERROR; + } + } + + if (prinfo & NVME_PRINFO_PRCHK_APP) { + trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g64.apptag), apptag, + appmask); + + if ((be16_to_cpu(dif->g64.apptag) & appmask) != (apptag & appmask)) { + return NVME_E2E_APP_ERROR; + } + } + + if (prinfo & NVME_PRINFO_PRCHK_REF) { + trace_pci_nvme_dif_prchk_reftag_crc64(r, reftag); + + if (r != reftag) { + return NVME_E2E_REF_ERROR; + } + } + + return NVME_SUCCESS; +} + +static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif, + uint8_t *buf, uint8_t *mbuf, size_t pil, + uint8_t prinfo, uint16_t apptag, + uint16_t appmask, uint64_t reftag) +{ + switch (ns->pif) { + case NVME_PI_GUARD_16: + return nvme_dif_prchk_crc16(ns, dif, buf, mbuf, pil, prinfo, apptag, + appmask, reftag); + case NVME_PI_GUARD_64: + return nvme_dif_prchk_crc64(ns, dif, buf, mbuf, pil, prinfo, apptag, + appmask, reftag); + } + + abort(); +} + uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len, uint8_t *mbuf, size_t mlen, uint8_t prinfo, uint64_t slba, uint16_t apptag, - uint16_t appmask, uint32_t *reftag) + uint16_t appmask, uint64_t *reftag) { - uint8_t *end = buf + len; + uint8_t *bufp, *end = buf + len; int16_t pil = 0; uint16_t status; @@ -144,18 +303,34 @@ uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len, } if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) { - pil = ns->lbaf.ms - sizeof(NvmeDifTuple); + pil = ns->lbaf.ms - nvme_pi_tuple_size(ns); } trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil); - for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) { + for (bufp = buf; bufp < end; bufp += ns->lbasz, mbuf += ns->lbaf.ms) { NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil); - - status = nvme_dif_prchk(ns, dif, buf, mbuf, pil, prinfo, apptag, + status = nvme_dif_prchk(ns, dif, bufp, mbuf, pil, prinfo, apptag, appmask, *reftag); if (status) { - return status; + /* + * The first block of a 'raw' image is always allocated, so we + * cannot reliably know if the block is all zeroes or not. For + * CRC16 this works fine because the T10 CRC16 is 0x0 for all + * zeroes, but the Rocksoft CRC64 is not. Thus, if a guard error is + * detected for the first block, check if it is zeroed and manually + * set the protection information to all ones to disable protection + * information checking. + */ + if (status == NVME_E2E_GUARD_ERROR && slba == 0x0 && bufp == buf) { + g_autofree uint8_t *zeroes = g_malloc0(ns->lbasz); + + if (memcmp(bufp, zeroes, ns->lbasz) == 0) { + memset(mbuf + pil, 0xff, nvme_pi_tuple_size(ns)); + } + } else { + return status; + } } if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) { @@ -183,7 +358,7 @@ uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen, if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) { - pil = ns->lbaf.ms - sizeof(NvmeDifTuple); + pil = ns->lbaf.ms - nvme_pi_tuple_size(ns); } do { @@ -209,7 +384,7 @@ uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen, end = mbufp + mlen; for (; mbufp < end; mbufp += ns->lbaf.ms) { - memset(mbufp + pil, 0xff, sizeof(NvmeDifTuple)); + memset(mbufp + pil, 0xff, nvme_pi_tuple_size(ns)); } } @@ -251,9 +426,12 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret) uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control)); uint16_t apptag = le16_to_cpu(rw->apptag); uint16_t appmask = le16_to_cpu(rw->appmask); - uint32_t reftag = le32_to_cpu(rw->reftag); + uint64_t reftag = le32_to_cpu(rw->reftag); + uint64_t cdw3 = le32_to_cpu(rw->cdw3); uint16_t status; + reftag |= cdw3 << 32; + trace_pci_nvme_dif_rw_check_cb(nvme_cid(req), prinfo, apptag, appmask, reftag); @@ -283,7 +461,7 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret) goto out; } - if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == 8) { + if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == nvme_pi_tuple_size(ns)) { goto out; } @@ -367,11 +545,14 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req) uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control)); uint16_t apptag = le16_to_cpu(rw->apptag); uint16_t appmask = le16_to_cpu(rw->appmask); - uint32_t reftag = le32_to_cpu(rw->reftag); + uint64_t reftag = le32_to_cpu(rw->reftag); + uint64_t cdw3 = le32_to_cpu(rw->cdw3); bool pract = !!(prinfo & NVME_PRINFO_PRACT); NvmeBounceContext *ctx; uint16_t status; + reftag |= cdw3 << 32; + trace_pci_nvme_dif_rw(pract, prinfo); ctx = g_new0(NvmeBounceContext, 1); @@ -387,7 +568,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req) if (pract) { uint8_t *mbuf, *end; - int16_t pil = ns->lbaf.ms - sizeof(NvmeDifTuple); + int16_t pil = ns->lbaf.ms - nvme_pi_tuple_size(ns); status = nvme_check_prinfo(ns, prinfo, slba, reftag); if (status) { @@ -411,8 +592,29 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req) for (; mbuf < end; mbuf += ns->lbaf.ms) { NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil); - dif->apptag = cpu_to_be16(apptag); - dif->reftag = cpu_to_be32(reftag); + switch (ns->pif) { + case NVME_PI_GUARD_16: + dif->g16.apptag = cpu_to_be16(apptag); + dif->g16.reftag = cpu_to_be32(reftag); + + break; + + case NVME_PI_GUARD_64: + dif->g64.guard = cpu_to_be64(0x6482d367eb22b64e); + dif->g64.apptag = cpu_to_be16(apptag); + + dif->g64.sr[0] = reftag >> 40; + dif->g64.sr[1] = reftag >> 32; + dif->g64.sr[2] = reftag >> 24; + dif->g64.sr[3] = reftag >> 16; + dif->g64.sr[4] = reftag >> 8; + dif->g64.sr[5] = reftag; + + break; + + default: + abort(); + } switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { case NVME_ID_NS_DPS_TYPE_1: @@ -427,7 +629,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req) return NVME_NO_COMPLETE; } - if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == 8)) { + if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) { mapped_len += mlen; } @@ -461,7 +663,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req) qemu_iovec_init(&ctx->mdata.iov, 1); qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen); - if (!(pract && ns->lbaf.ms == 8)) { + if (!(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) { status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size, NVME_TX_DIRECTION_TO_DEVICE, req); if (status) { diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h new file mode 100644 index 0000000000..f12e312250 --- /dev/null +++ b/hw/nvme/dif.h @@ -0,0 +1,191 @@ +#ifndef HW_NVME_DIF_H +#define HW_NVME_DIF_H + +/* from Linux kernel (crypto/crct10dif_common.c) */ +static const uint16_t crc16_t10dif_table[256] = { + 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B, + 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6, + 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6, + 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B, + 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1, + 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C, + 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C, + 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781, + 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8, + 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255, + 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925, + 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698, + 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472, + 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF, + 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF, + 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02, + 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA, + 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067, + 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17, + 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA, + 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640, + 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD, + 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D, + 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30, + 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759, + 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4, + 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394, + 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29, + 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3, + 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E, + 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E, + 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 +}; + +#define CRC64_NVME_POLY 0x9A6C9329AC4BC9B5ULL + +static const uint64_t crc64_nvme_table[] = { + 0x0000000000000000ULL, 0x7F6EF0C830358979ULL, + 0xFEDDE190606B12F2ULL, 0x81B31158505E9B8BULL, + 0xC962E5739841B68FULL, 0xB60C15BBA8743FF6ULL, + 0x37BF04E3F82AA47DULL, 0x48D1F42BC81F2D04ULL, + 0xA61CECB46814FE75ULL, 0xD9721C7C5821770CULL, + 0x58C10D24087FEC87ULL, 0x27AFFDEC384A65FEULL, + 0x6F7E09C7F05548FAULL, 0x1010F90FC060C183ULL, + 0x91A3E857903E5A08ULL, 0xEECD189FA00BD371ULL, + 0x78E0FF3B88BE6F81ULL, 0x078E0FF3B88BE6F8ULL, + 0x863D1EABE8D57D73ULL, 0xF953EE63D8E0F40AULL, + 0xB1821A4810FFD90EULL, 0xCEECEA8020CA5077ULL, + 0x4F5FFBD87094CBFCULL, 0x30310B1040A14285ULL, + 0xDEFC138FE0AA91F4ULL, 0xA192E347D09F188DULL, + 0x2021F21F80C18306ULL, 0x5F4F02D7B0F40A7FULL, + 0x179EF6FC78EB277BULL, 0x68F0063448DEAE02ULL, + 0xE943176C18803589ULL, 0x962DE7A428B5BCF0ULL, + 0xF1C1FE77117CDF02ULL, 0x8EAF0EBF2149567BULL, + 0x0F1C1FE77117CDF0ULL, 0x7072EF2F41224489ULL, + 0x38A31B04893D698DULL, 0x47CDEBCCB908E0F4ULL, + 0xC67EFA94E9567B7FULL, 0xB9100A5CD963F206ULL, + 0x57DD12C379682177ULL, 0x28B3E20B495DA80EULL, + 0xA900F35319033385ULL, 0xD66E039B2936BAFCULL, + 0x9EBFF7B0E12997F8ULL, 0xE1D10778D11C1E81ULL, + 0x606216208142850AULL, 0x1F0CE6E8B1770C73ULL, + 0x8921014C99C2B083ULL, 0xF64FF184A9F739FAULL, + 0x77FCE0DCF9A9A271ULL, 0x08921014C99C2B08ULL, + 0x4043E43F0183060CULL, 0x3F2D14F731B68F75ULL, + 0xBE9E05AF61E814FEULL, 0xC1F0F56751DD9D87ULL, + 0x2F3DEDF8F1D64EF6ULL, 0x50531D30C1E3C78FULL, + 0xD1E00C6891BD5C04ULL, 0xAE8EFCA0A188D57DULL, + 0xE65F088B6997F879ULL, 0x9931F84359A27100ULL, + 0x1882E91B09FCEA8BULL, 0x67EC19D339C963F2ULL, + 0xD75ADABD7A6E2D6FULL, 0xA8342A754A5BA416ULL, + 0x29873B2D1A053F9DULL, 0x56E9CBE52A30B6E4ULL, + 0x1E383FCEE22F9BE0ULL, 0x6156CF06D21A1299ULL, + 0xE0E5DE5E82448912ULL, 0x9F8B2E96B271006BULL, + 0x71463609127AD31AULL, 0x0E28C6C1224F5A63ULL, + 0x8F9BD7997211C1E8ULL, 0xF0F5275142244891ULL, + 0xB824D37A8A3B6595ULL, 0xC74A23B2BA0EECECULL, + 0x46F932EAEA507767ULL, 0x3997C222DA65FE1EULL, + 0xAFBA2586F2D042EEULL, 0xD0D4D54EC2E5CB97ULL, + 0x5167C41692BB501CULL, 0x2E0934DEA28ED965ULL, + 0x66D8C0F56A91F461ULL, 0x19B6303D5AA47D18ULL, + 0x980521650AFAE693ULL, 0xE76BD1AD3ACF6FEAULL, + 0x09A6C9329AC4BC9BULL, 0x76C839FAAAF135E2ULL, + 0xF77B28A2FAAFAE69ULL, 0x8815D86ACA9A2710ULL, + 0xC0C42C4102850A14ULL, 0xBFAADC8932B0836DULL, + 0x3E19CDD162EE18E6ULL, 0x41773D1952DB919FULL, + 0x269B24CA6B12F26DULL, 0x59F5D4025B277B14ULL, + 0xD846C55A0B79E09FULL, 0xA72835923B4C69E6ULL, + 0xEFF9C1B9F35344E2ULL, 0x90973171C366CD9BULL, + 0x1124202993385610ULL, 0x6E4AD0E1A30DDF69ULL, + 0x8087C87E03060C18ULL, 0xFFE938B633338561ULL, + 0x7E5A29EE636D1EEAULL, 0x0134D92653589793ULL, + 0x49E52D0D9B47BA97ULL, 0x368BDDC5AB7233EEULL, + 0xB738CC9DFB2CA865ULL, 0xC8563C55CB19211CULL, + 0x5E7BDBF1E3AC9DECULL, 0x21152B39D3991495ULL, + 0xA0A63A6183C78F1EULL, 0xDFC8CAA9B3F20667ULL, + 0x97193E827BED2B63ULL, 0xE877CE4A4BD8A21AULL, + 0x69C4DF121B863991ULL, 0x16AA2FDA2BB3B0E8ULL, + 0xF86737458BB86399ULL, 0x8709C78DBB8DEAE0ULL, + 0x06BAD6D5EBD3716BULL, 0x79D4261DDBE6F812ULL, + 0x3105D23613F9D516ULL, 0x4E6B22FE23CC5C6FULL, + 0xCFD833A67392C7E4ULL, 0xB0B6C36E43A74E9DULL, + 0x9A6C9329AC4BC9B5ULL, 0xE50263E19C7E40CCULL, + 0x64B172B9CC20DB47ULL, 0x1BDF8271FC15523EULL, + 0x530E765A340A7F3AULL, 0x2C608692043FF643ULL, + 0xADD397CA54616DC8ULL, 0xD2BD67026454E4B1ULL, + 0x3C707F9DC45F37C0ULL, 0x431E8F55F46ABEB9ULL, + 0xC2AD9E0DA4342532ULL, 0xBDC36EC59401AC4BULL, + 0xF5129AEE5C1E814FULL, 0x8A7C6A266C2B0836ULL, + 0x0BCF7B7E3C7593BDULL, 0x74A18BB60C401AC4ULL, + 0xE28C6C1224F5A634ULL, 0x9DE29CDA14C02F4DULL, + 0x1C518D82449EB4C6ULL, 0x633F7D4A74AB3DBFULL, + 0x2BEE8961BCB410BBULL, 0x548079A98C8199C2ULL, + 0xD53368F1DCDF0249ULL, 0xAA5D9839ECEA8B30ULL, + 0x449080A64CE15841ULL, 0x3BFE706E7CD4D138ULL, + 0xBA4D61362C8A4AB3ULL, 0xC52391FE1CBFC3CAULL, + 0x8DF265D5D4A0EECEULL, 0xF29C951DE49567B7ULL, + 0x732F8445B4CBFC3CULL, 0x0C41748D84FE7545ULL, + 0x6BAD6D5EBD3716B7ULL, 0x14C39D968D029FCEULL, + 0x95708CCEDD5C0445ULL, 0xEA1E7C06ED698D3CULL, + 0xA2CF882D2576A038ULL, 0xDDA178E515432941ULL, + 0x5C1269BD451DB2CAULL, 0x237C997575283BB3ULL, + 0xCDB181EAD523E8C2ULL, 0xB2DF7122E51661BBULL, + 0x336C607AB548FA30ULL, 0x4C0290B2857D7349ULL, + 0x04D364994D625E4DULL, 0x7BBD94517D57D734ULL, + 0xFA0E85092D094CBFULL, 0x856075C11D3CC5C6ULL, + 0x134D926535897936ULL, 0x6C2362AD05BCF04FULL, + 0xED9073F555E26BC4ULL, 0x92FE833D65D7E2BDULL, + 0xDA2F7716ADC8CFB9ULL, 0xA54187DE9DFD46C0ULL, + 0x24F29686CDA3DD4BULL, 0x5B9C664EFD965432ULL, + 0xB5517ED15D9D8743ULL, 0xCA3F8E196DA80E3AULL, + 0x4B8C9F413DF695B1ULL, 0x34E26F890DC31CC8ULL, + 0x7C339BA2C5DC31CCULL, 0x035D6B6AF5E9B8B5ULL, + 0x82EE7A32A5B7233EULL, 0xFD808AFA9582AA47ULL, + 0x4D364994D625E4DAULL, 0x3258B95CE6106DA3ULL, + 0xB3EBA804B64EF628ULL, 0xCC8558CC867B7F51ULL, + 0x8454ACE74E645255ULL, 0xFB3A5C2F7E51DB2CULL, + 0x7A894D772E0F40A7ULL, 0x05E7BDBF1E3AC9DEULL, + 0xEB2AA520BE311AAFULL, 0x944455E88E0493D6ULL, + 0x15F744B0DE5A085DULL, 0x6A99B478EE6F8124ULL, + 0x224840532670AC20ULL, 0x5D26B09B16452559ULL, + 0xDC95A1C3461BBED2ULL, 0xA3FB510B762E37ABULL, + 0x35D6B6AF5E9B8B5BULL, 0x4AB846676EAE0222ULL, + 0xCB0B573F3EF099A9ULL, 0xB465A7F70EC510D0ULL, + 0xFCB453DCC6DA3DD4ULL, 0x83DAA314F6EFB4ADULL, + 0x0269B24CA6B12F26ULL, 0x7D0742849684A65FULL, + 0x93CA5A1B368F752EULL, 0xECA4AAD306BAFC57ULL, + 0x6D17BB8B56E467DCULL, 0x12794B4366D1EEA5ULL, + 0x5AA8BF68AECEC3A1ULL, 0x25C64FA09EFB4AD8ULL, + 0xA4755EF8CEA5D153ULL, 0xDB1BAE30FE90582AULL, + 0xBCF7B7E3C7593BD8ULL, 0xC399472BF76CB2A1ULL, + 0x422A5673A732292AULL, 0x3D44A6BB9707A053ULL, + 0x759552905F188D57ULL, 0x0AFBA2586F2D042EULL, + 0x8B48B3003F739FA5ULL, 0xF42643C80F4616DCULL, + 0x1AEB5B57AF4DC5ADULL, 0x6585AB9F9F784CD4ULL, + 0xE436BAC7CF26D75FULL, 0x9B584A0FFF135E26ULL, + 0xD389BE24370C7322ULL, 0xACE74EEC0739FA5BULL, + 0x2D545FB4576761D0ULL, 0x523AAF7C6752E8A9ULL, + 0xC41748D84FE75459ULL, 0xBB79B8107FD2DD20ULL, + 0x3ACAA9482F8C46ABULL, 0x45A459801FB9CFD2ULL, + 0x0D75ADABD7A6E2D6ULL, 0x721B5D63E7936BAFULL, + 0xF3A84C3BB7CDF024ULL, 0x8CC6BCF387F8795DULL, + 0x620BA46C27F3AA2CULL, 0x1D6554A417C62355ULL, + 0x9CD645FC4798B8DEULL, 0xE3B8B53477AD31A7ULL, + 0xAB69411FBFB21CA3ULL, 0xD407B1D78F8795DAULL, + 0x55B4A08FDFD90E51ULL, 0x2ADA5047EFEC8728ULL, +}; + +static inline size_t nvme_pi_tuple_size(NvmeNamespace *ns) +{ + return ns->pif ? 16 : 8; +} + +uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba, + uint64_t reftag); +uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen, + uint64_t slba); +void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len, + uint8_t *mbuf, size_t mlen, uint16_t apptag, + uint64_t *reftag); +uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len, + uint8_t *mbuf, size_t mlen, uint8_t prinfo, + uint64_t slba, uint16_t apptag, + uint16_t appmask, uint64_t *reftag); +uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req); + +#endif /* HW_NVME_DIF_H */ diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index ee673f1a5b..324f53ea0c 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -58,6 +58,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) { static uint64_t ns_count; NvmeIdNs *id_ns = &ns->id_ns; + NvmeIdNsNvm *id_ns_nvm = &ns->id_ns_nvm; uint8_t ds; uint16_t ms; int i; @@ -101,6 +102,8 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) id_ns->dps |= NVME_ID_NS_DPS_FIRST_EIGHT; } + ns->pif = ns->params.pif; + static const NvmeLBAF lbaf[16] = { [0] = { .ds = 9 }, [1] = { .ds = 9, .ms = 8 }, @@ -112,10 +115,11 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) [7] = { .ds = 12, .ms = 64 }, }; - memcpy(&id_ns->lbaf, &lbaf, sizeof(lbaf)); - id_ns->nlbaf = 7; + ns->nlbaf = 8; - for (i = 0; i <= id_ns->nlbaf; i++) { + memcpy(&id_ns->lbaf, &lbaf, sizeof(lbaf)); + + for (i = 0; i < ns->nlbaf; i++) { NvmeLBAF *lbaf = &id_ns->lbaf[i]; if (lbaf->ds == ds) { if (lbaf->ms == ms) { @@ -126,12 +130,16 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) } /* add non-standard lba format */ - id_ns->nlbaf++; - id_ns->lbaf[id_ns->nlbaf].ds = ds; - id_ns->lbaf[id_ns->nlbaf].ms = ms; - id_ns->flbas |= id_ns->nlbaf; + id_ns->lbaf[ns->nlbaf].ds = ds; + id_ns->lbaf[ns->nlbaf].ms = ms; + ns->nlbaf++; + + id_ns->flbas |= i; + lbaf_found: + id_ns_nvm->elbaf[i] = (ns->pif & 0x3) << 7; + id_ns->nlbaf = ns->nlbaf - 1; nvme_ns_init_format(ns); return 0; @@ -260,7 +268,7 @@ static void nvme_ns_init_zoned(NvmeNamespace *ns) nvme_ns_zoned_init_state(ns); - id_ns_z = g_malloc0(sizeof(NvmeIdNsZoned)); + id_ns_z = g_new0(NvmeIdNsZoned, 1); /* MAR/MOR are zeroes-based, FFFFFFFFFh means no limit */ id_ns_z->mar = cpu_to_le32(ns->params.max_active_zones - 1); @@ -370,15 +378,36 @@ static void nvme_zoned_ns_shutdown(NvmeNamespace *ns) static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp) { + unsigned int pi_size; + if (!ns->blkconf.blk) { error_setg(errp, "block backend not configured"); return -1; } - if (ns->params.pi && ns->params.ms < 8) { - error_setg(errp, "at least 8 bytes of metadata required to enable " - "protection information"); - return -1; + if (ns->params.pi) { + if (ns->params.pi > NVME_ID_NS_DPS_TYPE_3) { + error_setg(errp, "invalid 'pi' value"); + return -1; + } + + switch (ns->params.pif) { + case NVME_PI_GUARD_16: + pi_size = 8; + break; + case NVME_PI_GUARD_64: + pi_size = 16; + break; + default: + error_setg(errp, "invalid 'pif'"); + return -1; + } + + if (ns->params.ms < pi_size) { + error_setg(errp, "at least %u bytes of metadata required to " + "enable protection information", pi_size); + return -1; + } } if (ns->params.nsid > NVME_MAX_NAMESPACES) { @@ -590,6 +619,7 @@ static Property nvme_ns_props[] = { DEFINE_PROP_UINT8("mset", NvmeNamespace, params.mset, 0), DEFINE_PROP_UINT8("pi", NvmeNamespace, params.pi, 0), DEFINE_PROP_UINT8("pil", NvmeNamespace, params.pil, 0), + DEFINE_PROP_UINT8("pif", NvmeNamespace, params.pif, 0), DEFINE_PROP_UINT16("mssrl", NvmeNamespace, params.mssrl, 128), DEFINE_PROP_UINT32("mcl", NvmeNamespace, params.mcl, 128), DEFINE_PROP_UINT8("msrc", NvmeNamespace, params.msrc, 127), diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 90c0bb7ce2..739c8b8f79 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -102,6 +102,7 @@ typedef struct NvmeNamespaceParams { uint8_t mset; uint8_t pi; uint8_t pil; + uint8_t pif; uint16_t mssrl; uint32_t mcl; @@ -127,12 +128,15 @@ typedef struct NvmeNamespace { int64_t size; int64_t moff; NvmeIdNs id_ns; + NvmeIdNsNvm id_ns_nvm; NvmeLBAF lbaf; + unsigned int nlbaf; size_t lbasz; const uint32_t *iocs; uint8_t csi; uint16_t status; int attached; + uint8_t pif; struct { uint16_t zrwas; @@ -468,7 +472,9 @@ typedef struct NvmeCtrl { uint16_t temp_thresh_hi; uint16_t temp_thresh_low; }; - uint32_t async_config; + + uint32_t async_config; + NvmeHostBehaviorSupport hbs; } features; } NvmeCtrl; @@ -513,54 +519,4 @@ void nvme_rw_complete_cb(void *opaque, int ret); uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len, NvmeCmd *cmd); -/* from Linux kernel (crypto/crct10dif_common.c) */ -static const uint16_t t10_dif_crc_table[256] = { - 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B, - 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6, - 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6, - 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B, - 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1, - 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C, - 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C, - 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781, - 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8, - 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255, - 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925, - 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698, - 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472, - 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF, - 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF, - 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02, - 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA, - 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067, - 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17, - 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA, - 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640, - 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD, - 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D, - 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30, - 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759, - 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4, - 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394, - 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29, - 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3, - 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E, - 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E, - 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 -}; - -uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba, - uint32_t reftag); -uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen, - uint64_t slba); -void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len, - uint8_t *mbuf, size_t mlen, uint16_t apptag, - uint32_t *reftag); -uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len, - uint8_t *mbuf, size_t mlen, uint8_t prinfo, - uint64_t slba, uint16_t apptag, - uint16_t appmask, uint32_t *reftag); -uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req); - - #endif /* HW_NVME_INTERNAL_H */ diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events index 90730d802f..ff1b458969 100644 --- a/hw/nvme/trace-events +++ b/hw/nvme/trace-events @@ -20,12 +20,16 @@ pci_nvme_dif_rw_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'" pci_nvme_dif_rw_mdata_in_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'" pci_nvme_dif_rw_mdata_out_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'" pci_nvme_dif_rw_check_cb(uint16_t cid, uint8_t prinfo, uint16_t apptag, uint16_t appmask, uint32_t reftag) "cid %"PRIu16" prinfo 0x%"PRIx8" apptag 0x%"PRIx16" appmask 0x%"PRIx16" reftag 0x%"PRIx32"" -pci_nvme_dif_pract_generate_dif(size_t len, size_t lba_size, size_t chksum_len, uint16_t apptag, uint32_t reftag) "len %zu lba_size %zu chksum_len %zu apptag 0x%"PRIx16" reftag 0x%"PRIx32"" +pci_nvme_dif_pract_generate_dif_crc16(size_t len, size_t lba_size, size_t chksum_len, uint16_t apptag, uint32_t reftag) "len %zu lba_size %zu chksum_len %zu apptag 0x%"PRIx16" reftag 0x%"PRIx32"" +pci_nvme_dif_pract_generate_dif_crc64(size_t len, size_t lba_size, size_t chksum_len, uint16_t apptag, uint64_t reftag) "len %zu lba_size %zu chksum_len %zu apptag 0x%"PRIx16" reftag 0x%"PRIx64"" pci_nvme_dif_check(uint8_t prinfo, uint16_t chksum_len) "prinfo 0x%"PRIx8" chksum_len %"PRIu16"" -pci_nvme_dif_prchk_disabled(uint16_t apptag, uint32_t reftag) "apptag 0x%"PRIx16" reftag 0x%"PRIx32"" -pci_nvme_dif_prchk_guard(uint16_t guard, uint16_t crc) "guard 0x%"PRIx16" crc 0x%"PRIx16"" +pci_nvme_dif_prchk_disabled_crc16(uint16_t apptag, uint32_t reftag) "apptag 0x%"PRIx16" reftag 0x%"PRIx32"" +pci_nvme_dif_prchk_disabled_crc64(uint16_t apptag, uint64_t reftag) "apptag 0x%"PRIx16" reftag 0x%"PRIx64"" +pci_nvme_dif_prchk_guard_crc16(uint16_t guard, uint16_t crc) "guard 0x%"PRIx16" crc 0x%"PRIx16"" +pci_nvme_dif_prchk_guard_crc64(uint64_t guard, uint64_t crc) "guard 0x%"PRIx64" crc 0x%"PRIx64"" pci_nvme_dif_prchk_apptag(uint16_t apptag, uint16_t elbat, uint16_t elbatm) "apptag 0x%"PRIx16" elbat 0x%"PRIx16" elbatm 0x%"PRIx16"" -pci_nvme_dif_prchk_reftag(uint32_t reftag, uint32_t elbrt) "reftag 0x%"PRIx32" elbrt 0x%"PRIx32"" +pci_nvme_dif_prchk_reftag_crc16(uint32_t reftag, uint32_t elbrt) "reftag 0x%"PRIx32" elbrt 0x%"PRIx32"" +pci_nvme_dif_prchk_reftag_crc64(uint64_t reftag, uint64_t elbrt) "reftag 0x%"PRIx64" elbrt 0x%"PRIx64"" pci_nvme_copy(uint16_t cid, uint32_t nsid, uint16_t nr, uint8_t format) "cid %"PRIu16" nsid %"PRIu32" nr %"PRIu16" format 0x%"PRIx8"" pci_nvme_copy_source_range(uint64_t slba, uint32_t nlb) "slba 0x%"PRIx64" nlb %"PRIu32"" pci_nvme_copy_out(uint64_t slba, uint32_t nlb) "slba 0x%"PRIx64" nlb %"PRIu32"" diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index da435500ba..01a3093600 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -58,9 +58,10 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) switch (event) { case I2C_START_SEND: - case I2C_START_RECV: case I2C_FINISH: ee->haveaddr = 0; + /* fallthrough */ + case I2C_START_RECV: DPRINTK("clear\n"); if (ee->blk && ee->changed) { int len = blk_pwrite(ee->blk, 0, ee->mem, ee->rsize, 0); @@ -84,6 +85,10 @@ uint8_t at24c_eeprom_recv(I2CSlave *s) EEPROMState *ee = AT24C_EE(s); uint8_t ret; + if (ee->haveaddr == 1) { + return 0xff; + } + ret = ee->mem[ee->cur]; ee->cur = (ee->cur + 1u) % ee->rsize; diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index e5f3c98184..4125cbebcd 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" @@ -622,9 +621,9 @@ static bool fw_cfg_acpi_mr_restore(void *opaque) FWCfgState *s = opaque; bool mr_aligned; - mr_aligned = QEMU_IS_ALIGNED(s->table_mr_size, qemu_real_host_page_size) && - QEMU_IS_ALIGNED(s->linker_mr_size, qemu_real_host_page_size) && - QEMU_IS_ALIGNED(s->rsdp_mr_size, qemu_real_host_page_size); + mr_aligned = QEMU_IS_ALIGNED(s->table_mr_size, qemu_real_host_page_size()) && + QEMU_IS_ALIGNED(s->linker_mr_size, qemu_real_host_page_size()) && + QEMU_IS_ALIGNED(s->rsdp_mr_size, qemu_real_host_page_size()); return s->acpi_mr_restore && !mr_aligned; } diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index fbfdf47e26..18b43be7f6 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -219,7 +219,7 @@ static void postload_update_cb(void *opaque, bool running, RunState state) { SpaprNvram *nvram = opaque; - /* This is called after bdrv_invalidate_cache_all. */ + /* This is called after bdrv_activate_all. */ qemu_del_vm_change_state_handler(nvram->vmstate); nvram->vmstate = NULL; diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index b70828e5bf..6ed32adad9 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -89,7 +89,7 @@ static bool bbram_pgm_enabled(XlnxBBRam *s) static void bbram_bdrv_error(XlnxBBRam *s, int rc, gchar *detail) { - Error *errp; + Error *errp = NULL; error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.", blk_name(s->blk), detail); diff --git a/hw/openrisc/meson.build b/hw/openrisc/meson.build index 947f63ee08..ec48172c9d 100644 --- a/hw/openrisc/meson.build +++ b/hw/openrisc/meson.build @@ -1,5 +1,5 @@ openrisc_ss = ss.source_set() openrisc_ss.add(files('cputimer.c')) -openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: files('openrisc_sim.c')) +openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: [files('openrisc_sim.c'), fdt]) hw_arch += {'openrisc': openrisc_ss} diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 73fe383c2d..8184caa60b 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -29,16 +29,61 @@ #include "net/net.h" #include "hw/loader.h" #include "hw/qdev-properties.h" +#include "exec/address-spaces.h" +#include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "sysemu/qtest.h" #include "sysemu/reset.h" #include "hw/core/split-irq.h" +#include + #define KERNEL_LOAD_ADDR 0x100 +#define OR1KSIM_CPUS_MAX 4 +#define OR1KSIM_CLK_MHZ 20000000 + +#define TYPE_OR1KSIM_MACHINE MACHINE_TYPE_NAME("or1k-sim") +#define OR1KSIM_MACHINE(obj) \ + OBJECT_CHECK(Or1ksimState, (obj), TYPE_OR1KSIM_MACHINE) + +typedef struct Or1ksimState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + void *fdt; + int fdt_size; + +} Or1ksimState; + +enum { + OR1KSIM_DRAM, + OR1KSIM_UART, + OR1KSIM_ETHOC, + OR1KSIM_OMPIC, +}; + +enum { + OR1KSIM_OMPIC_IRQ = 1, + OR1KSIM_UART_IRQ = 2, + OR1KSIM_ETHOC_IRQ = 4, +}; + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} or1ksim_memmap[] = { + [OR1KSIM_DRAM] = { 0x00000000, 0 }, + [OR1KSIM_UART] = { 0x90000000, 0x100 }, + [OR1KSIM_ETHOC] = { 0x92000000, 0x800 }, + [OR1KSIM_OMPIC] = { 0x98000000, 16 }, +}; + static struct openrisc_boot_info { uint32_t bootstrap_pc; + uint32_t fdt_addr; } boot_info; static void main_cpu_reset(void *opaque) @@ -49,6 +94,7 @@ static void main_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); cpu_set_pc(cs, boot_info.bootstrap_pc); + cpu_set_gpr(&cpu->env, 3, boot_info.fdt_addr); } static qemu_irq get_cpu_irq(OpenRISCCPU *cpus[], int cpunum, int irq_pin) @@ -56,12 +102,77 @@ static qemu_irq get_cpu_irq(OpenRISCCPU *cpus[], int cpunum, int irq_pin) return qdev_get_gpio_in_named(DEVICE(cpus[cpunum]), "IRQ", irq_pin); } -static void openrisc_sim_net_init(hwaddr base, hwaddr descriptors, +static void openrisc_create_fdt(Or1ksimState *state, + const struct MemmapEntry *memmap, + int num_cpus, uint64_t mem_size, + const char *cmdline) +{ + void *fdt; + int cpu; + char *nodename; + int pic_ph; + + fdt = state->fdt = create_device_tree(&state->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + qemu_fdt_setprop_string(fdt, "/", "compatible", "opencores,or1ksim"); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x1); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1); + + nodename = g_strdup_printf("/memory@%" HWADDR_PRIx, + memmap[OR1KSIM_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[OR1KSIM_DRAM].base, mem_size); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + g_free(nodename); + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + for (cpu = 0; cpu < num_cpus; cpu++) { + nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "opencores,or1200-rtlsvn481"); + qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + OR1KSIM_CLK_MHZ); + g_free(nodename); + } + + nodename = (char *)"/pic"; + qemu_fdt_add_subnode(fdt, nodename); + pic_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "opencores,or1k-pic-level"); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", pic_ph); + + qemu_fdt_setprop_cell(fdt, "/", "interrupt-parent", pic_ph); + + qemu_fdt_add_subnode(fdt, "/chosen"); + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } + + /* Create aliases node for use by devices. */ + qemu_fdt_add_subnode(fdt, "/aliases"); +} + +static void openrisc_sim_net_init(Or1ksimState *state, hwaddr base, hwaddr size, int num_cpus, OpenRISCCPU *cpus[], int irq_pin, NICInfo *nd) { + void *fdt = state->fdt; DeviceState *dev; SysBusDevice *s; + char *nodename; int i; dev = qdev_new("open_eth"); @@ -81,14 +192,28 @@ static void openrisc_sim_net_init(hwaddr base, hwaddr descriptors, sysbus_connect_irq(s, 0, get_cpu_irq(cpus, 0, irq_pin)); } sysbus_mmio_map(s, 0, base); - sysbus_mmio_map(s, 1, descriptors); + sysbus_mmio_map(s, 1, base + 0x400); + + /* Init device tree node for ethoc. */ + nodename = g_strdup_printf("/ethoc@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "opencores,ethoc"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", irq_pin); + qemu_fdt_setprop(fdt, nodename, "big-endian", NULL, 0); + + qemu_fdt_setprop_string(fdt, "/aliases", "enet0", nodename); + g_free(nodename); } -static void openrisc_sim_ompic_init(hwaddr base, int num_cpus, +static void openrisc_sim_ompic_init(Or1ksimState *state, hwaddr base, + hwaddr size, int num_cpus, OpenRISCCPU *cpus[], int irq_pin) { + void *fdt = state->fdt; DeviceState *dev; SysBusDevice *s; + char *nodename; int i; dev = qdev_new("or1k-ompic"); @@ -100,28 +225,79 @@ static void openrisc_sim_ompic_init(hwaddr base, int num_cpus, sysbus_connect_irq(s, i, get_cpu_irq(cpus, i, irq_pin)); } sysbus_mmio_map(s, 0, base); + + /* Add device tree node for ompic. */ + nodename = g_strdup_printf("/ompic@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "openrisc,ompic"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 0); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", irq_pin); + g_free(nodename); } -static void openrisc_load_kernel(ram_addr_t ram_size, - const char *kernel_filename) +static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, + hwaddr size, int num_cpus, + OpenRISCCPU *cpus[], int irq_pin) +{ + void *fdt = state->fdt; + char *nodename; + qemu_irq serial_irq; + int i; + + if (num_cpus > 1) { + DeviceState *splitter = qdev_new(TYPE_SPLIT_IRQ); + qdev_prop_set_uint32(splitter, "num-lines", num_cpus); + qdev_realize_and_unref(splitter, NULL, &error_fatal); + for (i = 0; i < num_cpus; i++) { + qdev_connect_gpio_out(splitter, i, get_cpu_irq(cpus, i, irq_pin)); + } + serial_irq = qdev_get_gpio_in(splitter, 0); + } else { + serial_irq = get_cpu_irq(cpus, 0, irq_pin); + } + serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200, + serial_hd(0), DEVICE_NATIVE_ENDIAN); + + /* Add device tree node for serial. */ + nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", irq_pin); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", OR1KSIM_CLK_MHZ); + qemu_fdt_setprop(fdt, nodename, "big-endian", NULL, 0); + + /* The /chosen node is created during fdt creation. */ + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(fdt, "/aliases", "uart0", nodename); + g_free(nodename); +} + +static hwaddr openrisc_load_kernel(ram_addr_t ram_size, + const char *kernel_filename) { long kernel_size; uint64_t elf_entry; + uint64_t high_addr; hwaddr entry; if (kernel_filename && !qtest_enabled()) { kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, NULL, NULL, 1, EM_OPENRISC, - 1, 0); + &elf_entry, NULL, &high_addr, NULL, 1, + EM_OPENRISC, 1, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, NULL, NULL); + high_addr = entry + kernel_size; } if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, ram_size - KERNEL_LOAD_ADDR); + high_addr = KERNEL_LOAD_ADDR + kernel_size; } if (entry <= 0) { @@ -133,20 +309,79 @@ static void openrisc_load_kernel(ram_addr_t ram_size, exit(1); } boot_info.bootstrap_pc = entry; + + return high_addr; } + return 0; +} + +static hwaddr openrisc_load_initrd(Or1ksimState *state, const char *filename, + hwaddr load_start, uint64_t mem_size) +{ + void *fdt = state->fdt; + int size; + hwaddr start; + + /* We put the initrd right after the kernel; page aligned. */ + start = TARGET_PAGE_ALIGN(load_start); + + size = load_ramdisk(filename, start, mem_size - start); + if (size < 0) { + size = load_image_targphys(filename, start, mem_size - start); + if (size < 0) { + error_report("could not load ramdisk '%s'", filename); + exit(1); + } + } + + qemu_fdt_setprop_cell(fdt, "/chosen", + "linux,initrd-start", start); + qemu_fdt_setprop_cell(fdt, "/chosen", + "linux,initrd-end", start + size); + + return start + size; +} + +static uint32_t openrisc_load_fdt(Or1ksimState *state, hwaddr load_start, + uint64_t mem_size) +{ + void *fdt = state->fdt; + uint32_t fdt_addr; + int ret; + int fdtsize = fdt_totalsize(fdt); + + if (fdtsize <= 0) { + error_report("invalid device-tree"); + exit(1); + } + + /* We put fdt right after the kernel and/or initrd. */ + fdt_addr = ROUND_UP(load_start, 4); + + ret = fdt_pack(fdt); + /* Should only fail if we've built a corrupted tree */ + g_assert(ret == 0); + /* copy in the device tree */ + qemu_fdt_dumpdtb(fdt, fdtsize); + + rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, + &address_space_memory); + + return fdt_addr; } static void openrisc_sim_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; - OpenRISCCPU *cpus[2] = {}; + OpenRISCCPU *cpus[OR1KSIM_CPUS_MAX] = {}; + Or1ksimState *state = OR1KSIM_MACHINE(machine); MemoryRegion *ram; - qemu_irq serial_irq; + hwaddr load_addr; int n; unsigned int smp_cpus = machine->smp.cpus; - assert(smp_cpus >= 1 && smp_cpus <= 2); + assert(smp_cpus >= 1 && smp_cpus <= OR1KSIM_CPUS_MAX); for (n = 0; n < smp_cpus; n++) { cpus[n] = OPENRISC_CPU(cpu_create(machine->cpu_type)); if (cpus[n] == NULL) { @@ -163,33 +398,58 @@ static void openrisc_sim_init(MachineState *machine) memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_fatal); memory_region_add_subregion(get_system_memory(), 0, ram); + openrisc_create_fdt(state, or1ksim_memmap, smp_cpus, machine->ram_size, + machine->kernel_cmdline); + if (nd_table[0].used) { - openrisc_sim_net_init(0x92000000, 0x92000400, smp_cpus, - cpus, 4, nd_table); + openrisc_sim_net_init(state, or1ksim_memmap[OR1KSIM_ETHOC].base, + or1ksim_memmap[OR1KSIM_ETHOC].size, + smp_cpus, cpus, + OR1KSIM_ETHOC_IRQ, nd_table); } if (smp_cpus > 1) { - openrisc_sim_ompic_init(0x98000000, smp_cpus, cpus, 1); - - serial_irq = qemu_irq_split(get_cpu_irq(cpus, 0, 2), - get_cpu_irq(cpus, 1, 2)); - } else { - serial_irq = get_cpu_irq(cpus, 0, 2); + openrisc_sim_ompic_init(state, or1ksim_memmap[OR1KSIM_OMPIC].base, + or1ksim_memmap[OR1KSIM_UART].size, + smp_cpus, cpus, OR1KSIM_OMPIC_IRQ); } - serial_mm_init(get_system_memory(), 0x90000000, 0, serial_irq, - 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + openrisc_sim_serial_init(state, or1ksim_memmap[OR1KSIM_UART].base, + or1ksim_memmap[OR1KSIM_UART].size, smp_cpus, cpus, + OR1KSIM_UART_IRQ); - openrisc_load_kernel(ram_size, kernel_filename); + load_addr = openrisc_load_kernel(ram_size, kernel_filename); + if (load_addr > 0) { + if (machine->initrd_filename) { + load_addr = openrisc_load_initrd(state, machine->initrd_filename, + load_addr, machine->ram_size); + } + boot_info.fdt_addr = openrisc_load_fdt(state, load_addr, + machine->ram_size); + } } -static void openrisc_sim_machine_init(MachineClass *mc) +static void openrisc_sim_machine_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "or1k simulation"; mc->init = openrisc_sim_init; - mc->max_cpus = 2; + mc->max_cpus = OR1KSIM_CPUS_MAX; mc->is_default = true; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); } -DEFINE_MACHINE("or1k-sim", openrisc_sim_machine_init) +static const TypeInfo or1ksim_machine_typeinfo = { + .name = TYPE_OR1KSIM_MACHINE, + .parent = TYPE_MACHINE, + .class_init = openrisc_sim_machine_init, + .instance_size = sizeof(Or1ksimState), +}; + +static void or1ksim_machine_init_register_types(void) +{ + type_register_static(&or1ksim_machine_typeinfo); +} + +type_init(or1ksim_machine_init_register_types) diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 10e6e7c2ab..de932286b5 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -192,6 +192,12 @@ static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) { PCIDevice *pxb = pci_get_bus(pci_dev)->parent_dev; + /* + * First carry out normal swizzle to handle + * multple root ports on a pxb instance. + */ + pin = pci_swizzle_map_irq_fn(pci_dev, pin); + /* * The bios does not index the pxb slot number when * it computes the IRQ because it resides on bus 0 diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index 04aae72cd6..05e2b06c0c 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -28,6 +28,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/module.h" +#include "hw/pci-bridge/xio3130_downstream.h" #define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ #define XIO3130_REVISION 0x1 @@ -84,7 +85,7 @@ static void xio3130_downstream_realize(PCIDevice *d, Error **errp) XIO3130_SSVID_SVID, XIO3130_SSVID_SSID, errp); if (rc < 0) { - goto err_bridge; + goto err_msi; } rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM, @@ -173,7 +174,7 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data) } static const TypeInfo xio3130_downstream_info = { - .name = "xio3130-downstream", + .name = TYPE_XIO3130_DOWNSTREAM, .parent = TYPE_PCIE_SLOT, .class_init = xio3130_downstream_class_init, .interfaces = (InterfaceInfo[]) { diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index 5cd3af4fbc..5ff46ef050 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -75,7 +75,7 @@ static void xio3130_upstream_realize(PCIDevice *d, Error **errp) XIO3130_SSVID_SVID, XIO3130_SSVID_SSID, errp); if (rc < 0) { - goto err_bridge; + goto err_msi; } rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM, diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 00b3ff7d90..cc9c4d6d3b 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index aafd46b635..3f03467dde 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -10,7 +10,6 @@ #include "qemu/log.h" #include "qapi/visitor.h" #include "qapi/error.h" -#include "qemu-common.h" #include "hw/pci-host/pnv_phb3_regs.h" #include "hw/pci-host/pnv_phb3.h" #include "hw/pci/pcie_host.h" @@ -946,7 +945,7 @@ static AddressSpace *pnv_phb3_dma_iommu(PCIBus *bus, void *opaque, int devfn) } if (ds == NULL) { - ds = g_malloc0(sizeof(PnvPhb3DMASpace)); + ds = g_new0(PnvPhb3DMASpace, 1); ds->bus = bus; ds->devfn = devfn; ds->pe_num = PHB_INVALID_PE; @@ -994,30 +993,6 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp) PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); int i; - /* User created devices */ - if (!phb->chip) { - Error *local_err = NULL; - BusState *s; - - phb->chip = pnv_get_chip(pnv, phb->chip_id); - if (!phb->chip) { - error_setg(errp, "invalid chip id: %d", phb->chip_id); - return; - } - - /* - * Reparent user created devices to the chip to build - * correctly the device tree. - */ - pnv_chip_parent_fixup(phb->chip, OBJECT(phb), phb->phb_id); - - s = qdev_get_parent_bus(DEVICE(phb->chip)); - if (!qdev_set_parent_bus(DEVICE(phb), s, &local_err)) { - error_propagate(errp, local_err); - return; - } - } - if (phb->phb_id >= PNV_CHIP_GET_CLASS(phb->chip)->num_phbs) { error_setg(errp, "invalid PHB index: %d", phb->phb_id); return; @@ -1077,10 +1052,7 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp) pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb); - if (defaults_enabled()) { - pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), - TYPE_PNV_PHB3_ROOT_PORT); - } + pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), TYPE_PNV_PHB3_ROOT_PORT); } void pnv_phb3_update_regions(PnvPHB3 *phb) @@ -1131,7 +1103,7 @@ static void pnv_phb3_class_init(ObjectClass *klass, void *data) dc->realize = pnv_phb3_realize; device_class_set_props(dc, pnv_phb3_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->user_creatable = true; + dc->user_creatable = false; } static const TypeInfo pnv_phb3_type_info = { @@ -1189,6 +1161,7 @@ static void pnv_phb3_root_port_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + pci_config_set_interrupt_pin(pci->config, 0); } static void pnv_phb3_root_port_class_init(ObjectClass *klass, void *data) @@ -1201,7 +1174,7 @@ static void pnv_phb3_root_port_class_init(ObjectClass *klass, void *data) device_class_set_parent_realize(dc, pnv_phb3_root_port_realize, &rpc->parent_realize); - dc->user_creatable = true; + dc->user_creatable = false; k->vendor_id = PCI_VENDOR_ID_IBM; k->device_id = 0x03dc; diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index 8bcbc2cc4f..2f4112907b 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qapi/error.h" -#include "qemu-common.h" #include "hw/pci-host/pnv_phb3_regs.h" #include "hw/pci-host/pnv_phb3.h" #include "hw/ppc/pnv.h" diff --git a/hw/pci-host/pnv_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c index c7426cd27a..82f70efa43 100644 --- a/hw/pci-host/pnv_phb3_pbcq.c +++ b/hw/pci-host/pnv_phb3_pbcq.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/log.h" #include "target/ppc/cpu.h" #include "hw/ppc/fdt.h" diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index e91249ef64..13ba9e45d8 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -10,7 +10,6 @@ #include "qemu/log.h" #include "qapi/visitor.h" #include "qapi/error.h" -#include "qemu-common.h" #include "monitor/monitor.h" #include "target/ppc/cpu.h" #include "hw/pci-host/pnv_phb4_regs.h" @@ -485,6 +484,15 @@ static void pnv_phb4_update_xsrc(PnvPHB4 *phb) flags = 0; } + /* + * When the PQ disable configuration bit is set, the check on the + * PQ state bits is disabled on the PHB side (for MSI only) and it + * is performed on the IC side instead. + */ + if (phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_PQ_DISABLE) { + flags |= XIVE_SRC_PQ_DISABLE; + } + phb->xsrc.esb_shift = shift; phb->xsrc.esb_flags = flags; @@ -1457,7 +1465,7 @@ static AddressSpace *pnv_phb4_dma_iommu(PCIBus *bus, void *opaque, int devfn) ds = pnv_phb4_dma_find(phb, bus, devfn); if (ds == NULL) { - ds = g_malloc0(sizeof(PnvPhb4DMASpace)); + ds = g_new0(PnvPhb4DMASpace, 1); ds->bus = bus; ds->devfn = devfn; ds->pe_num = PHB_INVALID_PE; @@ -1536,74 +1544,14 @@ static void pnv_phb4_instance_init(Object *obj) object_initialize_child(obj, "source", &phb->xsrc, TYPE_XIVE_SOURCE); } -static PnvPhb4PecState *pnv_phb4_get_pec(PnvChip *chip, PnvPHB4 *phb, - Error **errp) -{ - Pnv9Chip *chip9 = PNV9_CHIP(chip); - int chip_id = phb->chip_id; - int index = phb->phb_id; - int i, j; - - for (i = 0; i < chip->num_pecs; i++) { - /* - * For each PEC, check the amount of phbs it supports - * and see if the given phb4 index matches an index. - */ - PnvPhb4PecState *pec = &chip9->pecs[i]; - - for (j = 0; j < pec->num_phbs; j++) { - if (index == pnv_phb4_pec_get_phb_id(pec, j)) { - return pec; - } - } - } - - error_setg(errp, - "pnv-phb4 chip-id %d index %d didn't match any existing PEC", - chip_id, index); - - return NULL; -} - static void pnv_phb4_realize(DeviceState *dev, Error **errp) { PnvPHB4 *phb = PNV_PHB4(dev); PCIHostState *pci = PCI_HOST_BRIDGE(dev); XiveSource *xsrc = &phb->xsrc; - Error *local_err = NULL; int nr_irqs; char name[32]; - /* User created PHB */ - if (!phb->pec) { - PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); - PnvChip *chip = pnv_get_chip(pnv, phb->chip_id); - BusState *s; - - if (!chip) { - error_setg(errp, "invalid chip id: %d", phb->chip_id); - return; - } - - phb->pec = pnv_phb4_get_pec(chip, phb, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - /* - * Reparent user created devices to the chip to build - * correctly the device tree. - */ - pnv_chip_parent_fixup(chip, OBJECT(phb), phb->phb_id); - - s = qdev_get_parent_bus(DEVICE(chip)); - if (!qdev_set_parent_bus(DEVICE(phb), s, &local_err)) { - error_propagate(errp, local_err); - return; - } - } - /* Set the "big_phb" flag */ phb->big_phb = phb->phb_id == 0 || phb->phb_id == 3; @@ -1664,15 +1612,64 @@ static const char *pnv_phb4_root_bus_path(PCIHostState *host_bridge, return phb->bus_path; } -static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno) +/* + * Address base trigger mode (POWER10) + * + * Trigger directly the IC ESB page + */ +static void pnv_phb4_xive_notify_abt(PnvPHB4 *phb, uint32_t srcno, + bool pq_checked) +{ + uint64_t notif_port = phb->regs[PHB_INT_NOTIFY_ADDR >> 3]; + uint64_t data = 0; /* trigger data : don't care */ + hwaddr addr; + MemTxResult result; + int esb_shift; + + if (notif_port & PHB_INT_NOTIFY_ADDR_64K) { + esb_shift = 16; + } else { + esb_shift = 12; + } + + /* Compute the address of the IC ESB management page */ + addr = (notif_port & ~PHB_INT_NOTIFY_ADDR_64K); + addr |= (1ull << (esb_shift + 1)) * srcno; + addr |= (1ull << esb_shift); + + /* + * When the PQ state bits are checked on the PHB, the associated + * PQ state bits on the IC should be ignored. Use the unconditional + * trigger offset to inject a trigger on the IC. This is always + * the case for LSIs + */ + if (pq_checked) { + addr |= XIVE_ESB_INJECT; + } + + trace_pnv_phb4_xive_notify_ic(addr, data); + + address_space_stq_be(&address_space_memory, addr, data, + MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { + phb_error(phb, "trigger failed @%"HWADDR_PRIx "\n", addr); + return; + } +} + +static void pnv_phb4_xive_notify_ic(PnvPHB4 *phb, uint32_t srcno, + bool pq_checked) { - PnvPHB4 *phb = PNV_PHB4(xf); uint64_t notif_port = phb->regs[PHB_INT_NOTIFY_ADDR >> 3]; uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3]; - uint64_t data = XIVE_TRIGGER_PQ | offset | srcno; + uint64_t data = offset | srcno; MemTxResult result; - trace_pnv_phb4_xive_notify(notif_port, data); + if (pq_checked) { + data |= XIVE_TRIGGER_PQ; + } + + trace_pnv_phb4_xive_notify_ic(notif_port, data); address_space_stq_be(&address_space_memory, notif_port, data, MEMTXATTRS_UNSPECIFIED, &result); @@ -1682,6 +1679,18 @@ static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno) } } +static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno, + bool pq_checked) +{ + PnvPHB4 *phb = PNV_PHB4(xf); + + if (phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_ABT_MODE) { + pnv_phb4_xive_notify_abt(phb, srcno, pq_checked); + } else { + pnv_phb4_xive_notify_ic(phb, srcno, pq_checked); + } +} + static Property pnv_phb4_properties[] = { DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), @@ -1700,7 +1709,7 @@ static void pnv_phb4_class_init(ObjectClass *klass, void *data) dc->realize = pnv_phb4_realize; device_class_set_props(dc, pnv_phb4_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->user_creatable = true; + dc->user_creatable = false; xfc->notify = pnv_phb4_xive_notify; } @@ -1717,6 +1726,12 @@ static const TypeInfo pnv_phb4_type_info = { } }; +static const TypeInfo pnv_phb5_type_info = { + .name = TYPE_PNV_PHB5, + .parent = TYPE_PNV_PHB4, + .instance_size = sizeof(PnvPHB4), +}; + static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); @@ -1756,6 +1771,7 @@ static void pnv_phb4_root_port_reset(DeviceState *dev) pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0xfff1); pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0x1); /* Hack */ pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0xffffffff); + pci_config_set_interrupt_pin(conf, 0); } static void pnv_phb4_root_port_realize(DeviceState *dev, Error **errp) @@ -1792,7 +1808,7 @@ static void pnv_phb4_root_port_class_init(ObjectClass *klass, void *data) PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); dc->desc = "IBM PHB4 PCIE Root Port"; - dc->user_creatable = true; + dc->user_creatable = false; device_class_set_parent_realize(dc, pnv_phb4_root_port_realize, &rpc->parent_realize); @@ -1816,11 +1832,32 @@ static const TypeInfo pnv_phb4_root_port_info = { .class_init = pnv_phb4_root_port_class_init, }; +static void pnv_phb5_root_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "IBM PHB5 PCIE Root Port"; + dc->user_creatable = false; + + k->vendor_id = PCI_VENDOR_ID_IBM; + k->device_id = PNV_PHB5_DEVICE_ID; +} + +static const TypeInfo pnv_phb5_root_port_info = { + .name = TYPE_PNV_PHB5_ROOT_PORT, + .parent = TYPE_PNV_PHB4_ROOT_PORT, + .instance_size = sizeof(PnvPHB4RootPort), + .class_init = pnv_phb5_root_port_class_init, +}; + static void pnv_phb4_register_types(void) { type_register_static(&pnv_phb4_root_bus_info); + type_register_static(&pnv_phb5_root_port_info); type_register_static(&pnv_phb4_root_port_info); type_register_static(&pnv_phb4_type_info); + type_register_static(&pnv_phb5_type_info); type_register_static(&pnv_phb4_iommu_memory_region_info); } @@ -1828,10 +1865,15 @@ type_init(pnv_phb4_register_types); void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon) { + uint64_t notif_port = + phb->regs[PHB_INT_NOTIFY_ADDR >> 3] & ~PHB_INT_NOTIFY_ADDR_64K; uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3]; + bool abt = !!(phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_ABT_MODE); - monitor_printf(mon, "PHB4[%x:%x] Source %08x .. %08x\n", + monitor_printf(mon, "PHB4[%x:%x] Source %08x .. %08x %s @%"HWADDR_PRIx"\n", phb->chip_id, phb->phb_id, - offset, offset + phb->xsrc.nr_irqs - 1); + offset, offset + phb->xsrc.nr_irqs - 1, + abt ? "ABT" : "", + notif_port); xive_source_pic_print_info(&phb->xsrc, 0, mon); } diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index 40d89fda56..61bc0b503e 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/log.h" #include "target/ppc/cpu.h" #include "hw/ppc/fdt.h" @@ -116,9 +115,11 @@ static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, int stack_no, Error **errp) { - PnvPHB4 *phb = PNV_PHB4(qdev_new(TYPE_PNV_PHB4)); + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + PnvPHB4 *phb = PNV_PHB4(qdev_new(pecc->phb_type)); int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no); + object_property_add_child(OBJECT(pec), "phb[*]", OBJECT(phb)); object_property_set_link(OBJECT(phb), "pec", OBJECT(pec), &error_abort); object_property_set_int(OBJECT(phb), "chip-id", pec->chip_id, @@ -131,9 +132,7 @@ static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, } /* Add a single Root port if running with defaults */ - pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), - PNV_PHB4_PEC_GET_CLASS(pec)->rp_model); - + pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), pecc->rp_model); } static void pnv_pec_realize(DeviceState *dev, Error **errp) @@ -151,10 +150,8 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp) pec->num_phbs = pecc->num_phbs[pec->index]; /* Create PHBs if running with defaults */ - if (defaults_enabled()) { - for (i = 0; i < pec->num_phbs; i++) { - pnv_pec_default_phb_realize(pec, i, errp); - } + for (i = 0; i < pec->num_phbs; i++) { + pnv_pec_default_phb_realize(pec, i, errp); } /* Initialize the XSCOM regions for the PEC registers */ @@ -265,6 +262,7 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data) pecc->stk_compat = stk_compat; pecc->stk_compat_size = sizeof(stk_compat); pecc->version = PNV_PHB4_VERSION; + pecc->phb_type = TYPE_PNV_PHB4; pecc->num_phbs = pnv_pec_num_phbs; pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT; } @@ -281,9 +279,63 @@ static const TypeInfo pnv_pec_type_info = { } }; +/* + * POWER10 definitions + */ + +static uint32_t pnv_phb5_pec_xscom_pci_base(PnvPhb4PecState *pec) +{ + return PNV10_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index; +} + +static uint32_t pnv_phb5_pec_xscom_nest_base(PnvPhb4PecState *pec) +{ + /* index goes down ... */ + return PNV10_XSCOM_PEC_NEST_BASE - 0x1000000 * pec->index; +} + +/* + * PEC0 -> 3 stacks + * PEC1 -> 3 stacks + */ +static const uint32_t pnv_phb5_pec_num_stacks[] = { 3, 3 }; + +static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data) +{ + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass); + static const char compat[] = "ibm,power10-pbcq"; + static const char stk_compat[] = "ibm,power10-phb-stack"; + + pecc->xscom_nest_base = pnv_phb5_pec_xscom_nest_base; + pecc->xscom_pci_base = pnv_phb5_pec_xscom_pci_base; + pecc->xscom_nest_size = PNV10_XSCOM_PEC_NEST_SIZE; + pecc->xscom_pci_size = PNV10_XSCOM_PEC_PCI_SIZE; + pecc->compat = compat; + pecc->compat_size = sizeof(compat); + pecc->stk_compat = stk_compat; + pecc->stk_compat_size = sizeof(stk_compat); + pecc->version = PNV_PHB5_VERSION; + pecc->phb_type = TYPE_PNV_PHB5; + pecc->num_phbs = pnv_phb5_pec_num_stacks; + pecc->rp_model = TYPE_PNV_PHB5_ROOT_PORT; +} + +static const TypeInfo pnv_phb5_pec_type_info = { + .name = TYPE_PNV_PHB5_PEC, + .parent = TYPE_PNV_PHB4_PEC, + .instance_size = sizeof(PnvPhb4PecState), + .class_init = pnv_phb5_pec_class_init, + .class_size = sizeof(PnvPhb4PecClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + static void pnv_pec_register_types(void) { type_register_static(&pnv_pec_type_info); + type_register_static(&pnv_phb5_pec_type_info); } type_init(pnv_pec_register_types); diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 6e514f75eb..7a105e4a63 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/log.h" diff --git a/hw/pci-host/remote.c b/hw/pci-host/remote.c index eee45444ef..bfb25ef6af 100644 --- a/hw/pci-host/remote.c +++ b/hw/pci-host/remote.c @@ -22,7 +22,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 630e9fcc5e..6e5d8d3355 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -32,3 +32,5 @@ unin_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 # pnv_phb4.c pnv_phb4_xive_notify(uint64_t notif_port, uint64_t data) "notif=@0x%"PRIx64" data=0x%"PRIx64 +pnv_phb4_xive_notify_ic(uint64_t addr, uint64_t data) "addr=@0x%"PRIx64" data=0x%"PRIx64 +pnv_phb4_xive_notify_abt(uint64_t notif_port, uint64_t data) "notif=@0x%"PRIx64" data=0x%"PRIx64 diff --git a/hw/pci/meson.build b/hw/pci/meson.build index 5c4bbac817..bcc9c75919 100644 --- a/hw/pci/meson.build +++ b/hw/pci/meson.build @@ -5,6 +5,7 @@ pci_ss.add(files( 'pci.c', 'pci_bridge.c', 'pci_host.c', + 'pcie_sriov.c', 'shpc.c', 'slotid_cap.c' )) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 5d30f9ca60..e99417e501 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "hw/irq.h" @@ -239,6 +238,9 @@ int pci_bar(PCIDevice *d, int reg) { uint8_t type; + /* PCIe virtual functions do not have their own BARs */ + assert(!pci_is_vf(d)); + if (reg != PCI_ROM_SLOT) return PCI_BASE_ADDRESS_0 + reg * 4; @@ -304,10 +306,30 @@ void pci_device_deassert_intx(PCIDevice *dev) } } -static void pci_do_device_reset(PCIDevice *dev) +static void pci_reset_regions(PCIDevice *dev) { int r; + if (pci_is_vf(dev)) { + return; + } + for (r = 0; r < PCI_NUM_REGIONS; ++r) { + PCIIORegion *region = &dev->io_regions[r]; + if (!region->size) { + continue; + } + + if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) && + region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(dev->config + pci_bar(dev, r), region->type); + } else { + pci_set_long(dev->config + pci_bar(dev, r), region->type); + } + } +} + +static void pci_do_device_reset(PCIDevice *dev) +{ pci_device_deassert_intx(dev); assert(dev->irq_state == 0); @@ -323,19 +345,7 @@ static void pci_do_device_reset(PCIDevice *dev) pci_get_word(dev->wmask + PCI_INTERRUPT_LINE) | pci_get_word(dev->w1cmask + PCI_INTERRUPT_LINE)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; - for (r = 0; r < PCI_NUM_REGIONS; ++r) { - PCIIORegion *region = &dev->io_regions[r]; - if (!region->size) { - continue; - } - - if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) && - region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(dev->config + pci_bar(dev, r), region->type); - } else { - pci_set_long(dev->config + pci_bar(dev, r), region->type); - } - } + pci_reset_regions(dev); pci_update_mappings(dev); msi_reset(dev); @@ -884,6 +894,16 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; } + /* + * With SR/IOV and ARI, a device at function 0 need not be a multifunction + * device, as it may just be a VF that ended up with function 0 in + * the legacy PCI interpretation. Avoid failing in such cases: + */ + if (pci_is_vf(dev) && + dev->exp.sriov_vf.pf->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + return; + } + /* * multifunction bit is interpreted in two ways as follows. * - all functions must set the bit to 1. @@ -1078,11 +1098,12 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, return NULL; } else if (!pci_bus_devfn_available(bus, devfn)) { error_setg(errp, "PCI: slot %d function %d not available for %s," - " in use by %s", + " in use by %s,id=%s", PCI_SLOT(devfn), PCI_FUNC(devfn), name, - bus->devices[devfn]->name); + bus->devices[devfn]->name, bus->devices[devfn]->qdev.id); return NULL; } else if (dev->hotplugged && + !pci_is_vf(pci_dev) && pci_get_function_0(pci_dev)) { error_setg(errp, "PCI: slot %d function 0 already occupied by %s," " new func %s cannot be exposed to guest.", @@ -1191,6 +1212,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pcibus_t size = memory_region_size(memory); uint8_t hdr_type; + assert(!pci_is_vf(pci_dev)); /* VFs must use pcie_sriov_vf_register_bar */ assert(region_num >= 0); assert(region_num < PCI_NUM_REGIONS); assert(is_power_of_2(size)); @@ -1294,11 +1316,45 @@ pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num) return pci_dev->io_regions[region_num].addr; } -static pcibus_t pci_bar_address(PCIDevice *d, - int reg, uint8_t type, pcibus_t size) +static pcibus_t pci_config_get_bar_addr(PCIDevice *d, int reg, + uint8_t type, pcibus_t size) +{ + pcibus_t new_addr; + if (!pci_is_vf(d)) { + int bar = pci_bar(d, reg); + if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + new_addr = pci_get_quad(d->config + bar); + } else { + new_addr = pci_get_long(d->config + bar); + } + } else { + PCIDevice *pf = d->exp.sriov_vf.pf; + uint16_t sriov_cap = pf->exp.sriov_cap; + int bar = sriov_cap + PCI_SRIOV_BAR + reg * 4; + uint16_t vf_offset = + pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET); + uint16_t vf_stride = + pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_STRIDE); + uint32_t vf_num = (d->devfn - (pf->devfn + vf_offset)) / vf_stride; + + if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + new_addr = pci_get_quad(pf->config + bar); + } else { + new_addr = pci_get_long(pf->config + bar); + } + new_addr += vf_num * size; + } + /* The ROM slot has a specific enable bit, keep it intact */ + if (reg != PCI_ROM_SLOT) { + new_addr &= ~(size - 1); + } + return new_addr; +} + +pcibus_t pci_bar_address(PCIDevice *d, + int reg, uint8_t type, pcibus_t size) { pcibus_t new_addr, last_addr; - int bar = pci_bar(d, reg); uint16_t cmd = pci_get_word(d->config + PCI_COMMAND); Object *machine = qdev_get_machine(); ObjectClass *oc = object_get_class(machine); @@ -1309,7 +1365,7 @@ static pcibus_t pci_bar_address(PCIDevice *d, if (!(cmd & PCI_COMMAND_IO)) { return PCI_BAR_UNMAPPED; } - new_addr = pci_get_long(d->config + bar) & ~(size - 1); + new_addr = pci_config_get_bar_addr(d, reg, type, size); last_addr = new_addr + size - 1; /* Check if 32 bit BAR wraps around explicitly. * TODO: make priorities correct and remove this work around. @@ -1324,11 +1380,7 @@ static pcibus_t pci_bar_address(PCIDevice *d, if (!(cmd & PCI_COMMAND_MEMORY)) { return PCI_BAR_UNMAPPED; } - if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - new_addr = pci_get_quad(d->config + bar); - } else { - new_addr = pci_get_long(d->config + bar); - } + new_addr = pci_config_get_bar_addr(d, reg, type, size); /* the ROM slot has a specific enable bit */ if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) { return PCI_BAR_UNMAPPED; @@ -1473,6 +1525,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int msi_write_config(d, addr, val_in, l); msix_write_config(d, addr, val_in, l); + pcie_sriov_config_write(d, addr, val_in, l); } /***********************************************************/ @@ -1643,7 +1696,7 @@ static const pci_class_desc pci_class_descriptions[] = { 0x0902, "Mouse", "mouse"}, { 0x0A00, "Dock station", "dock", 0x00ff}, { 0x0B00, "i386 cpu", "cpu", 0x00ff}, - { 0x0c00, "Fireware contorller", "fireware"}, + { 0x0c00, "Firewire controller", "firewire"}, { 0x0c01, "Access bus controller", "access-bus"}, { 0x0c02, "SSA controller", "ssa"}, { 0x0c03, "USB controller", "usb"}, diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index d7d73a31e4..68a62da0b5 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -353,7 +353,7 @@ static void hotplug_event_notify(PCIDevice *dev) msix_notify(dev, pcie_cap_flags_get_vector(dev)); } else if (msi_enabled(dev)) { msi_notify(dev, pcie_cap_flags_get_vector(dev)); - } else { + } else if (pci_intx(dev) != -1) { pci_set_irq(dev, dev->exp.hpev_notified); } } @@ -361,11 +361,23 @@ static void hotplug_event_notify(PCIDevice *dev) static void hotplug_event_clear(PCIDevice *dev) { hotplug_event_update_event_status(dev); - if (!msix_enabled(dev) && !msi_enabled(dev) && !dev->exp.hpev_notified) { + if (!msix_enabled(dev) && !msi_enabled(dev) && pci_intx(dev) != -1 && + !dev->exp.hpev_notified) { pci_irq_deassert(dev); } } +void pcie_cap_slot_enable_power(PCIDevice *dev) +{ + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + uint32_t sltcap = pci_get_long(exp_cap + PCI_EXP_SLTCAP); + + if (sltcap & PCI_EXP_SLTCAP_PCP) { + pci_set_word_by_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_ON); + } +} + static void pcie_set_power_device(PCIBus *bus, PCIDevice *dev, void *opaque) { bool *power = opaque; @@ -446,6 +458,11 @@ void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, PCIDevice *pci_dev = PCI_DEVICE(dev); uint32_t lnkcap = pci_get_long(exp_cap + PCI_EXP_LNKCAP); + if (pci_is_vf(pci_dev)) { + /* Virtual function cannot be physically disconnected */ + return; + } + /* Don't send event when device is enabled during qemu machine creation: * it is present on boot, no hotplug event is necessary. We do send an * event when the device is disabled later. */ diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index e1a8a88c8c..92bd0530dd 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -290,7 +290,7 @@ static void pcie_aer_root_notify(PCIDevice *dev) msix_notify(dev, pcie_aer_root_get_vector(dev)); } else if (msi_enabled(dev)) { msi_notify(dev, pcie_aer_root_get_vector(dev)); - } else { + } else if (pci_intx(dev) != -1) { pci_irq_assert(dev); } } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c new file mode 100644 index 0000000000..8e3faf1f59 --- /dev/null +++ b/hw/pci/pcie_sriov.c @@ -0,0 +1,302 @@ +/* + * pcie_sriov.c: + * + * Implementation of SR/IOV emulation support. + * + * Copyright (c) 2015-2017 Knut Omang + * + * 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 "hw/pci/pci.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pci_bus.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "qemu/range.h" +#include "qapi/error.h" +#include "trace.h" + +static PCIDevice *register_vf(PCIDevice *pf, int devfn, + const char *name, uint16_t vf_num); +static void unregister_vfs(PCIDevice *dev); + +void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, + const char *vfname, uint16_t vf_dev_id, + uint16_t init_vfs, uint16_t total_vfs, + uint16_t vf_offset, uint16_t vf_stride) +{ + uint8_t *cfg = dev->config + offset; + uint8_t *wmask; + + pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, + offset, PCI_EXT_CAP_SRIOV_SIZEOF); + dev->exp.sriov_cap = offset; + dev->exp.sriov_pf.num_vfs = 0; + dev->exp.sriov_pf.vfname = g_strdup(vfname); + dev->exp.sriov_pf.vf = NULL; + + pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); + pci_set_word(cfg + PCI_SRIOV_VF_STRIDE, vf_stride); + + /* + * Mandatory page sizes to support. + * Device implementations can call pcie_sriov_pf_add_sup_pgsize() + * to set more bits: + */ + pci_set_word(cfg + PCI_SRIOV_SUP_PGSIZE, SRIOV_SUP_PGSIZE_MINREQ); + + /* + * Default is to use 4K pages, software can modify it + * to any of the supported bits + */ + pci_set_word(cfg + PCI_SRIOV_SYS_PGSIZE, 0x1); + + /* Set up device ID and initial/total number of VFs available */ + pci_set_word(cfg + PCI_SRIOV_VF_DID, vf_dev_id); + pci_set_word(cfg + PCI_SRIOV_INITIAL_VF, init_vfs); + pci_set_word(cfg + PCI_SRIOV_TOTAL_VF, total_vfs); + pci_set_word(cfg + PCI_SRIOV_NUM_VF, 0); + + /* Write enable control bits */ + wmask = dev->wmask + offset; + pci_set_word(wmask + PCI_SRIOV_CTRL, + PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI); + pci_set_word(wmask + PCI_SRIOV_NUM_VF, 0xffff); + pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553); + + qdev_prop_set_bit(&dev->qdev, "multifunction", true); +} + +void pcie_sriov_pf_exit(PCIDevice *dev) +{ + unregister_vfs(dev); + g_free((char *)dev->exp.sriov_pf.vfname); + dev->exp.sriov_pf.vfname = NULL; +} + +void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, + uint8_t type, dma_addr_t size) +{ + uint32_t addr; + uint64_t wmask; + uint16_t sriov_cap = dev->exp.sriov_cap; + + assert(sriov_cap > 0); + assert(region_num >= 0); + assert(region_num < PCI_NUM_REGIONS); + assert(region_num != PCI_ROM_SLOT); + + wmask = ~(size - 1); + addr = sriov_cap + PCI_SRIOV_BAR + region_num * 4; + + pci_set_long(dev->config + addr, type); + if (!(type & PCI_BASE_ADDRESS_SPACE_IO) && + type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(dev->wmask + addr, wmask); + pci_set_quad(dev->cmask + addr, ~0ULL); + } else { + pci_set_long(dev->wmask + addr, wmask & 0xffffffff); + pci_set_long(dev->cmask + addr, 0xffffffff); + } + dev->exp.sriov_pf.vf_bar_type[region_num] = type; +} + +void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, + MemoryRegion *memory) +{ + PCIIORegion *r; + PCIBus *bus = pci_get_bus(dev); + uint8_t type; + pcibus_t size = memory_region_size(memory); + + assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */ + assert(region_num >= 0); + assert(region_num < PCI_NUM_REGIONS); + type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num]; + + if (!is_power_of_2(size)) { + error_report("%s: PCI region size must be a power" + " of two - type=0x%x, size=0x%"FMT_PCIBUS, + __func__, type, size); + exit(1); + } + + r = &dev->io_regions[region_num]; + r->memory = memory; + r->address_space = + type & PCI_BASE_ADDRESS_SPACE_IO + ? bus->address_space_io + : bus->address_space_mem; + r->size = size; + r->type = type; + + r->addr = pci_bar_address(dev, region_num, r->type, r->size); + if (r->addr != PCI_BAR_UNMAPPED) { + memory_region_add_subregion_overlap(r->address_space, + r->addr, r->memory, 1); + } +} + +static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, + uint16_t vf_num) +{ + PCIDevice *dev = pci_new(devfn, name); + dev->exp.sriov_vf.pf = pf; + dev->exp.sriov_vf.vf_number = vf_num; + PCIBus *bus = pci_get_bus(pf); + Error *local_err = NULL; + + qdev_realize(&dev->qdev, &bus->qbus, &local_err); + if (local_err) { + error_report_err(local_err); + return NULL; + } + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(dev->config, 0xffff); + pci_config_set_device_id(dev->config, 0xffff); + + return dev; +} + +static void register_vfs(PCIDevice *dev) +{ + uint16_t num_vfs; + uint16_t i; + uint16_t sriov_cap = dev->exp.sriov_cap; + uint16_t vf_offset = + pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET); + uint16_t vf_stride = + pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE); + int32_t devfn = dev->devfn + vf_offset; + + assert(sriov_cap > 0); + num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + + dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs); + assert(dev->exp.sriov_pf.vf); + + trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), num_vfs); + for (i = 0; i < num_vfs; i++) { + dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn, + dev->exp.sriov_pf.vfname, i); + if (!dev->exp.sriov_pf.vf[i]) { + num_vfs = i; + break; + } + devfn += vf_stride; + } + dev->exp.sriov_pf.num_vfs = num_vfs; +} + +static void unregister_vfs(PCIDevice *dev) +{ + Error *local_err = NULL; + uint16_t num_vfs = dev->exp.sriov_pf.num_vfs; + uint16_t i; + + trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), num_vfs); + for (i = 0; i < num_vfs; i++) { + PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + object_property_set_bool(OBJECT(vf), "realized", false, &local_err); + if (local_err) { + fprintf(stderr, "Failed to unplug: %s\n", + error_get_pretty(local_err)); + error_free(local_err); + } + object_unparent(OBJECT(vf)); + } + g_free(dev->exp.sriov_pf.vf); + dev->exp.sriov_pf.vf = NULL; + dev->exp.sriov_pf.num_vfs = 0; + pci_set_word(dev->config + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0); +} + +void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, + uint32_t val, int len) +{ + uint32_t off; + uint16_t sriov_cap = dev->exp.sriov_cap; + + if (!sriov_cap || address < sriov_cap) { + return; + } + off = address - sriov_cap; + if (off >= PCI_EXT_CAP_SRIOV_SIZEOF) { + return; + } + + trace_sriov_config_write(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), off, val, len); + + if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { + if (dev->exp.sriov_pf.num_vfs) { + if (!(val & PCI_SRIOV_CTRL_VFE)) { + unregister_vfs(dev); + } + } else { + if (val & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); + } + } + } +} + + +/* Reset SR/IOV VF Enable bit to trigger an unregister of all VFs */ +void pcie_sriov_pf_disable_vfs(PCIDevice *dev) +{ + uint16_t sriov_cap = dev->exp.sriov_cap; + if (sriov_cap) { + uint32_t val = pci_get_byte(dev->config + sriov_cap + PCI_SRIOV_CTRL); + if (val & PCI_SRIOV_CTRL_VFE) { + val &= ~PCI_SRIOV_CTRL_VFE; + pcie_sriov_config_write(dev, sriov_cap + PCI_SRIOV_CTRL, val, 1); + } + } +} + +/* Add optional supported page sizes to the mask of supported page sizes */ +void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize) +{ + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + uint8_t *wmask = dev->wmask + dev->exp.sriov_cap; + + uint16_t sup_pgsize = pci_get_word(cfg + PCI_SRIOV_SUP_PGSIZE); + + sup_pgsize |= opt_sup_pgsize; + + /* + * Make sure the new bits are set, and that system page size + * also can be set to any of the new values according to spec: + */ + pci_set_word(cfg + PCI_SRIOV_SUP_PGSIZE, sup_pgsize); + pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, sup_pgsize); +} + + +uint16_t pcie_sriov_vf_number(PCIDevice *dev) +{ + assert(pci_is_vf(dev)); + return dev->exp.sriov_vf.vf_number; +} + +PCIDevice *pcie_sriov_get_pf(PCIDevice *dev) +{ + return dev->exp.sriov_vf.pf; +} + +PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) +{ + assert(!pci_is_vf(dev)); + if (n < dev->exp.sriov_pf.num_vfs) { + return dev->exp.sriov_pf.vf[n]; + } + return NULL; +} diff --git a/hw/pci/trace-events b/hw/pci/trace-events index 7570752c40..aaf46bc92d 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -10,3 +10,8 @@ pci_cfg_write(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, unsig # msix.c msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d masked %d" + +# hw/pci/pcie_sriov.c +sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs" +sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs" +sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d" diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 960e7efcd3..2bc3dce1fb 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -15,7 +15,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" @@ -899,7 +898,7 @@ void ppce500_init(MachineState *machine) if (!i) { /* Primary CPU */ struct boot_info *boot_info; - boot_info = g_malloc0(sizeof(struct boot_info)); + boot_info = g_new0(struct boot_info, 1); qemu_register_reset(ppce500_cpu_reset, cpu); env->load_info = boot_info; } else { diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 4bddb529c2..e8ef1a9e5d 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -47,7 +47,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" #include "hw/ppc/ppc.h" @@ -456,7 +455,7 @@ static void ppc_core99_init(MachineState *machine) } /* The NewWorld NVRAM is not located in the MacIO device */ - if (kvm_enabled() && qemu_real_host_page_size > 4096) { + if (kvm_enabled() && qemu_real_host_page_size() > 4096) { /* We can't combine read-write and read-only in a single page, so move the NVRAM out of ROM again for KVM */ nvram_addr = 0xFFE00000; diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 7016979a7c..fe2adb057b 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -25,7 +25,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index d45008ac71..56bf203dfd 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 837146a2fb..7c08a78d6c 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/cutils.h" @@ -380,9 +379,12 @@ static void pnv_dt_serial(ISADevice *d, void *fdt, int lpc_off) cpu_to_be32(io_base), cpu_to_be32(8) }; + uint32_t irq; char *name; int node; + irq = object_property_get_uint(OBJECT(d), "irq", &error_fatal); + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); node = fdt_add_subnode(fdt, lpc_off, name); _FDT(node); @@ -394,7 +396,7 @@ static void pnv_dt_serial(ISADevice *d, void *fdt, int lpc_off) _FDT((fdt_setprop_cell(fdt, node, "clock-frequency", 1843200))); _FDT((fdt_setprop_cell(fdt, node, "current-speed", 115200))); - _FDT((fdt_setprop_cell(fdt, node, "interrupts", d->isairq[0]))); + _FDT((fdt_setprop_cell(fdt, node, "interrupts", irq))); _FDT((fdt_setprop_cell(fdt, node, "interrupt-parent", fdt_get_phandle(fdt, lpc_off)))); @@ -612,24 +614,36 @@ static void pnv_reset(MachineState *machine) static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) { Pnv8Chip *chip8 = PNV8_CHIP(chip); + qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_EXTERNAL); + + qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); return pnv_lpc_isa_create(&chip8->lpc, true, errp); } static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) { Pnv8Chip *chip8 = PNV8_CHIP(chip); + qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_LPC_I2C); + + qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); return pnv_lpc_isa_create(&chip8->lpc, false, errp); } static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) { Pnv9Chip *chip9 = PNV9_CHIP(chip); + qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC); + + qdev_connect_gpio_out(DEVICE(&chip9->lpc), 0, irq); return pnv_lpc_isa_create(&chip9->lpc, false, errp); } static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) { Pnv10Chip *chip10 = PNV10_CHIP(chip); + qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC); + + qdev_connect_gpio_out(DEVICE(&chip10->lpc), 0, irq); return pnv_lpc_isa_create(&chip10->lpc, false, errp); } @@ -722,7 +736,11 @@ static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) { Pnv10Chip *chip10 = PNV10_CHIP(chip); + pnv_xive2_pic_print_info(&chip10->xive, mon); pnv_psi_pic_print_info(&chip10->psi, mon); + + object_child_foreach_recursive(OBJECT(chip), + pnv_chip_power9_pic_print_info_child, mon); } /* Always give the first 1GB to chip 0 else we won't boot */ @@ -1044,27 +1062,45 @@ static void pnv_chip_power9_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, static void pnv_chip_power10_intc_create(PnvChip *chip, PowerPCCPU *cpu, Error **errp) { + Pnv10Chip *chip10 = PNV10_CHIP(chip); + Error *local_err = NULL; + Object *obj; PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); - /* Will be defined when the interrupt controller is */ - pnv_cpu->intc = NULL; + /* + * The core creates its interrupt presenter but the XIVE2 interrupt + * controller object is initialized afterwards. Hopefully, it's + * only used at runtime. + */ + obj = xive_tctx_create(OBJECT(cpu), XIVE_PRESENTER(&chip10->xive), + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pnv_cpu->intc = obj; } static void pnv_chip_power10_intc_reset(PnvChip *chip, PowerPCCPU *cpu) { - ; + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + xive_tctx_reset(XIVE_TCTX(pnv_cpu->intc)); } static void pnv_chip_power10_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) { PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + xive_tctx_destroy(XIVE_TCTX(pnv_cpu->intc)); pnv_cpu->intc = NULL; } static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, Monitor *mon) { + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); } /* @@ -1116,9 +1152,7 @@ static void pnv_chip_power8_instance_init(Object *obj) object_initialize_child(obj, "homer", &chip8->homer, TYPE_PNV8_HOMER); - if (defaults_enabled()) { - chip8->num_phbs = pcc->num_phbs; - } + chip8->num_phbs = pcc->num_phbs; for (i = 0; i < chip8->num_phbs; i++) { object_initialize_child(obj, "phb[*]", &chip8->phbs[i], TYPE_PNV_PHB3); @@ -1200,8 +1234,6 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) &PNV_PSI(psi8)->xscom_regs); /* Create LPC controller */ - object_property_set_link(OBJECT(&chip8->lpc), "psi", OBJECT(&chip8->psi), - &error_abort); qdev_realize(DEVICE(&chip8->lpc), NULL, &error_fatal); pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs); @@ -1221,12 +1253,12 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) } /* Create the simplified OCC model */ - object_property_set_link(OBJECT(&chip8->occ), "psi", OBJECT(&chip8->psi), - &error_abort); if (!qdev_realize(DEVICE(&chip8->occ), NULL, errp)) { return; } pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip8->occ), 0, + qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_OCC)); /* OCC SRAM model */ memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip), @@ -1366,6 +1398,21 @@ static void pnv_chip_power9_instance_init(Object *obj) } } +static void pnv_chip_quad_realize_one(PnvChip *chip, PnvQuad *eq, + PnvCore *pnv_core) +{ + char eq_name[32]; + int core_id = CPU_CORE(pnv_core)->core_id; + + snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id); + object_initialize_child_with_props(OBJECT(chip), eq_name, eq, + sizeof(*eq), TYPE_PNV_QUAD, + &error_fatal, NULL); + + object_property_set_int(OBJECT(eq), "quad-id", core_id, &error_fatal); + qdev_realize(DEVICE(eq), NULL, &error_fatal); +} + static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) { PnvChip *chip = PNV_CHIP(chip9); @@ -1375,18 +1422,9 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) chip9->quads = g_new0(PnvQuad, chip9->nr_quads); for (i = 0; i < chip9->nr_quads; i++) { - char eq_name[32]; PnvQuad *eq = &chip9->quads[i]; - PnvCore *pnv_core = chip->cores[i * 4]; - int core_id = CPU_CORE(pnv_core)->core_id; - snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id); - object_initialize_child_with_props(OBJECT(chip), eq_name, eq, - sizeof(*eq), TYPE_PNV_QUAD, - &error_fatal, NULL); - - object_property_set_int(OBJECT(eq), "quad-id", core_id, &error_fatal); - qdev_realize(DEVICE(eq), NULL, &error_fatal); + pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4]); pnv_xscom_add_subregion(chip, PNV9_XSCOM_EQ_BASE(eq->quad_id), &eq->xscom_regs); @@ -1469,6 +1507,9 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) /* Processor Service Interface (PSI) Host Bridge */ object_property_set_int(OBJECT(&chip9->psi), "bar", PNV9_PSIHB_BASE(chip), &error_fatal); + /* This is the only device with 4k ESB pages */ + object_property_set_int(OBJECT(&chip9->psi), "shift", XIVE_ESB_4K, + &error_fatal); if (!qdev_realize(DEVICE(&chip9->psi), NULL, errp)) { return; } @@ -1476,8 +1517,6 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) &PNV_PSI(psi9)->xscom_regs); /* LPC */ - object_property_set_link(OBJECT(&chip9->lpc), "psi", OBJECT(&chip9->psi), - &error_abort); if (!qdev_realize(DEVICE(&chip9->lpc), NULL, errp)) { return; } @@ -1489,12 +1528,12 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) (uint64_t) PNV9_LPCM_BASE(chip)); /* Create the simplified OCC model */ - object_property_set_link(OBJECT(&chip9->occ), "psi", OBJECT(&chip9->psi), - &error_abort); if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) { return; } pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in( + DEVICE(&chip9->psi), PSIHB9_IRQ_OCC)); /* OCC SRAM model */ memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), @@ -1553,10 +1592,71 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) static void pnv_chip_power10_instance_init(Object *obj) { + PnvChip *chip = PNV_CHIP(obj); Pnv10Chip *chip10 = PNV10_CHIP(obj); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); + int i; + object_initialize_child(obj, "xive", &chip10->xive, TYPE_PNV_XIVE2); + object_property_add_alias(obj, "xive-fabric", OBJECT(&chip10->xive), + "xive-fabric"); object_initialize_child(obj, "psi", &chip10->psi, TYPE_PNV10_PSI); object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC); + object_initialize_child(obj, "occ", &chip10->occ, TYPE_PNV10_OCC); + object_initialize_child(obj, "homer", &chip10->homer, TYPE_PNV10_HOMER); + + chip->num_pecs = pcc->num_pecs; + + for (i = 0; i < chip->num_pecs; i++) { + object_initialize_child(obj, "pec[*]", &chip10->pecs[i], + TYPE_PNV_PHB5_PEC); + } +} + +static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) +{ + PnvChip *chip = PNV_CHIP(chip10); + int i; + + chip10->nr_quads = DIV_ROUND_UP(chip->nr_cores, 4); + chip10->quads = g_new0(PnvQuad, chip10->nr_quads); + + for (i = 0; i < chip10->nr_quads; i++) { + PnvQuad *eq = &chip10->quads[i]; + + pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4]); + + pnv_xscom_add_subregion(chip, PNV10_XSCOM_EQ_BASE(eq->quad_id), + &eq->xscom_regs); + } +} + +static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp) +{ + Pnv10Chip *chip10 = PNV10_CHIP(chip); + int i; + + for (i = 0; i < chip->num_pecs; i++) { + PnvPhb4PecState *pec = &chip10->pecs[i]; + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_nest_base; + uint32_t pec_pci_base; + + object_property_set_int(OBJECT(pec), "index", i, &error_fatal); + object_property_set_int(OBJECT(pec), "chip-id", chip->chip_id, + &error_fatal); + object_property_set_link(OBJECT(pec), "chip", OBJECT(chip), + &error_fatal); + if (!qdev_realize(DEVICE(pec), NULL, errp)) { + return; + } + + pec_nest_base = pecc->xscom_nest_base(pec); + pec_pci_base = pecc->xscom_pci_base(pec); + + pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); + pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); + } } static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) @@ -1580,9 +1680,39 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) return; } + pnv_chip_power10_quad_realize(chip10, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* XIVE2 interrupt controller (POWER10) */ + object_property_set_int(OBJECT(&chip10->xive), "ic-bar", + PNV10_XIVE2_IC_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip10->xive), "esb-bar", + PNV10_XIVE2_ESB_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip10->xive), "end-bar", + PNV10_XIVE2_END_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip10->xive), "nvpg-bar", + PNV10_XIVE2_NVPG_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip10->xive), "nvc-bar", + PNV10_XIVE2_NVC_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip10->xive), "tm-bar", + PNV10_XIVE2_TM_BASE(chip), &error_fatal); + object_property_set_link(OBJECT(&chip10->xive), "chip", OBJECT(chip), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&chip10->xive), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_XIVE2_BASE, + &chip10->xive.xscom_regs); + /* Processor Service Interface (PSI) Host Bridge */ object_property_set_int(OBJECT(&chip10->psi), "bar", PNV10_PSIHB_BASE(chip), &error_fatal); + /* PSI can now be configured to use 64k ESB pages on POWER10 */ + object_property_set_int(OBJECT(&chip10->psi), "shift", XIVE_ESB_64K, + &error_fatal); if (!qdev_realize(DEVICE(&chip10->psi), NULL, errp)) { return; } @@ -1590,8 +1720,6 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) &PNV_PSI(&chip10->psi)->xscom_regs); /* LPC */ - object_property_set_link(OBJECT(&chip10->lpc), "psi", - OBJECT(&chip10->psi), &error_abort); if (!qdev_realize(DEVICE(&chip10->lpc), NULL, errp)) { return; } @@ -1601,6 +1729,41 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) chip->fw_mr = &chip10->lpc.isa_fw; chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", (uint64_t) PNV10_LPCM_BASE(chip)); + + /* Create the simplified OCC model */ + if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_OCC_BASE, + &chip10->occ.xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip10->occ), 0, qdev_get_gpio_in( + DEVICE(&chip10->psi), PSIHB9_IRQ_OCC)); + + /* OCC SRAM model */ + memory_region_add_subregion(get_system_memory(), + PNV10_OCC_SENSOR_BASE(chip), + &chip10->occ.sram_regs); + + /* HOMER */ + object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) { + return; + } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE, + &chip10->homer.pba_regs); + + /* Homer mmio region */ + memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip), + &chip10->homer.regs); + + /* PHBs */ + pnv_chip_power10_phb_realize(chip, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } } static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) @@ -1627,6 +1790,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) k->xscom_core_base = pnv_chip_power10_xscom_core_base; k->xscom_pcba = pnv_chip_power10_xscom_pcba; dc->desc = "PowerNV Chip POWER10"; + k->num_pecs = PNV10_CHIP_MAX_PEC; device_class_set_parent_realize(dc, pnv_chip_power10_realize, &k->parent_realize); @@ -1813,23 +1977,6 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq) return NULL; } -void pnv_chip_parent_fixup(PnvChip *chip, Object *obj, int index) -{ - Object *parent = OBJECT(chip); - g_autofree char *default_id = - g_strdup_printf("%s[%d]", object_get_typename(obj), index); - - if (obj->parent == parent) { - return; - } - - object_ref(obj); - object_unparent(obj); - object_property_add_child( - parent, DEVICE(obj)->id ? DEVICE(obj)->id : default_id, obj); - object_unref(obj); -} - PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id) { int i; @@ -1924,6 +2071,35 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, return total_count; } +static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) +{ + PnvMachineState *pnv = PNV_MACHINE(xfb); + int total_count = 0; + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + int count; + + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, + priority, logic_serv, match); + + if (count < 0) { + return count; + } + + total_count += count; + } + + return total_count; +} + static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1940,8 +2116,6 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); - - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB3); } static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) @@ -1960,14 +2134,13 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->dt_power_mgt = pnv_dt_power_mgt; - - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB4); } static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); static const char compat[] = "qemu,powernv10\0ibm,powernv"; mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; @@ -1976,6 +2149,8 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->dt_power_mgt = pnv_dt_power_mgt; + + xfc->match_nvt = pnv10_xive_match_nvt; } static bool pnv_machine_get_hb(Object *obj, Error **errp) @@ -2087,6 +2262,10 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv10"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power10_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_FABRIC }, + { }, + }, }, { .name = MACHINE_TYPE_NAME("powernv9"), diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index 75a22ce50b..99f1e8d7f9 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -17,7 +17,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "target/ppc/cpu.h" #include "qemu/log.h" diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index 9a262629b7..ea73919e54 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -332,6 +332,69 @@ static const TypeInfo pnv_homer_power9_type_info = { .class_init = pnv_homer_power9_class_init, }; +static uint64_t pnv_homer_power10_pba_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvHomer *homer = PNV_HOMER(opaque); + PnvChip *chip = homer->chip; + uint32_t reg = addr >> 3; + uint64_t val = 0; + + switch (reg) { + case PBA_BAR0: + val = PNV10_HOMER_BASE(chip); + break; + case PBA_BARMASK0: /* P10 homer region mask */ + val = (PNV10_HOMER_SIZE - 1) & 0x300000; + break; + case PBA_BAR2: /* P10 occ common area */ + val = PNV10_OCC_COMMON_AREA_BASE; + break; + case PBA_BARMASK2: /* P10 occ common area size */ + val = (PNV10_OCC_COMMON_AREA_SIZE - 1) & 0x700000; + break; + default: + qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + return val; +} + +static void pnv_homer_power10_pba_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); +} + +static const MemoryRegionOps pnv_homer_power10_pba_ops = { + .read = pnv_homer_power10_pba_read, + .write = pnv_homer_power10_pba_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void pnv_homer_power10_class_init(ObjectClass *klass, void *data) +{ + PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + + homer->pba_size = PNV10_XSCOM_PBA_SIZE; + homer->pba_ops = &pnv_homer_power10_pba_ops; + homer->homer_size = PNV10_HOMER_SIZE; + homer->homer_ops = &pnv_power9_homer_ops; /* TODO */ + homer->core_max_base = PNV9_CORE_MAX_BASE; +} + +static const TypeInfo pnv_homer_power10_type_info = { + .name = TYPE_PNV10_HOMER, + .parent = TYPE_PNV_HOMER, + .instance_size = sizeof(PnvHomer), + .class_init = pnv_homer_power10_class_init, +}; + static void pnv_homer_realize(DeviceState *dev, Error **errp) { PnvHomer *homer = PNV_HOMER(dev); @@ -377,6 +440,7 @@ static void pnv_homer_register_types(void) type_register_static(&pnv_homer_type_info); type_register_static(&pnv_homer_power8_type_info); type_register_static(&pnv_homer_power9_type_info); + type_register_static(&pnv_homer_power10_type_info); } type_init(pnv_homer_register_types); diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index bcbca3db97..ee890e7ab4 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -422,7 +422,6 @@ static const MemoryRegionOps pnv_lpc_mmio_ops = { static void pnv_lpc_eval_irqs(PnvLpcController *lpc) { bool lpc_to_opb_irq = false; - PnvLpcClass *plc = PNV_LPC_GET_CLASS(lpc); /* Update LPC controller to OPB line */ if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { @@ -445,7 +444,7 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc) lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; /* Reflect the interrupt */ - pnv_psi_irq_set(lpc->psi, plc->psi_irq, lpc->opb_irq_stat != 0); + qemu_set_irq(lpc->psi_irq, lpc->opb_irq_stat != 0); } static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) @@ -637,8 +636,6 @@ static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data) xdc->dt_xscom = pnv_lpc_dt_xscom; - plc->psi_irq = PSIHB_IRQ_LPC_I2C; - device_class_set_parent_realize(dc, pnv_lpc_power8_realize, &plc->parent_realize); } @@ -677,8 +674,6 @@ static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV LPC Controller POWER9"; - plc->psi_irq = PSIHB9_IRQ_LPCHC; - device_class_set_parent_realize(dc, pnv_lpc_power9_realize, &plc->parent_realize); } @@ -706,8 +701,6 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) { PnvLpcController *lpc = PNV_LPC(dev); - assert(lpc->psi); - /* Reg inits */ lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; @@ -746,12 +739,9 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) "lpc-hc", LPC_HC_REGS_OPB_SIZE); memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, &lpc->lpc_hc_regs); -} -static Property pnv_lpc_properties[] = { - DEFINE_PROP_LINK("psi", PnvLpcController, psi, TYPE_PNV_PSI, PnvPsi *), - DEFINE_PROP_END_OF_LIST(), -}; + qdev_init_gpio_out(DEVICE(dev), &lpc->psi_irq, 1); +} static void pnv_lpc_class_init(ObjectClass *klass, void *data) { @@ -759,7 +749,6 @@ static void pnv_lpc_class_init(ObjectClass *klass, void *data) dc->realize = pnv_lpc_realize; dc->desc = "PowerNV LPC Controller"; - device_class_set_props(dc, pnv_lpc_properties); dc->user_creatable = false; } @@ -803,7 +792,7 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) } if (pnv->cpld_irqstate != old_state) { - pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_EXTERNAL, pnv->cpld_irqstate != 0); + qemu_set_irq(lpc->psi_irq, pnv->cpld_irqstate != 0); } } diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 5a716c256e..9fa6d91d31 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -21,6 +21,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" +#include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_xscom.h" @@ -51,13 +52,12 @@ static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) { bool irq_state; - PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); val &= 0xffff000000000000ull; occ->occmisc = val; irq_state = !!(val >> 63); - pnv_psi_irq_set(occ->psi, poc->psi_irq, irq_state); + qemu_set_irq(occ->psi_irq, irq_state); } static uint64_t pnv_occ_power8_xscom_read(void *opaque, hwaddr addr, @@ -168,7 +168,6 @@ static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) poc->xscom_size = PNV_XSCOM_OCC_SIZE; poc->xscom_ops = &pnv_occ_power8_xscom_ops; - poc->psi_irq = PSIHB_IRQ_OCC; } static const TypeInfo pnv_occ_power8_type_info = { @@ -236,10 +235,11 @@ static const MemoryRegionOps pnv_occ_power9_xscom_ops = { static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "PowerNV OCC Controller (POWER9)"; poc->xscom_size = PNV9_XSCOM_OCC_SIZE; poc->xscom_ops = &pnv_occ_power9_xscom_ops; - poc->psi_irq = PSIHB9_IRQ_OCC; } static const TypeInfo pnv_occ_power9_type_info = { @@ -249,13 +249,24 @@ static const TypeInfo pnv_occ_power9_type_info = { .class_init = pnv_occ_power9_class_init, }; +static void pnv_occ_power10_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "PowerNV OCC Controller (POWER10)"; +} + +static const TypeInfo pnv_occ_power10_type_info = { + .name = TYPE_PNV10_OCC, + .parent = TYPE_PNV9_OCC, + .class_init = pnv_occ_power10_class_init, +}; + static void pnv_occ_realize(DeviceState *dev, Error **errp) { PnvOCC *occ = PNV_OCC(dev); PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); - assert(occ->psi); - occ->occmisc = 0; /* XScom region for OCC registers */ @@ -266,12 +277,9 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) memory_region_init_io(&occ->sram_regs, OBJECT(dev), &pnv_occ_sram_ops, occ, "occ-common-area", PNV_OCC_SENSOR_DATA_BLOCK_SIZE); -} -static Property pnv_occ_properties[] = { - DEFINE_PROP_LINK("psi", PnvOCC, psi, TYPE_PNV_PSI, PnvPsi *), - DEFINE_PROP_END_OF_LIST(), -}; + qdev_init_gpio_out(DEVICE(dev), &occ->psi_irq, 1); +} static void pnv_occ_class_init(ObjectClass *klass, void *data) { @@ -279,7 +287,6 @@ static void pnv_occ_class_init(ObjectClass *klass, void *data) dc->realize = pnv_occ_realize; dc->desc = "PowerNV OCC Controller"; - device_class_set_props(dc, pnv_occ_properties); dc->user_creatable = false; } @@ -297,6 +304,7 @@ static void pnv_occ_register_types(void) type_register_static(&pnv_occ_type_info); type_register_static(&pnv_occ_power8_type_info); type_register_static(&pnv_occ_power9_type_info); + type_register_static(&pnv_occ_power10_type_info); } type_init(pnv_occ_register_types); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index cd9a2c5952..98045ed3d2 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -184,8 +184,7 @@ static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val) /* * FSP and PSI interrupts are muxed under the same number. */ -static const uint32_t xivr_regs[] = { - [PSIHB_IRQ_PSI] = PSIHB_XSCOM_XIVR_FSP, +static const uint32_t xivr_regs[PSI_NUM_INTERRUPTS] = { [PSIHB_IRQ_FSP] = PSIHB_XSCOM_XIVR_FSP, [PSIHB_IRQ_OCC] = PSIHB_XSCOM_XIVR_OCC, [PSIHB_IRQ_FSI] = PSIHB_XSCOM_XIVR_FSI, @@ -194,8 +193,7 @@ static const uint32_t xivr_regs[] = { [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_XIVR_EXT, }; -static const uint32_t stat_regs[] = { - [PSIHB_IRQ_PSI] = PSIHB_XSCOM_CR, +static const uint32_t stat_regs[PSI_NUM_INTERRUPTS] = { [PSIHB_IRQ_FSP] = PSIHB_XSCOM_CR, [PSIHB_IRQ_OCC] = PSIHB_XSCOM_IRQ_STAT, [PSIHB_IRQ_FSI] = PSIHB_XSCOM_IRQ_STAT, @@ -204,8 +202,7 @@ static const uint32_t stat_regs[] = { [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_IRQ_STAT, }; -static const uint64_t stat_bits[] = { - [PSIHB_IRQ_PSI] = PSIHB_CR_PSI_IRQ, +static const uint64_t stat_bits[PSI_NUM_INTERRUPTS] = { [PSIHB_IRQ_FSP] = PSIHB_CR_FSP_IRQ, [PSIHB_IRQ_OCC] = PSIHB_IRQ_STAT_OCC, [PSIHB_IRQ_FSI] = PSIHB_IRQ_STAT_FSI, @@ -214,23 +211,14 @@ static const uint64_t stat_bits[] = { [PSIHB_IRQ_EXTERNAL] = PSIHB_IRQ_STAT_EXT, }; -void pnv_psi_irq_set(PnvPsi *psi, int irq, bool state) -{ - PNV_PSI_GET_CLASS(psi)->irq_set(psi, irq, state); -} - -static void pnv_psi_power8_irq_set(PnvPsi *psi, int irq, bool state) +static void pnv_psi_power8_set_irq(void *opaque, int irq, int state) { + PnvPsi *psi = opaque; uint32_t xivr_reg; uint32_t stat_reg; uint32_t src; bool masked; - if (irq > PSIHB_IRQ_EXTERNAL) { - qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); - return; - } - xivr_reg = xivr_regs[irq]; stat_reg = stat_regs[irq]; @@ -515,6 +503,8 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) ics_set_irq_type(ics, i, true); } + qdev_init_gpio_in(dev, pnv_psi_power8_set_irq, ics->nr_irqs); + psi->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); /* XSCOM region for PSI registers */ @@ -576,7 +566,6 @@ static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) ppc->xscom_pcba = PNV_XSCOM_PSIHB_BASE; ppc->xscom_size = PNV_XSCOM_PSIHB_SIZE; ppc->bar_mask = PSIHB_BAR_MASK; - ppc->irq_set = pnv_psi_power8_irq_set; ppc->compat = compat; ppc->compat_size = sizeof(compat); } @@ -601,7 +590,6 @@ static const TypeInfo pnv_psi_power8_info = { #define PSIHB9_IRQ_METHOD PPC_BIT(0) #define PSIHB9_IRQ_RESET PPC_BIT(1) #define PSIHB9_ESB_CI_BASE 0x60 -#define PSIHB9_ESB_CI_64K PPC_BIT(1) #define PSIHB9_ESB_CI_ADDR_MASK PPC_BITMASK(8, 47) #define PSIHB9_ESB_CI_VALID PPC_BIT(63) #define PSIHB9_ESB_NOTIF_ADDR 0x68 @@ -646,7 +634,15 @@ static const TypeInfo pnv_psi_power8_info = { #define PSIHB9_IRQ_STAT_DIO PPC_BIT(12) #define PSIHB9_IRQ_STAT_PSU PPC_BIT(13) -static void pnv_psi_notify(XiveNotifier *xf, uint32_t srcno) +/* P10 register extensions */ + +#define PSIHB10_CR PSIHB9_CR +#define PSIHB10_CR_STORE_EOI PPC_BIT(12) + +#define PSIHB10_ESB_CI_BASE PSIHB9_ESB_CI_BASE +#define PSIHB10_ESB_CI_64K PPC_BIT(1) + +static void pnv_psi_notify(XiveNotifier *xf, uint32_t srcno, bool pq_checked) { PnvPsi *psi = PNV_PSI(xf); uint64_t notif_port = psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)]; @@ -655,9 +651,13 @@ static void pnv_psi_notify(XiveNotifier *xf, uint32_t srcno) uint32_t offset = (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); - uint64_t data = XIVE_TRIGGER_PQ | offset | srcno; + uint64_t data = offset | srcno; MemTxResult result; + if (pq_checked) { + data |= XIVE_TRIGGER_PQ; + } + if (!valid) { return; } @@ -704,6 +704,13 @@ static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr, switch (addr) { case PSIHB9_CR: + if (val & PSIHB10_CR_STORE_EOI) { + psi9->source.esb_flags |= XIVE_SRC_STORE_EOI; + } else { + psi9->source.esb_flags &= ~XIVE_SRC_STORE_EOI; + } + break; + case PSIHB9_SEMR: /* FSP stuff */ break; @@ -715,15 +722,20 @@ static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr, break; case PSIHB9_ESB_CI_BASE: + if (val & PSIHB10_ESB_CI_64K) { + psi9->source.esb_shift = XIVE_ESB_64K; + } else { + psi9->source.esb_shift = XIVE_ESB_4K; + } if (!(val & PSIHB9_ESB_CI_VALID)) { if (psi->regs[reg] & PSIHB9_ESB_CI_VALID) { memory_region_del_subregion(sysmem, &psi9->source.esb_mmio); } } else { if (!(psi->regs[reg] & PSIHB9_ESB_CI_VALID)) { - memory_region_add_subregion(sysmem, - val & ~PSIHB9_ESB_CI_VALID, - &psi9->source.esb_mmio); + hwaddr addr = val & ~(PSIHB9_ESB_CI_VALID | PSIHB10_ESB_CI_64K); + memory_region_add_subregion(sysmem, addr, + &psi9->source.esb_mmio); } } psi->regs[reg] = val; @@ -791,15 +803,11 @@ static const MemoryRegionOps pnv_psi_p9_xscom_ops = { } }; -static void pnv_psi_power9_irq_set(PnvPsi *psi, int irq, bool state) +static void pnv_psi_power9_set_irq(void *opaque, int irq, int state) { + PnvPsi *psi = opaque; uint64_t irq_method = psi->regs[PSIHB_REG(PSIHB9_INTERRUPT_CONTROL)]; - if (irq > PSIHB9_NUM_IRQS) { - qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); - return; - } - if (irq_method & PSIHB9_IRQ_METHOD) { qemu_log_mask(LOG_GUEST_ERROR, "PSI: LSI IRQ method no supported\n"); return; @@ -831,6 +839,7 @@ static void pnv_psi_power9_instance_init(Object *obj) Pnv9Psi *psi = PNV9_PSI(obj); object_initialize_child(obj, "source", &psi->source, TYPE_XIVE_SOURCE); + object_property_add_alias(obj, "shift", OBJECT(&psi->source), "shift"); } static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) @@ -839,8 +848,6 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) XiveSource *xsrc = &PNV9_PSI(psi)->source; int i; - /* This is the only device with 4k ESB pages */ - object_property_set_int(OBJECT(xsrc), "shift", XIVE_ESB_4K, &error_fatal); object_property_set_int(OBJECT(xsrc), "nr-irqs", PSIHB9_NUM_IRQS, &error_fatal); object_property_set_link(OBJECT(xsrc), "xive", OBJECT(psi), &error_abort); @@ -854,6 +861,8 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) psi->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc, xsrc->nr_irqs); + qdev_init_gpio_in(dev, pnv_psi_power9_set_irq, xsrc->nr_irqs); + /* XSCOM region for PSI registers */ pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_p9_xscom_ops, psi, "xscom-psi", PNV9_XSCOM_PSIHB_SIZE); @@ -879,7 +888,6 @@ static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) ppc->xscom_pcba = PNV9_XSCOM_PSIHB_BASE; ppc->xscom_size = PNV9_XSCOM_PSIHB_SIZE; ppc->bar_mask = PSIHB9_BAR_MASK; - ppc->irq_set = pnv_psi_power9_irq_set; ppc->compat = compat; ppc->compat_size = sizeof(compat); diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 9e99625ea9..fea70df45e 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -1063,7 +1063,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) PowerPCCPU *cpu = env_archcpu(env); ppc_tb_t *tb_env; - tb_env = g_malloc0(sizeof(ppc_tb_t)); + tb_env = g_new0(ppc_tb_t, 1); env->tb_env = tb_env; tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; if (is_book3s_arch2x(env)) { @@ -1083,6 +1083,13 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) return &cpu_ppc_set_tb_clk; } +void cpu_ppc_tb_free(CPUPPCState *env) +{ + timer_free(env->tb_env->decr_timer); + timer_free(env->tb_env->hdecr_timer); + g_free(env->tb_env); +} + /* cpu_ppc_hdecr_init may be used if the timer is not used by HDEC emulation */ void cpu_ppc_hdecr_init(CPUPPCState *env) { @@ -1338,8 +1345,8 @@ clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, trace_ppc40x_timers_init(freq); - tb_env = g_malloc0(sizeof(ppc_tb_t)); - ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t)); + tb_env = g_new0(ppc_tb_t, 1); + ppc40x_timer = g_new0(ppc40x_timer_t, 1); env->tb_env = tb_env; tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; @@ -1447,7 +1454,7 @@ int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn), { ppc_dcr_t *dcr_env; - dcr_env = g_malloc0(sizeof(ppc_dcr_t)); + dcr_env = g_new0(ppc_dcr_t, 1); dcr_env->read_error = read_error; dcr_env->write_error = write_error; env->dcr_env = dcr_env; diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 3ae2b36373..a66ad05e3a 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "cpu.h" #include "hw/ppc/ppc.h" @@ -130,7 +129,7 @@ static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base) ref405ep_fpga_t *fpga; MemoryRegion *fpga_memory = g_new(MemoryRegion, 1); - fpga = g_malloc0(sizeof(ref405ep_fpga_t)); + fpga = g_new0(ref405ep_fpga_t, 1); memory_region_init_io(fpga_memory, NULL, &ref405ep_fpga_ops, fpga, "fpga", 0x00000100); memory_region_add_subregion(sysmem, base, fpga_memory); @@ -262,13 +261,13 @@ static void ref405ep_init(MachineState *machine) /* allocate and load BIOS */ if (machine->firmware) { MemoryRegion *bios = g_new(MemoryRegion, 1); - g_autofree char *filename; + g_autofree char *filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + machine->firmware); long bios_size; memory_region_init_rom(bios, NULL, "ef405ep.bios", BIOS_SIZE, &error_fatal); - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware); if (!filename) { error_report("Could not find firmware '%s'", machine->firmware); exit(1); @@ -431,7 +430,7 @@ static void taihu_cpld_init(MemoryRegion *sysmem, uint32_t base) taihu_cpld_t *cpld; MemoryRegion *cpld_memory = g_new(MemoryRegion, 1); - cpld = g_malloc0(sizeof(taihu_cpld_t)); + cpld = g_new0(taihu_cpld_t, 1); memory_region_init_io(cpld_memory, NULL, &taihu_cpld_ops, cpld, "cpld", 0x100); memory_region_add_subregion(sysmem, base, cpld_memory); qemu_register_reset(&taihu_cpld_reset, cpld); diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 8aacd275a6..36c8ba6f3c 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -215,7 +215,7 @@ void ppc4xx_plb_init(CPUPPCState *env) { ppc4xx_plb_t *plb; - plb = g_malloc0(sizeof(ppc4xx_plb_t)); + plb = g_new0(ppc4xx_plb_t, 1); ppc_dcr_register(env, PLB3A0_ACR, plb, &dcr_read_plb, &dcr_write_plb); ppc_dcr_register(env, PLB4A0_ACR, plb, &dcr_read_plb, &dcr_write_plb); ppc_dcr_register(env, PLB0_ACR, plb, &dcr_read_plb, &dcr_write_plb); @@ -300,7 +300,7 @@ static void ppc4xx_pob_init(CPUPPCState *env) { ppc4xx_pob_t *pob; - pob = g_malloc0(sizeof(ppc4xx_pob_t)); + pob = g_new0(ppc4xx_pob_t, 1); ppc_dcr_register(env, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob); ppc_dcr_register(env, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob); ppc_dcr_register(env, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob); @@ -380,7 +380,7 @@ static void ppc4xx_opba_init(hwaddr base) trace_opba_init(base); - opba = g_malloc0(sizeof(ppc4xx_opba_t)); + opba = g_new0(ppc4xx_opba_t, 1); memory_region_init_io(&opba->io, NULL, &opba_ops, opba, "opba", 0x002); memory_region_add_subregion(get_system_memory(), base, &opba->io); qemu_register_reset(ppc4xx_opba_reset, opba); @@ -575,7 +575,7 @@ void ppc405_ebc_init(CPUPPCState *env) { ppc4xx_ebc_t *ebc; - ebc = g_malloc0(sizeof(ppc4xx_ebc_t)); + ebc = g_new0(ppc4xx_ebc_t, 1); qemu_register_reset(&ebc_reset, ebc); ppc_dcr_register(env, EBC0_CFGADDR, ebc, &dcr_read_ebc, &dcr_write_ebc); @@ -658,7 +658,7 @@ static void ppc405_dma_init(CPUPPCState *env, qemu_irq irqs[4]) { ppc405_dma_t *dma; - dma = g_malloc0(sizeof(ppc405_dma_t)); + dma = g_new0(ppc405_dma_t, 1); memcpy(dma->irqs, irqs, 4 * sizeof(qemu_irq)); qemu_register_reset(&ppc405_dma_reset, dma); ppc_dcr_register(env, DMA0_CR0, @@ -757,7 +757,7 @@ static void ppc405_gpio_init(hwaddr base) trace_ppc405_gpio_init(base); - gpio = g_malloc0(sizeof(ppc405_gpio_t)); + gpio = g_new0(ppc405_gpio_t, 1); memory_region_init_io(&gpio->io, NULL, &ppc405_gpio_ops, gpio, "pgio", 0x038); memory_region_add_subregion(get_system_memory(), base, &gpio->io); qemu_register_reset(&ppc405_gpio_reset, gpio); @@ -906,7 +906,7 @@ static void ppc405_ocm_init(CPUPPCState *env) { ppc405_ocm_t *ocm; - ocm = g_malloc0(sizeof(ppc405_ocm_t)); + ocm = g_new0(ppc405_ocm_t, 1); /* XXX: Size is 4096 or 0x04000000 */ memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4 * KiB, &error_fatal); @@ -1148,7 +1148,7 @@ static void ppc4xx_gpt_init(hwaddr base, qemu_irq irqs[5]) trace_ppc4xx_gpt_init(base); - gpt = g_malloc0(sizeof(ppc4xx_gpt_t)); + gpt = g_new0(ppc4xx_gpt_t, 1); for (i = 0; i < 5; i++) { gpt->irqs[i] = irqs[i]; } @@ -1399,7 +1399,7 @@ static void ppc405ep_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[8], { ppc405ep_cpc_t *cpc; - cpc = g_malloc0(sizeof(ppc405ep_cpc_t)); + cpc = g_new0(ppc405ep_cpc_t, 1); memcpy(cpc->clk_setup, clk_setup, PPC405EP_CLK_NB * sizeof(clk_setup_t)); cpc->jtagid = 0x20267049; diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 7fb620b9a0..d5973f2484 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -3,9 +3,9 @@ * * Copyright 2007 IBM Corporation. * Authors: - * Jerone Young - * Christian Ehrhardt - * Hollis Blanchard + * Jerone Young + * Christian Ehrhardt + * Hollis Blanchard * * This work is licensed under the GNU GPL license version 2 or later. * @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/error-report.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/error-report.h" #include "net/net.h" diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index e7d82ae501..737c0896b4 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -389,7 +389,7 @@ void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, { ppc4xx_sdram_t *sdram; - sdram = g_malloc0(sizeof(ppc4xx_sdram_t)); + sdram = g_new0(ppc4xx_sdram_t, 1); sdram->irq = irq; sdram->nbanks = nbanks; sdram->ram_memories = ram_memories; diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index 10b643861f..ca22da196a 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -337,8 +337,8 @@ void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags) booke_timer_t *booke_timer; int ret = 0; - tb_env = g_malloc0(sizeof(ppc_tb_t)); - booke_timer = g_malloc0(sizeof(booke_timer_t)); + tb_env = g_new0(ppc_tb_t, 1); + booke_timer = g_new0(booke_timer_t, 1); cpu->env.tb_env = tb_env; tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 0737234d66..2f24598f55 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/error-report.h" #include "qapi/error.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f0b75b22bb..22569305d2 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -25,8 +25,8 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" +#include "qemu/memalign.h" #include "qapi/error.h" #include "qapi/qapi-events-machine.h" #include "qapi/qapi-events-qdev.h" @@ -1018,9 +1018,9 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) if (reset) { const char *boot_device = spapr->boot_device; - char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + g_autofree char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); size_t cb = 0; - char *bootlist = get_boot_devices_list(&cb); + g_autofree char *bootlist = get_boot_devices_list(&cb); if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { _FDT(fdt_setprop_string(fdt, chosen, "bootargs", @@ -1087,9 +1087,6 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) } spapr_dt_ov5_platform_support(spapr, fdt, chosen); - - g_free(stdout_path); - g_free(bootlist); } _FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5")); @@ -2710,15 +2707,25 @@ static void spapr_machine_init(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); const char *bios_default = spapr->vof ? FW_FILE_NAME_VOF : FW_FILE_NAME; const char *bios_name = machine->firmware ?: bios_default; + g_autofree char *filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; PCIHostState *phb; int i; MemoryRegion *sysmem = get_system_memory(); long load_limit, fw_size; - char *filename; Error *resize_hpt_err = NULL; + if (!filename) { + error_report("Could not find LPAR firmware '%s'", bios_name); + exit(1); + } + fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); + if (fw_size <= 0) { + error_report("Could not load LPAR firmware '%s'", filename); + exit(1); + } + /* * if Secure VM (PEF) support is configured, then initialize it */ @@ -2999,18 +3006,6 @@ static void spapr_machine_init(MachineState *machine) } } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!filename) { - error_report("Could not find LPAR firmware '%s'", bios_name); - exit(1); - } - fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); - if (fw_size <= 0) { - error_report("Could not load LPAR firmware '%s'", filename); - exit(1); - } - g_free(filename); - /* FIXME: Should register things through the MachineState's qdev * interface, this is a legacy from the sPAPREnvironment structure * which predated MachineState but had a similar function */ @@ -3605,7 +3600,7 @@ static SpaprDimmState *spapr_pending_dimm_unplugs_add(SpaprMachineState *spapr, */ ds = spapr_pending_dimm_unplugs_find(spapr, dimm); if (!ds) { - ds = g_malloc0(sizeof(SpaprDimmState)); + ds = g_new0(SpaprDimmState, 1); ds->nr_lmbs = nr_lmbs; ds->dimm = dimm; QTAILQ_INSERT_HEAD(&spapr->pending_dimm_unplugs, ds, next); @@ -4708,14 +4703,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) type_init(spapr_machine_register_##suffix) /* - * pseries-7.0 + * pseries-7.1 */ -static void spapr_machine_7_0_class_options(MachineClass *mc) +static void spapr_machine_7_1_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(7_0, "7.0", true); +DEFINE_SPAPR_MACHINE(7_1, "7.1", true); + +/* + * pseries-7.0 + */ +static void spapr_machine_7_0_class_options(MachineClass *mc) +{ + spapr_machine_7_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); +} + +DEFINE_SPAPR_MACHINE(7_0, "7.0", false); /* * pseries-6.2 diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 6167431271..655ab856a0 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -95,12 +95,12 @@ static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name, } -static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { SpaprCapabilityInfo *cap = opaque; SpaprMachineState *spapr = SPAPR_MACHINE(obj); - char *val = NULL; + g_autofree char *val = NULL; uint8_t value = spapr_get_cap(spapr, cap->index); if (value >= cap->possible->num) { @@ -111,7 +111,6 @@ static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name, val = g_strdup(cap->possible->vals[value]); visit_type_str(v, name, &val, errp); - g_free(val); } static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name, @@ -120,7 +119,7 @@ static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name, SpaprCapabilityInfo *cap = opaque; SpaprMachineState *spapr = SPAPR_MACHINE(obj); uint8_t i; - char *val; + g_autofree char *val = NULL; if (!visit_type_str(v, name, &val, errp)) { return; @@ -128,20 +127,18 @@ static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name, if (!strcmp(val, "?")) { error_setg(errp, "%s", cap->possible->help); - goto out; + return; } for (i = 0; i < cap->possible->num; i++) { if (!strcasecmp(val, cap->possible->vals[i])) { spapr->cmd_line_caps[cap->index] = true; spapr->eff.caps[cap->index] = i; - goto out; + return; } } error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val, cap->name); -out: - g_free(val); } static void spapr_cap_get_pagesize(Object *obj, Visitor *v, const char *name, @@ -933,16 +930,13 @@ void spapr_caps_add_properties(SpaprMachineClass *smc) for (i = 0; i < ARRAY_SIZE(capability_table); i++) { SpaprCapabilityInfo *cap = &capability_table[i]; - char *name = g_strdup_printf("cap-%s", cap->name); - char *desc; + g_autofree char *name = g_strdup_printf("cap-%s", cap->name); + g_autofree char *desc = g_strdup_printf("%s", cap->description); object_class_property_add(klass, name, cap->type, cap->get, cap->set, NULL, cap); - desc = g_strdup_printf("%s", cap->description); object_class_property_set_description(klass, name, desc); - g_free(name); - g_free(desc); } } diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index ed84713960..8a4861f45a 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -189,10 +189,13 @@ static const VMStateDescription vmstate_spapr_cpu_state = { static void spapr_unrealize_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc) { + CPUPPCState *env = &cpu->env; + if (!sc->pre_3_0_migration) { vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); } spapr_irq_cpu_intc_destroy(SPAPR_MACHINE(qdev_get_machine()), cpu); + cpu_ppc_tb_free(env); qdev_unrealize(DEVICE(cpu)); } diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index f8ac0a10df..76bc5d42a0 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -519,8 +519,8 @@ static const VMStateDescription vmstate_spapr_drc = { static void drc_realize(DeviceState *d, Error **errp) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); + g_autofree gchar *link_name = g_strdup_printf("%x", spapr_drc_index(drc)); Object *root_container; - gchar *link_name; const char *child_name; trace_spapr_drc_realize(spapr_drc_index(drc)); @@ -532,12 +532,10 @@ static void drc_realize(DeviceState *d, Error **errp) * existing in the composition tree */ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - link_name = g_strdup_printf("%x", spapr_drc_index(drc)); child_name = object_get_canonical_path_component(OBJECT(drc)); trace_spapr_drc_realize_child(spapr_drc_index(drc), child_name); object_property_add_alias(root_container, link_name, drc->owner, child_name); - g_free(link_name); vmstate_register(VMSTATE_IF(drc), spapr_drc_index(drc), &vmstate_spapr_drc, drc); trace_spapr_drc_realize_complete(spapr_drc_index(drc)); @@ -546,22 +544,20 @@ static void drc_realize(DeviceState *d, Error **errp) static void drc_unrealize(DeviceState *d) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); + g_autofree gchar *name = g_strdup_printf("%x", spapr_drc_index(drc)); Object *root_container; - gchar *name; trace_spapr_drc_unrealize(spapr_drc_index(drc)); vmstate_unregister(VMSTATE_IF(drc), &vmstate_spapr_drc, drc); root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - name = g_strdup_printf("%x", spapr_drc_index(drc)); object_property_del(root_container, name); - g_free(name); } SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type, uint32_t id) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(object_new(type)); - char *prop_name; + g_autofree char *prop_name = NULL; drc->id = id; drc->owner = owner; @@ -570,7 +566,6 @@ SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type, object_property_add_child(owner, prop_name, OBJECT(drc)); object_unref(OBJECT(drc)); qdev_realize(DEVICE(drc), NULL, NULL); - g_free(prop_name); return drc; } @@ -803,11 +798,9 @@ static const TypeInfo spapr_drc_pmem_info = { SpaprDrc *spapr_drc_by_index(uint32_t index) { Object *obj; - gchar *name; - - name = g_strdup_printf("%s/%x", DRC_CONTAINER_PATH, index); + g_autofree gchar *name = g_strdup_printf("%s/%x", DRC_CONTAINER_PATH, + index); obj = object_resolve_path(name, NULL); - g_free(name); return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); } @@ -841,8 +834,14 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) ObjectProperty *prop; ObjectPropertyIterator iter; uint32_t drc_count = 0; - GArray *drc_indexes, *drc_power_domains; - GString *drc_names, *drc_types; + g_autoptr(GArray) drc_indexes = g_array_new(false, true, + sizeof(uint32_t)); + g_autoptr(GArray) drc_power_domains = g_array_new(false, true, + sizeof(uint32_t)); + g_autoptr(GString) drc_names = g_string_set_size(g_string_new(NULL), + sizeof(uint32_t)); + g_autoptr(GString) drc_types = g_string_set_size(g_string_new(NULL), + sizeof(uint32_t)); int ret; /* @@ -857,12 +856,8 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) * reserve the space now and set the offsets accordingly so we * can fill them in later. */ - drc_indexes = g_array_new(false, true, sizeof(uint32_t)); drc_indexes = g_array_set_size(drc_indexes, 1); - drc_power_domains = g_array_new(false, true, sizeof(uint32_t)); drc_power_domains = g_array_set_size(drc_power_domains, 1); - drc_names = g_string_set_size(g_string_new(NULL), sizeof(uint32_t)); - drc_types = g_string_set_size(g_string_new(NULL), sizeof(uint32_t)); /* aliases for all DRConnector objects will be rooted in QOM * composition tree at DRC_CONTAINER_PATH @@ -874,7 +869,7 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) Object *obj; SpaprDrc *drc; SpaprDrcClass *drck; - char *drc_name = NULL; + g_autofree char *drc_name = NULL; uint32_t drc_index, drc_power_domain; if (!strstart(prop->type, "link<", NULL)) { @@ -908,7 +903,6 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) drc_name = spapr_drc_name(drc); drc_names = g_string_append(drc_names, drc_name); drc_names = g_string_insert_len(drc_names, -1, "\0", 1); - g_free(drc_name); /* ibm,drc-types */ drc_types = g_string_append(drc_types, drck->typename); @@ -928,7 +922,7 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) drc_indexes->len * sizeof(uint32_t)); if (ret) { error_report("Couldn't create ibm,drc-indexes property"); - goto out; + return ret; } ret = fdt_setprop(fdt, offset, "ibm,drc-power-domains", @@ -936,29 +930,22 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) drc_power_domains->len * sizeof(uint32_t)); if (ret) { error_report("Couldn't finalize ibm,drc-power-domains property"); - goto out; + return ret; } ret = fdt_setprop(fdt, offset, "ibm,drc-names", drc_names->str, drc_names->len); if (ret) { error_report("Couldn't finalize ibm,drc-names property"); - goto out; + return ret; } ret = fdt_setprop(fdt, offset, "ibm,drc-types", drc_types->str, drc_types->len); if (ret) { error_report("Couldn't finalize ibm,drc-types property"); - goto out; } -out: - g_array_free(drc_indexes, true); - g_array_free(drc_power_domains, true); - g_string_free(drc_names, true); - g_string_free(drc_types, true); - return ret; } diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 630e86282c..4508e40814 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -594,7 +594,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, struct rtas_event_log_v6_hp *hp; entry = g_new(SpaprEventLogEntry, 1); - new_hp = g_malloc0(sizeof(struct hp_extended_log)); + new_hp = g_new0(struct hp_extended_log, 1); entry->extended_log = new_hp; v6hdr = &new_hp->v6hdr; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f008290787..d761a7d0c3 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1473,32 +1473,7 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, return H_FUNCTION; } -#ifndef CONFIG_TCG -static target_ulong h_softmmu(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - g_assert_not_reached(); -} - -static void hypercall_register_softmmu(void) -{ - /* hcall-pft */ - spapr_register_hypercall(H_ENTER, h_softmmu); - spapr_register_hypercall(H_REMOVE, h_softmmu); - spapr_register_hypercall(H_PROTECT, h_softmmu); - spapr_register_hypercall(H_READ, h_softmmu); - - /* hcall-bulk */ - spapr_register_hypercall(H_BULK_REMOVE, h_softmmu); -} -#else -static void hypercall_register_softmmu(void) -{ - /* DO NOTHING */ -} -#endif - -/* TCG only */ +#ifdef CONFIG_TCG #define PRTS_MASK 0x1f static target_ulong h_set_ptbl(PowerPCCPU *cpu, @@ -1596,7 +1571,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu, return H_PARAMETER; } - spapr_cpu->nested_host_state = g_try_malloc(sizeof(CPUPPCState)); + spapr_cpu->nested_host_state = g_try_new(CPUPPCState, 1); if (!spapr_cpu->nested_host_state) { return H_NO_MEM; } @@ -1825,6 +1800,48 @@ out_restore_l1: spapr_cpu->nested_host_state = NULL; } +static void hypercall_register_nested(void) +{ + spapr_register_hypercall(KVMPPC_H_SET_PARTITION_TABLE, h_set_ptbl); + spapr_register_hypercall(KVMPPC_H_ENTER_NESTED, h_enter_nested); + spapr_register_hypercall(KVMPPC_H_TLB_INVALIDATE, h_tlb_invalidate); + spapr_register_hypercall(KVMPPC_H_COPY_TOFROM_GUEST, h_copy_tofrom_guest); +} + +static void hypercall_register_softmmu(void) +{ + /* DO NOTHING */ +} +#else +void spapr_exit_nested(PowerPCCPU *cpu, int excp) +{ + g_assert_not_reached(); +} + +static target_ulong h_softmmu(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + g_assert_not_reached(); +} + +static void hypercall_register_nested(void) +{ + /* DO NOTHING */ +} + +static void hypercall_register_softmmu(void) +{ + /* hcall-pft */ + spapr_register_hypercall(H_ENTER, h_softmmu); + spapr_register_hypercall(H_REMOVE, h_softmmu); + spapr_register_hypercall(H_PROTECT, h_softmmu); + spapr_register_hypercall(H_READ, h_softmmu); + + /* hcall-bulk */ + spapr_register_hypercall(H_BULK_REMOVE, h_softmmu); +} +#endif + static void hypercall_register_types(void) { hypercall_register_softmmu(); @@ -1881,10 +1898,7 @@ static void hypercall_register_types(void) spapr_register_hypercall(KVMPPC_H_UPDATE_DT, h_update_dt); - spapr_register_hypercall(KVMPPC_H_SET_PARTITION_TABLE, h_set_ptbl); - spapr_register_hypercall(KVMPPC_H_ENTER_NESTED, h_enter_nested); - spapr_register_hypercall(KVMPPC_H_TLB_INVALIDATE, h_tlb_invalidate); - spapr_register_hypercall(KVMPPC_H_COPY_TOFROM_GUEST, h_copy_tofrom_guest); + hypercall_register_nested(); } type_init(hypercall_register_types) diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index e9ef7e7646..a64098c375 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/ppc/spapr_numa.h" #include "hw/pci-host/spapr.h" #include "hw/ppc/fdt.h" @@ -431,12 +430,13 @@ int spapr_numa_write_assoc_lookup_arrays(SpaprMachineState *spapr, void *fdt, int max_distance_ref_points = get_max_dist_ref_points(spapr); int nb_numa_nodes = machine->numa_state->num_nodes; int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; - uint32_t *int_buf, *cur_index, buf_len; - int ret, i; + g_autofree uint32_t *int_buf = NULL; + uint32_t *cur_index; + int i; /* ibm,associativity-lookup-arrays */ - buf_len = (nr_nodes * max_distance_ref_points + 2) * sizeof(uint32_t); - cur_index = int_buf = g_malloc0(buf_len); + int_buf = g_new0(uint32_t, nr_nodes * max_distance_ref_points + 2); + cur_index = int_buf; int_buf[0] = cpu_to_be32(nr_nodes); /* Number of entries per associativity list */ int_buf[1] = cpu_to_be32(max_distance_ref_points); @@ -451,11 +451,9 @@ int spapr_numa_write_assoc_lookup_arrays(SpaprMachineState *spapr, void *fdt, sizeof(uint32_t) * max_distance_ref_points); cur_index += max_distance_ref_points; } - ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, - (cur_index - int_buf) * sizeof(uint32_t)); - g_free(int_buf); - return ret; + return fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", + int_buf, (cur_index - int_buf) * sizeof(uint32_t)); } static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 5bfd4aa9e5..b2f5fbef0c 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1978,7 +1978,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) * our memory slot is of page size granularity. */ if (kvm_enabled()) { - msi_window_size = qemu_real_host_page_size; + msi_window_size = qemu_real_host_page_size(); } memory_region_init_io(&sphb->msiwindow, OBJECT(sphb), &spapr_msi_ops, spapr, diff --git a/hw/ppc/spapr_pci_nvlink2.c b/hw/ppc/spapr_pci_nvlink2.c index 7fb0cf4d04..63b476c8f7 100644 --- a/hw/ppc/spapr_pci_nvlink2.c +++ b/hw/ppc/spapr_pci_nvlink2.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu-common.h" #include "hw/pci/pci.h" #include "hw/pci-host/spapr.h" #include "hw/ppc/spapr_numa.h" @@ -320,7 +319,7 @@ void spapr_phb_nvgpu_populate_dt(SpaprPhbState *sphb, void *fdt, int bus_off, void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt) { int i, j, linkidx, npuoff; - char *npuname; + g_autofree char *npuname = NULL; if (!sphb->nvgpus) { return; @@ -333,11 +332,10 @@ void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt) _FDT(fdt_setprop_cell(fdt, npuoff, "#size-cells", 0)); /* Advertise NPU as POWER9 so the guest can enable NPU2 contexts */ _FDT((fdt_setprop_string(fdt, npuoff, "compatible", "ibm,power9-npu"))); - g_free(npuname); for (i = 0, linkidx = 0; i < sphb->nvgpus->num; ++i) { for (j = 0; j < sphb->nvgpus->slots[i].linknum; ++j) { - char *linkname = g_strdup_printf("link@%d", linkidx); + g_autofree char *linkname = g_strdup_printf("link@%d", linkidx); int off = fdt_add_subnode(fdt, npuoff, linkname); _FDT(off); @@ -347,7 +345,6 @@ void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt) _FDT((fdt_setprop_cell(fdt, off, "phandle", PHANDLE_NVLINK(sphb, i, j)))); _FDT((fdt_setprop_cell(fdt, off, "ibm,npu-link-index", linkidx))); - g_free(linkname); ++linkidx; } } @@ -360,7 +357,8 @@ void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt) &error_abort); uint64_t size = object_property_get_uint(nv_mrobj, "size", NULL); uint64_t mem_reg[2] = { cpu_to_be64(nvslot->gpa), cpu_to_be64(size) }; - char *mem_name = g_strdup_printf("memory@%"PRIx64, nvslot->gpa); + g_autofree char *mem_name = g_strdup_printf("memory@%"PRIx64, + nvslot->gpa); int off = fdt_add_subnode(fdt, 0, mem_name); _FDT(off); @@ -378,7 +376,6 @@ void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt) sizeof(mem_reg)))); _FDT((fdt_setprop_cell(fdt, off, "phandle", PHANDLE_GPURAM(sphb, i)))); - g_free(mem_name); } } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index b476382ae6..d58b65e88f 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -279,30 +279,29 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, switch (parameter) { case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { - char *param_val = g_strdup_printf("MaxEntCap=%d," - "DesMem=%" PRIu64 "," - "DesProcs=%d," - "MaxPlatProcs=%d", - ms->smp.max_cpus, - ms->ram_size / MiB, - ms->smp.cpus, - ms->smp.max_cpus); + g_autofree char *param_val = g_strdup_printf("MaxEntCap=%d," + "DesMem=%" PRIu64 "," + "DesProcs=%d," + "MaxPlatProcs=%d", + ms->smp.max_cpus, + ms->ram_size / MiB, + ms->smp.cpus, + ms->smp.max_cpus); if (pcc->n_host_threads > 0) { - char *hostthr_val, *old = param_val; - /* * Add HostThrs property. This property is not present in PAPR but * is expected by some guests to communicate the number of physical * host threads per core on the system so that they can scale * information which varies based on the thread configuration. */ - hostthr_val = g_strdup_printf(",HostThrs=%d", pcc->n_host_threads); + g_autofree char *hostthr_val = g_strdup_printf(",HostThrs=%d", + pcc->n_host_threads); + char *old = param_val; + param_val = g_strconcat(param_val, hostthr_val, NULL); - g_free(hostthr_val); g_free(old); } ret = sysparm_st(buffer, length, param_val, strlen(param_val) + 1); - g_free(param_val); break; } case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: { @@ -475,16 +474,16 @@ static void rtas_ibm_nmi_interlock(PowerPCCPU *cpu, if (spapr->fwnmi_machine_check_interlock != cpu->vcpu_id) { /* - * The vCPU that hit the NMI should invoke "ibm,nmi-interlock" + * The vCPU that hit the NMI should invoke "ibm,nmi-interlock" * This should be PARAM_ERROR, but Linux calls "ibm,nmi-interlock" - * for system reset interrupts, despite them not being interlocked. - * PowerVM silently ignores this and returns success here. Returning - * failure causes Linux to print the error "FWNMI: nmi-interlock - * failed: -3", although no other apparent ill effects, this is a - * regression for the user when enabling FWNMI. So for now, match - * PowerVM. When most Linux clients are fixed, this could be - * changed. - */ + * for system reset interrupts, despite them not being interlocked. + * PowerVM silently ignores this and returns success here. Returning + * failure causes Linux to print the error "FWNMI: nmi-interlock + * failed: -3", although no other apparent ill effects, this is a + * regression for the user when enabling FWNMI. So for now, match + * PowerVM. When most Linux clients are fixed, this could be + * changed. + */ rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; } diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c index 3e826e1308..13d339c807 100644 --- a/hw/ppc/spapr_rtas_ddw.c +++ b/hw/ppc/spapr_rtas_ddw.c @@ -72,6 +72,7 @@ static uint32_t spapr_page_mask_to_query_mask(uint64_t page_mask) const struct { int shift; uint32_t mask; } masks[] = { { 12, RTAS_DDW_PGSIZE_4K }, { 16, RTAS_DDW_PGSIZE_64K }, + { 21, RTAS_DDW_PGSIZE_2M }, { 24, RTAS_DDW_PGSIZE_16M }, { 25, RTAS_DDW_PGSIZE_32M }, { 26, RTAS_DDW_PGSIZE_64M }, diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index 94a5510e4e..d55b4b0c50 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -32,7 +32,7 @@ #include "hw/ppc/spapr.h" #include "migration/vmstate.h" #include "qapi/error.h" -#include "qapi/qapi-events-misc-target.h" +#include "qapi/qapi-events-misc.h" #include "qemu/cutils.h" #include "qemu/module.h" @@ -97,6 +97,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t nret, target_ulong rets) { SpaprRtcState *rtc = &spapr->rtc; + g_autofree const char *qom_path = NULL; struct tm tm; time_t new_s; int64_t host_ns; @@ -120,7 +121,8 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, SpaprMachineState *spapr, } /* Generate a monitor event for the change */ - qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); + qom_path = object_get_canonical_path(OBJECT(rtc)); + qapi_event_send_rtc_change(qemu_timedate_diff(&tm), qom_path); host_ns = qemu_clock_get_ns(rtc_clock); diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_softmmu.c index 4ee03c83e4..5170a33369 100644 --- a/hw/ppc/spapr_softmmu.c +++ b/hw/ppc/spapr_softmmu.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #include "cpu.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index 2454086744..e10af35a18 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/reset.h" diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index b975ed29ca..9d4fec2c04 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -726,7 +726,7 @@ void spapr_dt_vdevice(SpaprVioBus *bus, void *fdt) gchar *spapr_vio_stdout_path(SpaprVioBus *bus) { SpaprVioDevice *dev; - char *name, *path; + g_autofree char *name = NULL; dev = spapr_vty_get_default(bus); if (!dev) { @@ -734,8 +734,6 @@ gchar *spapr_vio_stdout_path(SpaprVioBus *bus) } name = spapr_vio_get_dev_name(DEVICE(dev)); - path = g_strdup_printf("/vdevice/%s", name); - g_free(name); - return path; + return g_strdup_printf("/vdevice/%s", name); } diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c index a33f940c32..09f29be0b9 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -4,7 +4,6 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 9c575403b8..b67a709ddc 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "cpu.h" diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index 2b63a62875..18c3f92317 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -10,7 +10,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/timer.h" #include "qemu/range.h" #include "qemu/units.h" @@ -294,7 +293,7 @@ static uint32_t vof_setprop(MachineState *ms, void *fdt, Vof *vof, uint32_t nodeph, uint32_t pname, uint32_t valaddr, uint32_t vallen) { - char propname[OF_PROPNAME_LEN_MAX + 1]; + char propname[OF_PROPNAME_LEN_MAX + 1] = ""; uint32_t ret = PROM_ERROR; int offset, rc; char trval[64] = ""; diff --git a/hw/rdma/vmw/pvrdma_dev_ring.c b/hw/rdma/vmw/pvrdma_dev_ring.c index 42130667a7..598e6adc5e 100644 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ b/hw/rdma/vmw/pvrdma_dev_ring.c @@ -41,7 +41,7 @@ int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, qatomic_set(&ring->ring_state->cons_head, 0); */ ring->npages = npages; - ring->pages = g_malloc0(npages * sizeof(void *)); + ring->pages = g_new0(void *, npages); for (i = 0; i < npages; i++) { if (!tbl[i]) { diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 91206dbb8e..58db0b8e3b 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -159,13 +159,13 @@ static void free_dsr(PVRDMADev *dev) free_dev_ring(pci_dev, &dev->dsr_info.cq, dev->dsr_info.cq_ring_state); rdma_pci_dma_unmap(pci_dev, dev->dsr_info.req, - sizeof(union pvrdma_cmd_req)); + sizeof(union pvrdma_cmd_req)); rdma_pci_dma_unmap(pci_dev, dev->dsr_info.rsp, - sizeof(union pvrdma_cmd_resp)); + sizeof(union pvrdma_cmd_resp)); rdma_pci_dma_unmap(pci_dev, dev->dsr_info.dsr, - sizeof(struct pvrdma_device_shared_region)); + sizeof(struct pvrdma_device_shared_region)); dev->dsr_info.dsr = NULL; } @@ -249,7 +249,8 @@ static void init_dsr_dev_caps(PVRDMADev *dev) { struct pvrdma_device_shared_region *dsr; - if (dev->dsr_info.dsr == NULL) { + if (!dev->dsr_info.dsr) { + /* Buggy or malicious guest driver */ rdma_error_report("Can't initialized DSR"); return; } @@ -608,7 +609,7 @@ static void pvrdma_realize(PCIDevice *pdev, Error **errp) rdma_info_report("Initializing device %s %x.%x", pdev->name, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - if (TARGET_PAGE_SIZE != qemu_real_host_page_size) { + if (TARGET_PAGE_SIZE != qemu_real_host_page_size()) { error_setg(errp, "Target page size must be the same as host page size"); return; } diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index 8050287a6c..bd7cbf2bdf 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -154,7 +154,7 @@ void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle) CompHandlerCtx *comp_ctx; /* Prepare CQE */ - comp_ctx = g_malloc(sizeof(CompHandlerCtx)); + comp_ctx = g_new(CompHandlerCtx, 1); comp_ctx->dev = dev; comp_ctx->cq_handle = qp->send_cq_handle; comp_ctx->cqe.wr_id = wqe->hdr.wr_id; @@ -217,7 +217,7 @@ void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) CompHandlerCtx *comp_ctx; /* Prepare CQE */ - comp_ctx = g_malloc(sizeof(CompHandlerCtx)); + comp_ctx = g_new(CompHandlerCtx, 1); comp_ctx->dev = dev; comp_ctx->cq_handle = qp->recv_cq_handle; comp_ctx->cqe.wr_id = wqe->hdr.wr_id; @@ -259,7 +259,7 @@ void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle) CompHandlerCtx *comp_ctx; /* Prepare CQE */ - comp_ctx = g_malloc(sizeof(CompHandlerCtx)); + comp_ctx = g_new(CompHandlerCtx, 1); comp_ctx->dev = dev; comp_ctx->cq_handle = srq->recv_cq_handle; comp_ctx->cqe.wr_id = wqe->hdr.wr_id; diff --git a/hw/remote/iohub.c b/hw/remote/iohub.c index 547d597f0f..40dfee4bad 100644 --- a/hw/remote/iohub.c +++ b/hw/remote/iohub.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" diff --git a/hw/remote/machine.c b/hw/remote/machine.c index 952105eab5..92d71d47bb 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -14,7 +14,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/remote/machine.h" #include "exec/memory.h" diff --git a/hw/remote/memory.c b/hw/remote/memory.c index 6e21ab1a45..6d60da91e0 100644 --- a/hw/remote/memory.c +++ b/hw/remote/memory.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/remote/memory.h" #include "exec/ram_addr.h" diff --git a/hw/remote/message.c b/hw/remote/message.c index 11d729845c..50f6bf2d49 100644 --- a/hw/remote/message.c +++ b/hw/remote/message.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/remote/machine.h" #include "io/channel.h" diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c index 7e841820e5..2a4aa651ca 100644 --- a/hw/remote/mpqemu-link.c +++ b/hw/remote/mpqemu-link.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/module.h" #include "hw/remote/mpqemu-link.h" diff --git a/hw/remote/proxy-memory-listener.c b/hw/remote/proxy-memory-listener.c index 0e893f3189..eb9918fe72 100644 --- a/hw/remote/proxy-memory-listener.c +++ b/hw/remote/proxy-memory-listener.c @@ -7,7 +7,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/compiler.h" #include "qemu/int128.h" diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index bad164299d..1c7786b52c 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -7,7 +7,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/remote/proxy.h" #include "hw/pci/pci.h" diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index 4f21254219..333e5ac443 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/notify.h" diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index d2d869aaad..79ff61c464 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -34,6 +34,7 @@ config RISCV_VIRT imply PCI_DEVICES imply VIRTIO_VGA imply TEST_DEVICES + imply TPM_TIS_SYSBUS select RISCV_NUMA select GOLDFISH_RTC select MSI_NONBROKEN @@ -42,10 +43,13 @@ config RISCV_VIRT select PFLASH_CFI01 select SERIAL select RISCV_ACLINT + select RISCV_APLIC + select RISCV_IMSIC select SIFIVE_PLIC select SIFIVE_TEST select VIRTIO_MMIO select FW_CFG_DMA + select PLATFORM_BUS config SIFIVE_E bool diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index cae74fcbcd..57a41df8e9 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/error-report.h" @@ -213,9 +212,9 @@ hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size, return *start + size; } -uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt) +uint64_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt) { - uint32_t temp, fdt_addr; + uint64_t temp, fdt_addr; hwaddr dram_end = dram_base + mem_size; int ret, fdtsize = fdt_totalsize(fdt); @@ -230,7 +229,7 @@ uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt) * Thus, put it at an 16MB aligned address that less than fdt size from the * end of dram or 3GB whichever is lesser. */ - temp = MIN(dram_end, 3072 * MiB); + temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 16 * MiB); ret = fdt_pack(fdt); @@ -286,13 +285,15 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts hwaddr start_addr, hwaddr rom_base, hwaddr rom_size, uint64_t kernel_entry, - uint32_t fdt_load_addr, void *fdt) + uint64_t fdt_load_addr, void *fdt) { int i; uint32_t start_addr_hi32 = 0x00000000; + uint32_t fdt_load_addr_hi32 = 0x00000000; if (!riscv_is_32bit(harts)) { start_addr_hi32 = start_addr >> 32; + fdt_load_addr_hi32 = fdt_load_addr >> 32; } /* reset vector */ uint32_t reset_vec[10] = { @@ -305,7 +306,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts start_addr, /* start: .dword */ start_addr_hi32, fdt_load_addr, /* fdt_laddr: .dword */ - 0x00000000, + fdt_load_addr_hi32, /* fw_dyn: */ }; if (riscv_is_32bit(harts)) { diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index cafd1fc9ae..10a5d0e501 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -571,7 +571,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) "linux,initrd-end", end); } - if (machine->kernel_cmdline) { + if (machine->kernel_cmdline && *machine->kernel_cmdline) { qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs", machine->kernel_cmdline); } diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index aec7cfa33f..2d401dcb23 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -34,13 +34,15 @@ static const MemMapEntry ibex_memmap[] = { [IBEX_DEV_FLASH] = { 0x20000000, 0x80000 }, [IBEX_DEV_UART] = { 0x40000000, 0x1000 }, [IBEX_DEV_GPIO] = { 0x40040000, 0x1000 }, - [IBEX_DEV_SPI] = { 0x40050000, 0x1000 }, + [IBEX_DEV_SPI_DEVICE] = { 0x40050000, 0x1000 }, [IBEX_DEV_I2C] = { 0x40080000, 0x1000 }, [IBEX_DEV_PATTGEN] = { 0x400e0000, 0x1000 }, [IBEX_DEV_TIMER] = { 0x40100000, 0x1000 }, [IBEX_DEV_SENSOR_CTRL] = { 0x40110000, 0x1000 }, [IBEX_DEV_OTP_CTRL] = { 0x40130000, 0x4000 }, [IBEX_DEV_USBDEV] = { 0x40150000, 0x1000 }, + [IBEX_DEV_SPI_HOST0] = { 0x40300000, 0x1000 }, + [IBEX_DEV_SPI_HOST1] = { 0x40310000, 0x1000 }, [IBEX_DEV_PWRMGR] = { 0x40400000, 0x1000 }, [IBEX_DEV_RSTMGR] = { 0x40410000, 0x1000 }, [IBEX_DEV_CLKMGR] = { 0x40420000, 0x1000 }, @@ -118,11 +120,18 @@ static void lowrisc_ibex_soc_init(Object *obj) object_initialize_child(obj, "uart", &s->uart, TYPE_IBEX_UART); object_initialize_child(obj, "timer", &s->timer, TYPE_IBEX_TIMER); + + for (int i = 0; i < OPENTITAN_NUM_SPI_HOSTS; i++) { + object_initialize_child(obj, "spi_host[*]", &s->spi_host[i], + TYPE_IBEX_SPI_HOST); + } } static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) { const MemMapEntry *memmap = ibex_memmap; + DeviceState *dev; + SysBusDevice *busdev; MachineState *ms = MACHINE(qdev_get_machine()); LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc); MemoryRegion *sys_mem = get_system_memory(); @@ -207,10 +216,35 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) qdev_get_gpio_in(DEVICE(qemu_get_cpu(0)), IRQ_M_TIMER)); + /* SPI-Hosts */ + for (int i = 0; i < OPENTITAN_NUM_SPI_HOSTS; ++i) { + dev = DEVICE(&(s->spi_host[i])); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi_host[i]), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, memmap[IBEX_DEV_SPI_HOST0 + i].base); + + switch (i) { + case OPENTITAN_SPI_HOST0: + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_SPI_HOST0_ERR_IRQ)); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_SPI_HOST0_SPI_EVENT_IRQ)); + break; + case OPENTITAN_SPI_HOST1: + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_SPI_HOST1_ERR_IRQ)); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_SPI_HOST1_SPI_EVENT_IRQ)); + break; + } + } + create_unimplemented_device("riscv.lowrisc.ibex.gpio", memmap[IBEX_DEV_GPIO].base, memmap[IBEX_DEV_GPIO].size); - create_unimplemented_device("riscv.lowrisc.ibex.spi", - memmap[IBEX_DEV_SPI].base, memmap[IBEX_DEV_SPI].size); + create_unimplemented_device("riscv.lowrisc.ibex.spi_device", + memmap[IBEX_DEV_SPI_DEVICE].base, memmap[IBEX_DEV_SPI_DEVICE].size); create_unimplemented_device("riscv.lowrisc.ibex.i2c", memmap[IBEX_DEV_I2C].base, memmap[IBEX_DEV_I2C].size); create_unimplemented_device("riscv.lowrisc.ibex.pattgen", diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 7fbc7dea42..cc8c7637cb 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -511,7 +511,7 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, g_free(nodename); update_bootargs: - if (cmdline) { + if (cmdline && *cmdline) { qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); } } diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index d059a67f9b..068ba3493e 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -174,10 +174,11 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, riscv_socket_fdt_write_distance_matrix(mc, fdt); - if (cmdline) { - qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "/htif"); + + if (cmdline && *cmdline) { qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); - qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "/htif"); } } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e3068d6126..3326f4db96 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -28,37 +28,69 @@ #include "hw/qdev-properties.h" #include "hw/char/serial.h" #include "target/riscv/cpu.h" +#include "hw/core/sysbus-fdt.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" #include "hw/intc/riscv_aclint.h" +#include "hw/intc/riscv_aplic.h" +#include "hw/intc/riscv_imsic.h" #include "hw/intc/sifive_plic.h" #include "hw/misc/sifive_test.h" +#include "hw/platform-bus.h" #include "chardev/char.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" +#include "sysemu/tpm.h" #include "hw/pci/pci.h" #include "hw/pci-host/gpex.h" #include "hw/display/ramfb.h" +/* + * The virt machine physical address space used by some of the devices + * namely ACLINT, PLIC, APLIC, and IMSIC depend on number of Sockets, + * number of CPUs, and number of IMSIC guest files. + * + * Various limits defined by VIRT_SOCKETS_MAX_BITS, VIRT_CPUS_MAX_BITS, + * and VIRT_IRQCHIP_MAX_GUESTS_BITS are tuned for maximum utilization + * of virt machine physical address space. + */ + +#define VIRT_IMSIC_GROUP_MAX_SIZE (1U << IMSIC_MMIO_GROUP_MIN_SHIFT) +#if VIRT_IMSIC_GROUP_MAX_SIZE < \ + IMSIC_GROUP_SIZE(VIRT_CPUS_MAX_BITS, VIRT_IRQCHIP_MAX_GUESTS_BITS) +#error "Can't accomodate single IMSIC group in address space" +#endif + +#define VIRT_IMSIC_MAX_SIZE (VIRT_SOCKETS_MAX * \ + VIRT_IMSIC_GROUP_MAX_SIZE) +#if 0x4000000 < VIRT_IMSIC_MAX_SIZE +#error "Can't accomodate all IMSIC groups in address space" +#endif + static const MemMapEntry virt_memmap[] = { - [VIRT_DEBUG] = { 0x0, 0x100 }, - [VIRT_MROM] = { 0x1000, 0xf000 }, - [VIRT_TEST] = { 0x100000, 0x1000 }, - [VIRT_RTC] = { 0x101000, 0x1000 }, - [VIRT_CLINT] = { 0x2000000, 0x10000 }, - [VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 }, - [VIRT_PCIE_PIO] = { 0x3000000, 0x10000 }, - [VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) }, - [VIRT_UART0] = { 0x10000000, 0x100 }, - [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, - [VIRT_FW_CFG] = { 0x10100000, 0x18 }, - [VIRT_FLASH] = { 0x20000000, 0x4000000 }, - [VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 }, - [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 }, - [VIRT_DRAM] = { 0x80000000, 0x0 }, + [VIRT_DEBUG] = { 0x0, 0x100 }, + [VIRT_MROM] = { 0x1000, 0xf000 }, + [VIRT_TEST] = { 0x100000, 0x1000 }, + [VIRT_RTC] = { 0x101000, 0x1000 }, + [VIRT_CLINT] = { 0x2000000, 0x10000 }, + [VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 }, + [VIRT_PCIE_PIO] = { 0x3000000, 0x10000 }, + [VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 }, + [VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) }, + [VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) }, + [VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) }, + [VIRT_UART0] = { 0x10000000, 0x100 }, + [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, + [VIRT_FW_CFG] = { 0x10100000, 0x18 }, + [VIRT_FLASH] = { 0x20000000, 0x4000000 }, + [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, + [VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE }, + [VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 }, + [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 }, + [VIRT_DRAM] = { 0x80000000, 0x0 }, }; /* PCIe high mmio is fixed for RV32 */ @@ -133,12 +165,13 @@ static void virt_flash_map(RISCVVirtState *s, sysmem); } -static void create_pcie_irq_map(void *fdt, char *nodename, - uint32_t plic_phandle) +static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, + uint32_t irqchip_phandle) { int pin, dev; - uint32_t - full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * FDT_INT_MAP_WIDTH] = {}; + uint32_t irq_map_stride = 0; + uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * + FDT_MAX_INT_MAP_WIDTH] = {}; uint32_t *irq_map = full_irq_map; /* This code creates a standard swizzle of interrupts such that @@ -156,23 +189,31 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); int i = 0; + /* Fill PCI address cells */ irq_map[i] = cpu_to_be32(devfn << 8); - i += FDT_PCI_ADDR_CELLS; + + /* Fill PCI Interrupt cells */ irq_map[i] = cpu_to_be32(pin + 1); - i += FDT_PCI_INT_CELLS; - irq_map[i++] = cpu_to_be32(plic_phandle); - i += FDT_PLIC_ADDR_CELLS; - irq_map[i] = cpu_to_be32(irq_nr); + /* Fill interrupt controller phandle and cells */ + irq_map[i++] = cpu_to_be32(irqchip_phandle); + irq_map[i++] = cpu_to_be32(irq_nr); + if (s->aia_type != VIRT_AIA_TYPE_NONE) { + irq_map[i++] = cpu_to_be32(0x4); + } - irq_map += FDT_INT_MAP_WIDTH; + if (!irq_map_stride) { + irq_map_stride = i; + } + irq_map += irq_map_stride; } } - qemu_fdt_setprop(fdt, nodename, "interrupt-map", - full_irq_map, sizeof(full_irq_map)); + qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, + GPEX_NUM_IRQS * GPEX_NUM_IRQS * + irq_map_stride * sizeof(uint32_t)); qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", 0x1800, 0, 0, 0x7); @@ -193,8 +234,14 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, cpu_name = g_strdup_printf("/cpus/cpu@%d", s->soc[socket].hartid_base + cpu); qemu_fdt_add_subnode(mc->fdt, cpu_name); - qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", - (is_32_bit) ? "riscv,sv32" : "riscv,sv48"); + if (riscv_feature(&s->soc[socket].harts[cpu].env, + RISCV_FEATURE_MMU)) { + qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", + (is_32_bit) ? "riscv,sv32" : "riscv,sv48"); + } else { + qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", + "riscv,none"); + } name = riscv_isa_string(&s->soc[socket].harts[cpu]); qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name); g_free(name); @@ -298,7 +345,7 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, { int cpu; char *name; - unsigned long addr; + unsigned long addr, size; uint32_t aclint_cells_size; uint32_t *aclint_mswi_cells; uint32_t *aclint_sswi_cells; @@ -319,29 +366,38 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, } aclint_cells_size = s->soc[socket].num_harts * sizeof(uint32_t) * 2; - addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); - name = g_strdup_printf("/soc/mswi@%lx", addr); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-mswi"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", - 0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE); - qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", - aclint_mswi_cells, aclint_cells_size); - qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0); - riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); - g_free(name); + if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { + addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); + name = g_strdup_printf("/soc/mswi@%lx", addr); + qemu_fdt_add_subnode(mc->fdt, name); + qemu_fdt_setprop_string(mc->fdt, name, "compatible", + "riscv,aclint-mswi"); + qemu_fdt_setprop_cells(mc->fdt, name, "reg", + 0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE); + qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", + aclint_mswi_cells, aclint_cells_size); + qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0); + riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); + g_free(name); + } - addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE + - (memmap[VIRT_CLINT].size * socket); + if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { + addr = memmap[VIRT_CLINT].base + + (RISCV_ACLINT_DEFAULT_MTIMER_SIZE * socket); + size = RISCV_ACLINT_DEFAULT_MTIMER_SIZE; + } else { + addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE + + (memmap[VIRT_CLINT].size * socket); + size = memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE; + } name = g_strdup_printf("/soc/mtimer@%lx", addr); qemu_fdt_add_subnode(mc->fdt, name); qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-mtimer"); qemu_fdt_setprop_cells(mc->fdt, name, "reg", 0x0, addr + RISCV_ACLINT_DEFAULT_MTIME, - 0x0, memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE - - RISCV_ACLINT_DEFAULT_MTIME, + 0x0, size - RISCV_ACLINT_DEFAULT_MTIME, 0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP, 0x0, RISCV_ACLINT_DEFAULT_MTIME); qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", @@ -349,19 +405,22 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); g_free(name); - addr = memmap[VIRT_ACLINT_SSWI].base + - (memmap[VIRT_ACLINT_SSWI].size * socket); - name = g_strdup_printf("/soc/sswi@%lx", addr); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-sswi"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", - 0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size); - qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", - aclint_sswi_cells, aclint_cells_size); - qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0); - riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); - g_free(name); + if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { + addr = memmap[VIRT_ACLINT_SSWI].base + + (memmap[VIRT_ACLINT_SSWI].size * socket); + name = g_strdup_printf("/soc/sswi@%lx", addr); + qemu_fdt_add_subnode(mc->fdt, name); + qemu_fdt_setprop_string(mc->fdt, name, "compatible", + "riscv,aclint-sswi"); + qemu_fdt_setprop_cells(mc->fdt, name, "reg", + 0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size); + qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", + aclint_sswi_cells, aclint_cells_size); + qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0); + riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); + g_free(name); + } g_free(aclint_mswi_cells); g_free(aclint_mtimer_cells); @@ -404,8 +463,6 @@ static void create_fdt_socket_plic(RISCVVirtState *s, plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket); plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr); qemu_fdt_add_subnode(mc->fdt, plic_name); - qemu_fdt_setprop_cell(mc->fdt, plic_name, - "#address-cells", FDT_PLIC_ADDR_CELLS); qemu_fdt_setprop_cell(mc->fdt, plic_name, "#interrupt-cells", FDT_PLIC_INT_CELLS); qemu_fdt_setprop_string_array(mc->fdt, plic_name, "compatible", @@ -420,22 +477,256 @@ static void create_fdt_socket_plic(RISCVVirtState *s, riscv_socket_fdt_write_id(mc, mc->fdt, plic_name, socket); qemu_fdt_setprop_cell(mc->fdt, plic_name, "phandle", plic_phandles[socket]); + + platform_bus_add_all_fdt_nodes(mc->fdt, plic_name, + memmap[VIRT_PLATFORM_BUS].base, + memmap[VIRT_PLATFORM_BUS].size, + VIRT_PLATFORM_BUS_IRQ); + g_free(plic_name); g_free(plic_cells); } +static uint32_t imsic_num_bits(uint32_t count) +{ + uint32_t ret = 0; + + while (BIT(ret) < count) { + ret++; + } + + return ret; +} + +static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, + uint32_t *phandle, uint32_t *intc_phandles, + uint32_t *msi_m_phandle, uint32_t *msi_s_phandle) +{ + int cpu, socket; + char *imsic_name; + MachineState *mc = MACHINE(s); + uint32_t imsic_max_hart_per_socket, imsic_guest_bits; + uint32_t *imsic_cells, *imsic_regs, imsic_addr, imsic_size; + + *msi_m_phandle = (*phandle)++; + *msi_s_phandle = (*phandle)++; + imsic_cells = g_new0(uint32_t, mc->smp.cpus * 2); + imsic_regs = g_new0(uint32_t, riscv_socket_count(mc) * 4); + + /* M-level IMSIC node */ + for (cpu = 0; cpu < mc->smp.cpus; cpu++) { + imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + imsic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_EXT); + } + imsic_max_hart_per_socket = 0; + for (socket = 0; socket < riscv_socket_count(mc); socket++) { + imsic_addr = memmap[VIRT_IMSIC_M].base + + socket * VIRT_IMSIC_GROUP_MAX_SIZE; + imsic_size = IMSIC_HART_SIZE(0) * s->soc[socket].num_harts; + imsic_regs[socket * 4 + 0] = 0; + imsic_regs[socket * 4 + 1] = cpu_to_be32(imsic_addr); + imsic_regs[socket * 4 + 2] = 0; + imsic_regs[socket * 4 + 3] = cpu_to_be32(imsic_size); + if (imsic_max_hart_per_socket < s->soc[socket].num_harts) { + imsic_max_hart_per_socket = s->soc[socket].num_harts; + } + } + imsic_name = g_strdup_printf("/soc/imsics@%lx", + (unsigned long)memmap[VIRT_IMSIC_M].base); + qemu_fdt_add_subnode(mc->fdt, imsic_name); + qemu_fdt_setprop_string(mc->fdt, imsic_name, "compatible", + "riscv,imsics"); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "#interrupt-cells", + FDT_IMSIC_INT_CELLS); + qemu_fdt_setprop(mc->fdt, imsic_name, "interrupt-controller", + NULL, 0); + qemu_fdt_setprop(mc->fdt, imsic_name, "msi-controller", + NULL, 0); + qemu_fdt_setprop(mc->fdt, imsic_name, "interrupts-extended", + imsic_cells, mc->smp.cpus * sizeof(uint32_t) * 2); + qemu_fdt_setprop(mc->fdt, imsic_name, "reg", imsic_regs, + riscv_socket_count(mc) * sizeof(uint32_t) * 4); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,num-ids", + VIRT_IRQCHIP_NUM_MSIS); + qemu_fdt_setprop_cells(mc->fdt, imsic_name, "riscv,ipi-id", + VIRT_IRQCHIP_IPI_MSI); + if (riscv_socket_count(mc) > 1) { + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,hart-index-bits", + imsic_num_bits(imsic_max_hart_per_socket)); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-bits", + imsic_num_bits(riscv_socket_count(mc))); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-shift", + IMSIC_MMIO_GROUP_MIN_SHIFT); + } + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "phandle", *msi_m_phandle); + + platform_bus_add_all_fdt_nodes(mc->fdt, imsic_name, + memmap[VIRT_PLATFORM_BUS].base, + memmap[VIRT_PLATFORM_BUS].size, + VIRT_PLATFORM_BUS_IRQ); + + g_free(imsic_name); + + /* S-level IMSIC node */ + for (cpu = 0; cpu < mc->smp.cpus; cpu++) { + imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + imsic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT); + } + imsic_guest_bits = imsic_num_bits(s->aia_guests + 1); + imsic_max_hart_per_socket = 0; + for (socket = 0; socket < riscv_socket_count(mc); socket++) { + imsic_addr = memmap[VIRT_IMSIC_S].base + + socket * VIRT_IMSIC_GROUP_MAX_SIZE; + imsic_size = IMSIC_HART_SIZE(imsic_guest_bits) * + s->soc[socket].num_harts; + imsic_regs[socket * 4 + 0] = 0; + imsic_regs[socket * 4 + 1] = cpu_to_be32(imsic_addr); + imsic_regs[socket * 4 + 2] = 0; + imsic_regs[socket * 4 + 3] = cpu_to_be32(imsic_size); + if (imsic_max_hart_per_socket < s->soc[socket].num_harts) { + imsic_max_hart_per_socket = s->soc[socket].num_harts; + } + } + imsic_name = g_strdup_printf("/soc/imsics@%lx", + (unsigned long)memmap[VIRT_IMSIC_S].base); + qemu_fdt_add_subnode(mc->fdt, imsic_name); + qemu_fdt_setprop_string(mc->fdt, imsic_name, "compatible", + "riscv,imsics"); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "#interrupt-cells", + FDT_IMSIC_INT_CELLS); + qemu_fdt_setprop(mc->fdt, imsic_name, "interrupt-controller", + NULL, 0); + qemu_fdt_setprop(mc->fdt, imsic_name, "msi-controller", + NULL, 0); + qemu_fdt_setprop(mc->fdt, imsic_name, "interrupts-extended", + imsic_cells, mc->smp.cpus * sizeof(uint32_t) * 2); + qemu_fdt_setprop(mc->fdt, imsic_name, "reg", imsic_regs, + riscv_socket_count(mc) * sizeof(uint32_t) * 4); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,num-ids", + VIRT_IRQCHIP_NUM_MSIS); + qemu_fdt_setprop_cells(mc->fdt, imsic_name, "riscv,ipi-id", + VIRT_IRQCHIP_IPI_MSI); + if (imsic_guest_bits) { + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,guest-index-bits", + imsic_guest_bits); + } + if (riscv_socket_count(mc) > 1) { + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,hart-index-bits", + imsic_num_bits(imsic_max_hart_per_socket)); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-bits", + imsic_num_bits(riscv_socket_count(mc))); + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-shift", + IMSIC_MMIO_GROUP_MIN_SHIFT); + } + qemu_fdt_setprop_cell(mc->fdt, imsic_name, "phandle", *msi_s_phandle); + g_free(imsic_name); + + g_free(imsic_regs); + g_free(imsic_cells); +} + +static void create_fdt_socket_aplic(RISCVVirtState *s, + const MemMapEntry *memmap, int socket, + uint32_t msi_m_phandle, + uint32_t msi_s_phandle, + uint32_t *phandle, + uint32_t *intc_phandles, + uint32_t *aplic_phandles) +{ + int cpu; + char *aplic_name; + uint32_t *aplic_cells; + unsigned long aplic_addr; + MachineState *mc = MACHINE(s); + uint32_t aplic_m_phandle, aplic_s_phandle; + + aplic_m_phandle = (*phandle)++; + aplic_s_phandle = (*phandle)++; + aplic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2); + + /* M-level APLIC node */ + for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { + aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + aplic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_EXT); + } + aplic_addr = memmap[VIRT_APLIC_M].base + + (memmap[VIRT_APLIC_M].size * socket); + aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); + qemu_fdt_add_subnode(mc->fdt, aplic_name); + qemu_fdt_setprop_string(mc->fdt, aplic_name, "compatible", "riscv,aplic"); + qemu_fdt_setprop_cell(mc->fdt, aplic_name, + "#interrupt-cells", FDT_APLIC_INT_CELLS); + qemu_fdt_setprop(mc->fdt, aplic_name, "interrupt-controller", NULL, 0); + if (s->aia_type == VIRT_AIA_TYPE_APLIC) { + qemu_fdt_setprop(mc->fdt, aplic_name, "interrupts-extended", + aplic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 2); + } else { + qemu_fdt_setprop_cell(mc->fdt, aplic_name, "msi-parent", + msi_m_phandle); + } + qemu_fdt_setprop_cells(mc->fdt, aplic_name, "reg", + 0x0, aplic_addr, 0x0, memmap[VIRT_APLIC_M].size); + qemu_fdt_setprop_cell(mc->fdt, aplic_name, "riscv,num-sources", + VIRT_IRQCHIP_NUM_SOURCES); + qemu_fdt_setprop_cell(mc->fdt, aplic_name, "riscv,children", + aplic_s_phandle); + qemu_fdt_setprop_cells(mc->fdt, aplic_name, "riscv,delegate", + aplic_s_phandle, 0x1, VIRT_IRQCHIP_NUM_SOURCES); + riscv_socket_fdt_write_id(mc, mc->fdt, aplic_name, socket); + qemu_fdt_setprop_cell(mc->fdt, aplic_name, "phandle", aplic_m_phandle); + g_free(aplic_name); + + /* S-level APLIC node */ + for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { + aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + aplic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT); + } + aplic_addr = memmap[VIRT_APLIC_S].base + + (memmap[VIRT_APLIC_S].size * socket); + aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); + qemu_fdt_add_subnode(mc->fdt, aplic_name); + qemu_fdt_setprop_string(mc->fdt, aplic_name, "compatible", "riscv,aplic"); + qemu_fdt_setprop_cell(mc->fdt, aplic_name, + "#interrupt-cells", FDT_APLIC_INT_CELLS); + qemu_fdt_setprop(mc->fdt, aplic_name, "interrupt-controller", NULL, 0); + if (s->aia_type == VIRT_AIA_TYPE_APLIC) { + qemu_fdt_setprop(mc->fdt, aplic_name, "interrupts-extended", + aplic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 2); + } else { + qemu_fdt_setprop_cell(mc->fdt, aplic_name, "msi-parent", + msi_s_phandle); + } + qemu_fdt_setprop_cells(mc->fdt, aplic_name, "reg", + 0x0, aplic_addr, 0x0, memmap[VIRT_APLIC_S].size); + qemu_fdt_setprop_cell(mc->fdt, aplic_name, "riscv,num-sources", + VIRT_IRQCHIP_NUM_SOURCES); + riscv_socket_fdt_write_id(mc, mc->fdt, aplic_name, socket); + qemu_fdt_setprop_cell(mc->fdt, aplic_name, "phandle", aplic_s_phandle); + + platform_bus_add_all_fdt_nodes(mc->fdt, aplic_name, + memmap[VIRT_PLATFORM_BUS].base, + memmap[VIRT_PLATFORM_BUS].size, + VIRT_PLATFORM_BUS_IRQ); + + g_free(aplic_name); + + g_free(aplic_cells); + aplic_phandles[socket] = aplic_s_phandle; +} + static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, bool is_32_bit, uint32_t *phandle, uint32_t *irq_mmio_phandle, uint32_t *irq_pcie_phandle, - uint32_t *irq_virtio_phandle) + uint32_t *irq_virtio_phandle, + uint32_t *msi_pcie_phandle) { - int socket; char *clust_name; - uint32_t *intc_phandles; + int socket, phandle_pos; MachineState *mc = MACHINE(s); - uint32_t xplic_phandles[MAX_NODES]; + uint32_t msi_m_phandle = 0, msi_s_phandle = 0; + uint32_t *intc_phandles, xplic_phandles[MAX_NODES]; qemu_fdt_add_subnode(mc->fdt, "/cpus"); qemu_fdt_setprop_cell(mc->fdt, "/cpus", "timebase-frequency", @@ -444,32 +735,55 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cell(mc->fdt, "/cpus", "#address-cells", 0x1); qemu_fdt_add_subnode(mc->fdt, "/cpus/cpu-map"); + intc_phandles = g_new0(uint32_t, mc->smp.cpus); + + phandle_pos = mc->smp.cpus; for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) { + phandle_pos -= s->soc[socket].num_harts; + clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); qemu_fdt_add_subnode(mc->fdt, clust_name); - intc_phandles = g_new0(uint32_t, s->soc[socket].num_harts); - create_fdt_socket_cpus(s, socket, clust_name, phandle, - is_32_bit, intc_phandles); + is_32_bit, &intc_phandles[phandle_pos]); create_fdt_socket_memory(s, memmap, socket); + g_free(clust_name); + if (!kvm_enabled()) { if (s->have_aclint) { - create_fdt_socket_aclint(s, memmap, socket, intc_phandles); + create_fdt_socket_aclint(s, memmap, socket, + &intc_phandles[phandle_pos]); } else { - create_fdt_socket_clint(s, memmap, socket, intc_phandles); + create_fdt_socket_clint(s, memmap, socket, + &intc_phandles[phandle_pos]); } } - - create_fdt_socket_plic(s, memmap, socket, phandle, - intc_phandles, xplic_phandles); - - g_free(intc_phandles); - g_free(clust_name); } + if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { + create_fdt_imsic(s, memmap, phandle, intc_phandles, + &msi_m_phandle, &msi_s_phandle); + *msi_pcie_phandle = msi_s_phandle; + } + + phandle_pos = mc->smp.cpus; + for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) { + phandle_pos -= s->soc[socket].num_harts; + + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + create_fdt_socket_plic(s, memmap, socket, phandle, + &intc_phandles[phandle_pos], xplic_phandles); + } else { + create_fdt_socket_aplic(s, memmap, socket, + msi_m_phandle, msi_s_phandle, phandle, + &intc_phandles[phandle_pos], xplic_phandles); + } + } + + g_free(intc_phandles); + for (socket = 0; socket < riscv_socket_count(mc); socket++) { if (socket == 0) { *irq_mmio_phandle = xplic_phandles[socket]; @@ -505,13 +819,20 @@ static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap, 0x0, memmap[VIRT_VIRTIO].size); qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", irq_virtio_phandle); - qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", VIRTIO_IRQ + i); + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", + VIRTIO_IRQ + i); + } else { + qemu_fdt_setprop_cells(mc->fdt, name, "interrupts", + VIRTIO_IRQ + i, 0x4); + } g_free(name); } } static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, - uint32_t irq_pcie_phandle) + uint32_t irq_pcie_phandle, + uint32_t msi_pcie_phandle) { char *name; MachineState *mc = MACHINE(s); @@ -531,6 +852,9 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cells(mc->fdt, name, "bus-range", 0, memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1); qemu_fdt_setprop(mc->fdt, name, "dma-coherent", NULL, 0); + if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { + qemu_fdt_setprop_cell(mc->fdt, name, "msi-parent", msi_pcie_phandle); + } qemu_fdt_setprop_cells(mc->fdt, name, "reg", 0, memmap[VIRT_PCIE_ECAM].base, 0, memmap[VIRT_PCIE_ECAM].size); qemu_fdt_setprop_sized_cells(mc->fdt, name, "ranges", @@ -543,7 +867,7 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size); - create_pcie_irq_map(mc->fdt, name, irq_pcie_phandle); + create_pcie_irq_map(s, mc->fdt, name, irq_pcie_phandle); g_free(name); } @@ -602,7 +926,11 @@ static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, 0x0, memmap[VIRT_UART0].size); qemu_fdt_setprop_cell(mc->fdt, name, "clock-frequency", 3686400); qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", irq_mmio_phandle); - qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", UART0_IRQ); + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", UART0_IRQ); + } else { + qemu_fdt_setprop_cells(mc->fdt, name, "interrupts", UART0_IRQ, 0x4); + } qemu_fdt_add_subnode(mc->fdt, "/chosen"); qemu_fdt_setprop_string(mc->fdt, "/chosen", "stdout-path", name); @@ -623,7 +951,11 @@ static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, 0x0, memmap[VIRT_RTC].base, 0x0, memmap[VIRT_RTC].size); qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", irq_mmio_phandle); - qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", RTC_IRQ); + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", RTC_IRQ); + } else { + qemu_fdt_setprop_cells(mc->fdt, name, "interrupts", RTC_IRQ, 0x4); + } g_free(name); } @@ -648,7 +980,7 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, uint64_t mem_size, const char *cmdline, bool is_32_bit) { MachineState *mc = MACHINE(s); - uint32_t phandle = 1, irq_mmio_phandle = 1; + uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1; uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; if (mc->dtb) { @@ -678,11 +1010,12 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cell(mc->fdt, "/soc", "#address-cells", 0x2); create_fdt_sockets(s, memmap, is_32_bit, &phandle, - &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle); + &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle, + &msi_pcie_phandle); create_fdt_virtio(s, memmap, irq_virtio_phandle); - create_fdt_pcie(s, memmap, irq_pcie_phandle); + create_fdt_pcie(s, memmap, irq_pcie_phandle, msi_pcie_phandle); create_fdt_reset(s, memmap, &phandle); @@ -693,7 +1026,7 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, create_fdt_flash(s, memmap); update_bootargs: - if (cmdline) { + if (cmdline && *cmdline) { qemu_fdt_setprop_string(mc->fdt, "/chosen", "bootargs", cmdline); } } @@ -704,7 +1037,7 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, hwaddr high_mmio_base, hwaddr high_mmio_size, hwaddr pio_base, - DeviceState *plic) + DeviceState *irqchip) { DeviceState *dev; MemoryRegion *ecam_alias, *ecam_reg; @@ -738,7 +1071,7 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); for (i = 0; i < GPEX_NUM_IRQS; i++) { - irq = qdev_get_gpio_in(plic, PCIE_IRQ + i); + irq = qdev_get_gpio_in(irqchip, PCIE_IRQ + i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ + i); @@ -769,89 +1102,21 @@ static FWCfgState *create_fw_cfg(const MachineState *mc) return fw_cfg; } -static void virt_machine_init(MachineState *machine) +static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket, + int base_hartid, int hart_count) { - const MemMapEntry *memmap = virt_memmap; - RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); - MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *mask_rom = g_new(MemoryRegion, 1); - char *plic_hart_config, *soc_name; - target_ulong start_addr = memmap[VIRT_DRAM].base; - target_ulong firmware_end_addr, kernel_start_addr; - uint32_t fdt_load_addr; - uint64_t kernel_entry; - DeviceState *mmio_plic, *virtio_plic, *pcie_plic; - int i, base_hartid, hart_count; + DeviceState *ret; + char *plic_hart_config; - /* Check socket count limit */ - if (VIRT_SOCKETS_MAX < riscv_socket_count(machine)) { - error_report("number of sockets/nodes should be less than %d", - VIRT_SOCKETS_MAX); - exit(1); - } + /* Per-socket PLIC hart topology configuration string */ + plic_hart_config = riscv_plic_hart_config_string(hart_count); - /* Initialize sockets */ - mmio_plic = virtio_plic = pcie_plic = NULL; - for (i = 0; i < riscv_socket_count(machine); i++) { - if (!riscv_socket_check_hartids(machine, i)) { - error_report("discontinuous hartids in socket%d", i); - exit(1); - } - - base_hartid = riscv_socket_first_hartid(machine, i); - if (base_hartid < 0) { - error_report("can't find hartid base for socket%d", i); - exit(1); - } - - hart_count = riscv_socket_hart_count(machine, i); - if (hart_count < 0) { - error_report("can't find hart count for socket%d", i); - exit(1); - } - - soc_name = g_strdup_printf("soc%d", i); - object_initialize_child(OBJECT(machine), soc_name, &s->soc[i], - TYPE_RISCV_HART_ARRAY); - g_free(soc_name); - object_property_set_str(OBJECT(&s->soc[i]), "cpu-type", - machine->cpu_type, &error_abort); - object_property_set_int(OBJECT(&s->soc[i]), "hartid-base", - base_hartid, &error_abort); - object_property_set_int(OBJECT(&s->soc[i]), "num-harts", - hart_count, &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort); - - if (!kvm_enabled()) { - /* Per-socket CLINT */ - riscv_aclint_swi_create( - memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size, - base_hartid, hart_count, false); - riscv_aclint_mtimer_create( - memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size + - RISCV_ACLINT_SWI_SIZE, - RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, - RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, - RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); - - /* Per-socket ACLINT SSWI */ - if (s->have_aclint) { - riscv_aclint_swi_create( - memmap[VIRT_ACLINT_SSWI].base + - i * memmap[VIRT_ACLINT_SSWI].size, - base_hartid, hart_count, true); - } - } - - /* Per-socket PLIC hart topology configuration string */ - plic_hart_config = riscv_plic_hart_config_string(hart_count); - - /* Per-socket PLIC */ - s->plic[i] = sifive_plic_create( - memmap[VIRT_PLIC].base + i * memmap[VIRT_PLIC].size, + /* Per-socket PLIC */ + ret = sifive_plic_create( + memmap[VIRT_PLIC].base + socket * memmap[VIRT_PLIC].size, plic_hart_config, hart_count, base_hartid, - VIRT_PLIC_NUM_SOURCES, - VIRT_PLIC_NUM_PRIORITIES, + VIRT_IRQCHIP_NUM_SOURCES, + ((1U << VIRT_IRQCHIP_NUM_PRIO_BITS) - 1), VIRT_PLIC_PRIORITY_BASE, VIRT_PLIC_PENDING_BASE, VIRT_PLIC_ENABLE_BASE, @@ -859,62 +1124,117 @@ static void virt_machine_init(MachineState *machine) VIRT_PLIC_CONTEXT_BASE, VIRT_PLIC_CONTEXT_STRIDE, memmap[VIRT_PLIC].size); - g_free(plic_hart_config); - /* Try to use different PLIC instance based device type */ - if (i == 0) { - mmio_plic = s->plic[i]; - virtio_plic = s->plic[i]; - pcie_plic = s->plic[i]; + g_free(plic_hart_config); + + return ret; +} + +static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, + const MemMapEntry *memmap, int socket, + int base_hartid, int hart_count) +{ + int i; + hwaddr addr; + uint32_t guest_bits; + DeviceState *aplic_m; + bool msimode = (aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) ? true : false; + + if (msimode) { + /* Per-socket M-level IMSICs */ + addr = memmap[VIRT_IMSIC_M].base + socket * VIRT_IMSIC_GROUP_MAX_SIZE; + for (i = 0; i < hart_count; i++) { + riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), + base_hartid + i, true, 1, + VIRT_IRQCHIP_NUM_MSIS); } - if (i == 1) { - virtio_plic = s->plic[i]; - pcie_plic = s->plic[i]; - } - if (i == 2) { - pcie_plic = s->plic[i]; + + /* Per-socket S-level IMSICs */ + guest_bits = imsic_num_bits(aia_guests + 1); + addr = memmap[VIRT_IMSIC_S].base + socket * VIRT_IMSIC_GROUP_MAX_SIZE; + for (i = 0; i < hart_count; i++) { + riscv_imsic_create(addr + i * IMSIC_HART_SIZE(guest_bits), + base_hartid + i, false, 1 + aia_guests, + VIRT_IRQCHIP_NUM_MSIS); } } - if (riscv_is_32bit(&s->soc[0])) { -#if HOST_LONG_BITS == 64 - /* limit RAM size in a 32-bit system */ - if (machine->ram_size > 10 * GiB) { - machine->ram_size = 10 * GiB; - error_report("Limiting RAM size to 10 GiB"); - } -#endif - virt_high_pcie_memmap.base = VIRT32_HIGH_PCIE_MMIO_BASE; - virt_high_pcie_memmap.size = VIRT32_HIGH_PCIE_MMIO_SIZE; - } else { - virt_high_pcie_memmap.size = VIRT64_HIGH_PCIE_MMIO_SIZE; - virt_high_pcie_memmap.base = memmap[VIRT_DRAM].base + machine->ram_size; - virt_high_pcie_memmap.base = - ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size); + /* Per-socket M-level APLIC */ + aplic_m = riscv_aplic_create( + memmap[VIRT_APLIC_M].base + socket * memmap[VIRT_APLIC_M].size, + memmap[VIRT_APLIC_M].size, + (msimode) ? 0 : base_hartid, + (msimode) ? 0 : hart_count, + VIRT_IRQCHIP_NUM_SOURCES, + VIRT_IRQCHIP_NUM_PRIO_BITS, + msimode, true, NULL); + + if (aplic_m) { + /* Per-socket S-level APLIC */ + riscv_aplic_create( + memmap[VIRT_APLIC_S].base + socket * memmap[VIRT_APLIC_S].size, + memmap[VIRT_APLIC_S].size, + (msimode) ? 0 : base_hartid, + (msimode) ? 0 : hart_count, + VIRT_IRQCHIP_NUM_SOURCES, + VIRT_IRQCHIP_NUM_PRIO_BITS, + msimode, false, aplic_m); } - /* register system main memory (actual RAM) */ - memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base, - machine->ram); + return aplic_m; +} - /* create device tree */ - create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, - riscv_is_32bit(&s->soc[0])); +static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) +{ + DeviceState *dev; + SysBusDevice *sysbus; + const MemMapEntry *memmap = virt_memmap; + int i; + MemoryRegion *sysmem = get_system_memory(); - /* boot rom */ - memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom", - memmap[VIRT_MROM].size, &error_fatal); - memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, - mask_rom); + dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); + dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); + qdev_prop_set_uint32(dev, "num_irqs", VIRT_PLATFORM_BUS_NUM_IRQS); + qdev_prop_set_uint32(dev, "mmio_size", memmap[VIRT_PLATFORM_BUS].size); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + s->platform_bus_dev = dev; + + sysbus = SYS_BUS_DEVICE(dev); + for (i = 0; i < VIRT_PLATFORM_BUS_NUM_IRQS; i++) { + int irq = VIRT_PLATFORM_BUS_IRQ + i; + sysbus_connect_irq(sysbus, i, qdev_get_gpio_in(irqchip, irq)); + } + + memory_region_add_subregion(sysmem, + memmap[VIRT_PLATFORM_BUS].base, + sysbus_mmio_get_region(sysbus, 0)); +} + +static void virt_machine_done(Notifier *notifier, void *data) +{ + RISCVVirtState *s = container_of(notifier, RISCVVirtState, + machine_done); + const MemMapEntry *memmap = virt_memmap; + MachineState *machine = MACHINE(s); + target_ulong start_addr = memmap[VIRT_DRAM].base; + target_ulong firmware_end_addr, kernel_start_addr; + uint32_t fdt_load_addr; + uint64_t kernel_entry; /* * Only direct boot kernel is currently supported for KVM VM, - * so the "-bios" parameter is ignored and treated like "-bios none" - * when KVM is enabled. + * so the "-bios" parameter is not supported when KVM is enabled. */ if (kvm_enabled()) { - g_free(machine->firmware); - machine->firmware = g_strdup("none"); + if (machine->firmware) { + if (strcmp(machine->firmware, "none")) { + error_report("Machine mode firmware is not supported in " + "combination with KVM."); + exit(1); + } + } else { + machine->firmware = g_strdup("none"); + } } if (riscv_is_32bit(&s->soc[0])) { @@ -982,6 +1302,149 @@ static void virt_machine_init(MachineState *machine) if (kvm_enabled()) { riscv_setup_direct_kernel(kernel_entry, fdt_load_addr); } +} + +static void virt_machine_init(MachineState *machine) +{ + const MemMapEntry *memmap = virt_memmap; + RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + char *soc_name; + DeviceState *mmio_irqchip, *virtio_irqchip, *pcie_irqchip; + int i, base_hartid, hart_count; + + /* Check socket count limit */ + if (VIRT_SOCKETS_MAX < riscv_socket_count(machine)) { + error_report("number of sockets/nodes should be less than %d", + VIRT_SOCKETS_MAX); + exit(1); + } + + /* Initialize sockets */ + mmio_irqchip = virtio_irqchip = pcie_irqchip = NULL; + for (i = 0; i < riscv_socket_count(machine); i++) { + if (!riscv_socket_check_hartids(machine, i)) { + error_report("discontinuous hartids in socket%d", i); + exit(1); + } + + base_hartid = riscv_socket_first_hartid(machine, i); + if (base_hartid < 0) { + error_report("can't find hartid base for socket%d", i); + exit(1); + } + + hart_count = riscv_socket_hart_count(machine, i); + if (hart_count < 0) { + error_report("can't find hart count for socket%d", i); + exit(1); + } + + soc_name = g_strdup_printf("soc%d", i); + object_initialize_child(OBJECT(machine), soc_name, &s->soc[i], + TYPE_RISCV_HART_ARRAY); + g_free(soc_name); + object_property_set_str(OBJECT(&s->soc[i]), "cpu-type", + machine->cpu_type, &error_abort); + object_property_set_int(OBJECT(&s->soc[i]), "hartid-base", + base_hartid, &error_abort); + object_property_set_int(OBJECT(&s->soc[i]), "num-harts", + hart_count, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort); + + if (!kvm_enabled()) { + if (s->have_aclint) { + if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { + /* Per-socket ACLINT MTIMER */ + riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + + i * RISCV_ACLINT_DEFAULT_MTIMER_SIZE, + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, + base_hartid, hart_count, + RISCV_ACLINT_DEFAULT_MTIMECMP, + RISCV_ACLINT_DEFAULT_MTIME, + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); + } else { + /* Per-socket ACLINT MSWI, MTIMER, and SSWI */ + riscv_aclint_swi_create(memmap[VIRT_CLINT].base + + i * memmap[VIRT_CLINT].size, + base_hartid, hart_count, false); + riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + + i * memmap[VIRT_CLINT].size + + RISCV_ACLINT_SWI_SIZE, + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, + base_hartid, hart_count, + RISCV_ACLINT_DEFAULT_MTIMECMP, + RISCV_ACLINT_DEFAULT_MTIME, + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); + riscv_aclint_swi_create(memmap[VIRT_ACLINT_SSWI].base + + i * memmap[VIRT_ACLINT_SSWI].size, + base_hartid, hart_count, true); + } + } else { + /* Per-socket SiFive CLINT */ + riscv_aclint_swi_create( + memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size, + base_hartid, hart_count, false); + riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + + i * memmap[VIRT_CLINT].size + RISCV_ACLINT_SWI_SIZE, + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, + RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); + } + } + + /* Per-socket interrupt controller */ + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + s->irqchip[i] = virt_create_plic(memmap, i, + base_hartid, hart_count); + } else { + s->irqchip[i] = virt_create_aia(s->aia_type, s->aia_guests, + memmap, i, base_hartid, + hart_count); + } + + /* Try to use different IRQCHIP instance based device type */ + if (i == 0) { + mmio_irqchip = s->irqchip[i]; + virtio_irqchip = s->irqchip[i]; + pcie_irqchip = s->irqchip[i]; + } + if (i == 1) { + virtio_irqchip = s->irqchip[i]; + pcie_irqchip = s->irqchip[i]; + } + if (i == 2) { + pcie_irqchip = s->irqchip[i]; + } + } + + if (riscv_is_32bit(&s->soc[0])) { +#if HOST_LONG_BITS == 64 + /* limit RAM size in a 32-bit system */ + if (machine->ram_size > 10 * GiB) { + machine->ram_size = 10 * GiB; + error_report("Limiting RAM size to 10 GiB"); + } +#endif + virt_high_pcie_memmap.base = VIRT32_HIGH_PCIE_MMIO_BASE; + virt_high_pcie_memmap.size = VIRT32_HIGH_PCIE_MMIO_SIZE; + } else { + virt_high_pcie_memmap.size = VIRT64_HIGH_PCIE_MMIO_SIZE; + virt_high_pcie_memmap.base = memmap[VIRT_DRAM].base + machine->ram_size; + virt_high_pcie_memmap.base = + ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size); + } + + /* register system main memory (actual RAM) */ + memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base, + machine->ram); + + /* boot rom */ + memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom", + memmap[VIRT_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, + mask_rom); /* SiFive Test MMIO device */ sifive_test_create(memmap[VIRT_TEST].base); @@ -990,7 +1453,7 @@ static void virt_machine_init(MachineState *machine) for (i = 0; i < VIRTIO_COUNT; i++) { sysbus_create_simple("virtio-mmio", memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, - qdev_get_gpio_in(DEVICE(virtio_plic), VIRTIO_IRQ + i)); + qdev_get_gpio_in(DEVICE(virtio_irqchip), VIRTIO_IRQ + i)); } gpex_pcie_init(system_memory, @@ -1001,14 +1464,16 @@ static void virt_machine_init(MachineState *machine) virt_high_pcie_memmap.base, virt_high_pcie_memmap.size, memmap[VIRT_PCIE_PIO].base, - DEVICE(pcie_plic)); + DEVICE(pcie_irqchip)); + + create_platform_bus(s, DEVICE(mmio_irqchip)); serial_mm_init(system_memory, memmap[VIRT_UART0].base, - 0, qdev_get_gpio_in(DEVICE(mmio_plic), UART0_IRQ), 399193, + 0, qdev_get_gpio_in(DEVICE(mmio_irqchip), UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, - qdev_get_gpio_in(DEVICE(mmio_plic), RTC_IRQ)); + qdev_get_gpio_in(DEVICE(mmio_irqchip), RTC_IRQ)); virt_flash_create(s); @@ -1018,12 +1483,77 @@ static void virt_machine_init(MachineState *machine) drive_get(IF_PFLASH, 0, i)); } virt_flash_map(s, system_memory); + + /* create device tree */ + create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, + riscv_is_32bit(&s->soc[0])); + + s->machine_done.notify = virt_machine_done; + qemu_add_machine_init_done_notifier(&s->machine_done); } static void virt_machine_instance_init(Object *obj) { } +static char *virt_get_aia_guests(Object *obj, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + char val[32]; + + sprintf(val, "%d", s->aia_guests); + return g_strdup(val); +} + +static void virt_set_aia_guests(Object *obj, const char *val, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + s->aia_guests = atoi(val); + if (s->aia_guests < 0 || s->aia_guests > VIRT_IRQCHIP_MAX_GUESTS) { + error_setg(errp, "Invalid number of AIA IMSIC guests"); + error_append_hint(errp, "Valid values be between 0 and %d.\n", + VIRT_IRQCHIP_MAX_GUESTS); + } +} + +static char *virt_get_aia(Object *obj, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + const char *val; + + switch (s->aia_type) { + case VIRT_AIA_TYPE_APLIC: + val = "aplic"; + break; + case VIRT_AIA_TYPE_APLIC_IMSIC: + val = "aplic-imsic"; + break; + default: + val = "none"; + break; + }; + + return g_strdup(val); +} + +static void virt_set_aia(Object *obj, const char *val, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + if (!strcmp(val, "none")) { + s->aia_type = VIRT_AIA_TYPE_NONE; + } else if (!strcmp(val, "aplic")) { + s->aia_type = VIRT_AIA_TYPE_APLIC; + } else if (!strcmp(val, "aplic-imsic")) { + s->aia_type = VIRT_AIA_TYPE_APLIC_IMSIC; + } else { + error_setg(errp, "Invalid AIA interrupt controller type"); + error_append_hint(errp, "Valid values are none, aplic, and " + "aplic-imsic.\n"); + } +} + static bool virt_get_aclint(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -1040,9 +1570,37 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) s->have_aclint = value; } +static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, + DeviceState *dev) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (device_is_dynamic_sysbus(mc, dev)) { + return HOTPLUG_HANDLER(machine); + } + return NULL; +} + +static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(hotplug_dev); + + if (s->platform_bus_dev) { + MachineClass *mc = MACHINE_GET_CLASS(s); + + if (device_is_dynamic_sysbus(mc, dev)) { + platform_bus_link_device(PLATFORM_BUS_DEVICE(s->platform_bus_dev), + SYS_BUS_DEVICE(dev)); + } + } +} + static void virt_machine_class_init(ObjectClass *oc, void *data) { + char str[128]; MachineClass *mc = MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->desc = "RISC-V VirtIO board"; mc->init = virt_machine_init; @@ -1054,14 +1612,35 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id; mc->numa_mem_supported = true; mc->default_ram_id = "riscv_virt_board.ram"; + assert(!mc->get_hotplug_handler); + mc->get_hotplug_handler = virt_machine_get_hotplug_handler; + + hc->plug = virt_machine_device_plug_cb; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); +#ifdef CONFIG_TPM + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); +#endif object_class_property_add_bool(oc, "aclint", virt_get_aclint, virt_set_aclint); object_class_property_set_description(oc, "aclint", "Set on/off to enable/disable " "emulating ACLINT devices"); + + object_class_property_add_str(oc, "aia", virt_get_aia, + virt_set_aia); + object_class_property_set_description(oc, "aia", + "Set type of AIA interrupt " + "conttoller. Valid values are " + "none, aplic, and aplic-imsic."); + + object_class_property_add_str(oc, "aia-guests", + virt_get_aia_guests, + virt_set_aia_guests); + sprintf(str, "Set number of guest MMIO pages for AIA IMSIC. Valid value " + "should be between 0 and %d.", VIRT_IRQCHIP_MAX_GUESTS); + object_class_property_set_description(oc, "aia-guests", str); } static const TypeInfo virt_machine_typeinfo = { @@ -1070,6 +1649,10 @@ static const TypeInfo virt_machine_typeinfo = { .class_init = virt_machine_class_init, .instance_init = virt_machine_instance_init, .instance_size = sizeof(RISCVVirtState), + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, }; static void virt_machine_init_register_types(void) diff --git a/hw/rtc/m41t80.c b/hw/rtc/m41t80.c index a00971a67e..e045c864bb 100644 --- a/hw/rtc/m41t80.c +++ b/hw/rtc/m41t80.c @@ -47,7 +47,7 @@ static uint8_t m41t80_recv(I2CSlave *i2c) { M41t80State *s = M41T80(i2c); struct tm now; - qemu_timeval tv; + int64_t rt; if (s->addr < 0) { s->addr = 0; @@ -57,8 +57,8 @@ static uint8_t m41t80_recv(I2CSlave *i2c) } switch (s->addr++) { case 0: - qemu_gettimeofday(&tv); - return to_bcd(tv.tv_usec / 10000); + rt = g_get_real_time(); + return to_bcd((rt % G_USEC_PER_SEC) / 10000); case 1: return to_bcd(now.tm_sec); case 2: diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index dc21fb10a5..e61f7ec370 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -42,6 +42,7 @@ struct M48txxISAState { ISADevice parent_obj; M48t59State state; uint32_t io_base; + uint8_t isairq; MemoryRegion io; }; @@ -79,6 +80,7 @@ static void m48txx_isa_toggle_lock(Nvram *obj, int lock) static Property m48t59_isa_properties[] = { DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), + DEFINE_PROP_UINT8("irq", M48txxISAState, isairq, 8), DEFINE_PROP_END_OF_LIST(), }; @@ -97,9 +99,14 @@ static void m48t59_isa_realize(DeviceState *dev, Error **errp) M48txxISAState *d = M48TXX_ISA(dev); M48t59State *s = &d->state; + if (d->isairq >= ISA_NUM_IRQS) { + error_setg(errp, "Maximum value for \"irq\" is: %u", ISA_NUM_IRQS - 1); + return; + } + s->model = u->info.model; s->size = u->info.size; - isa_init_irq(isadev, &s->IRQ, 8); + s->IRQ = isa_get_irq(isadev, d->isairq); m48t59_realize_common(s, errp); memory_region_init_io(&d->io, OBJECT(dev), &m48t59_io_ops, s, "m48t59", 4); if (d->io_base != 0) { diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index e61a0cced4..f235c2ddbe 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -40,7 +40,7 @@ #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "qapi/error.h" -#include "qapi/qapi-events-misc-target.h" +#include "qapi/qapi-events-misc.h" #include "qapi/visitor.h" #include "hw/rtc/mc146818rtc_regs.h" @@ -611,12 +611,13 @@ static void rtc_get_time(RTCState *s, struct tm *tm) static void rtc_set_time(RTCState *s) { struct tm tm; + g_autofree const char *qom_path = object_get_canonical_path(OBJECT(s)); rtc_get_time(s, &tm); s->base_rtc = mktimegm(&tm); s->last_update = qemu_clock_get_ns(rtc_clock); - qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); + qapi_event_send_rtc_change(qemu_timedate_diff(&tm), qom_path); } static void rtc_set_cmos(RTCState *s, const struct tm *tm) @@ -911,6 +912,11 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) s->base_year = 0; } + if (s->isairq >= ISA_NUM_IRQS) { + error_setg(errp, "Maximum value for \"irq\" is: %u", ISA_NUM_IRQS - 1); + return; + } + rtc_set_date_from_host(isadev); switch (s->lost_tick_policy) { @@ -956,15 +962,17 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) { DeviceState *dev; ISADevice *isadev; + RTCState *s; isadev = isa_new(TYPE_MC146818_RTC); dev = DEVICE(isadev); + s = MC146818_RTC(isadev); qdev_prop_set_int32(dev, "base_year", base_year); isa_realize_and_unref(isadev, bus, &error_fatal); if (intercept_irq) { qdev_connect_gpio_out(dev, 0, intercept_irq); } else { - isa_connect_gpio_out(isadev, 0, RTC_ISA_IRQ); + isa_connect_gpio_out(isadev, 0, s->isairq); } object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(isadev), @@ -975,6 +983,7 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) static Property mc146818rtc_properties[] = { DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), + DEFINE_PROP_UINT8("irq", RTCState, isairq, RTC_ISA_IRQ), DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState, lost_tick_policy, LOST_TICK_POLICY_DISCARD), DEFINE_PROP_END_OF_LIST(), @@ -1010,6 +1019,7 @@ static void rtc_reset_hold(Object *obj) static void rtc_build_aml(ISADevice *isadev, Aml *scope) { + RTCState *s = MC146818_RTC(isadev); Aml *dev; Aml *crs; @@ -1020,7 +1030,7 @@ static void rtc_build_aml(ISADevice *isadev, Aml *scope) crs = aml_resource_template(); aml_append(crs, aml_io(AML_DECODE16, RTC_ISA_BASE, RTC_ISA_BASE, 0x01, 0x08)); - aml_append(crs, aml_irq_no_flags(RTC_ISA_IRQ)); + aml_append(crs, aml_irq_no_flags(s->isairq)); dev = aml_device("RTC"); aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0B00"))); diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index 8fd8d8f9a7..7cecdee5dd 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -2,7 +2,7 @@ softmmu_ss.add(when: 'CONFIG_DS1338', if_true: files('ds1338.c')) softmmu_ss.add(when: 'CONFIG_M41T80', if_true: files('m41t80.c')) softmmu_ss.add(when: 'CONFIG_M48T59', if_true: files('m48t59.c')) -specific_ss.add(when: 'CONFIG_PL031', if_true: files('pl031.c')) +softmmu_ss.add(when: 'CONFIG_PL031', if_true: files('pl031.c')) softmmu_ss.add(when: 'CONFIG_TWL92230', if_true: files('twl92230.c')) softmmu_ss.add(when: ['CONFIG_ISA_BUS', 'CONFIG_M48T59'], if_true: files('m48t59-isa.c')) softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-rtc.c')) diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index 38d9d3c2f3..b01d0e75d1 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -24,7 +24,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" -#include "qapi/qapi-events-misc-target.h" +#include "qapi/qapi-events-misc.h" #define RTC_DR 0x00 /* Data read register */ #define RTC_MR 0x04 /* Match register */ @@ -138,12 +138,13 @@ static void pl031_write(void * opaque, hwaddr offset, switch (offset) { case RTC_LR: { + g_autofree const char *qom_path = object_get_canonical_path(opaque); struct tm tm; s->tick_offset += value - pl031_get_count(s); qemu_get_timedate(&tm, s->tick_offset); - qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); + qapi_event_send_rtc_change(qemu_timedate_diff(&tm), qom_path); pl031_set_alarm(s); break; diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 75d1fec6ca..be147b4bd9 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -20,7 +20,6 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qemu-common.h" #include "hw/loader.h" #include "hw/rx/rx62n.h" #include "sysemu/qtest.h" @@ -142,7 +141,7 @@ static void rx_gdbsim_init(MachineState *machine) exit(1); } /* DTB is located at the end of SDRAM space. */ - dtb_offset = machine->ram_size - dtb_size; + dtb_offset = ROUND_DOWN(machine->ram_size - dtb_size, 16); rom_add_blob_fixed("dtb", dtb, dtb_size, SDRAM_BASE + dtb_offset); /* Set dtb address to R1 */ diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 6fa47b889c..faa51aa4c7 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -28,7 +28,7 @@ typedef struct SCLPEventsBus { } SCLPEventsBus; /* we need to save 32 bit chunks for compatibility */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define RECV_MASK_LOWER 1 #define RECV_MASK_UPPER 0 #else /* little endian host */ diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index eb7fc4c4ae..4b5eb77afd 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -13,7 +13,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" #include "sysemu/reset.h" @@ -28,11 +27,14 @@ #include "hw/s390x/css.h" #include "hw/s390x/ebcdic.h" #include "hw/s390x/pv.h" +#include "hw/scsi/scsi.h" +#include "hw/virtio/virtio-net.h" #include "ipl.h" #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "standard-headers/linux/virtio_ids.h" #include "exec/exec-all.h" #define KERN_IMAGE_START 0x010000UL @@ -376,14 +378,18 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype) object_dynamic_cast(OBJECT(dev_st), TYPE_SCSI_DEVICE); if (sd) { - SCSIBus *bus = scsi_bus_from_device(sd); - VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); - VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, - vdev); - - ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw), - TYPE_CCW_DEVICE); - tmp_dt = CCW_DEVTYPE_SCSI; + SCSIBus *sbus = scsi_bus_from_device(sd); + VirtIODevice *vdev = (VirtIODevice *) + object_dynamic_cast(OBJECT(sbus->qbus.parent), + TYPE_VIRTIO_DEVICE); + if (vdev) { + ccw_dev = (CcwDevice *) + object_dynamic_cast(OBJECT(qdev_get_parent_bus(DEVICE(vdev))->parent), + TYPE_CCW_DEVICE); + if (ccw_dev) { + tmp_dt = CCW_DEVTYPE_SCSI; + } + } } } } diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index 28484256ec..feefe0717e 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -44,6 +44,7 @@ virtio_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-ccw-serial.c' if have_virtfs virtio_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-ccw-9p.c')) endif +virtio_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-ccw.c')) s390x_ss.add_all(when: 'CONFIG_VIRTIO_CCW', if_true: virtio_ss) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 84e3e63c43..047cca0487 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -25,6 +25,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/qemu-print.h" +#include "qemu/units.h" #include "hw/s390x/s390-pci-bus.h" #include "sysemu/reset.h" #include "hw/s390x/storage-keys.h" @@ -791,18 +792,36 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +static void ccw_machine_7_1_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_7_1_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(7_1, "7.1", true); + static void ccw_machine_7_0_instance_options(MachineState *machine) { + static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V7_0 }; + + ccw_machine_7_1_instance_options(machine); + s390_set_qemu_cpu_model(0x8561, 15, 1, qemu_cpu_feat); } static void ccw_machine_7_0_class_options(MachineClass *mc) { + ccw_machine_7_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); } -DEFINE_CCW_MACHINE(7_0, "7.0", true); +DEFINE_CCW_MACHINE(7_0, "7.0", false); static void ccw_machine_6_2_instance_options(MachineState *machine) { + static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V6_2 }; + ccw_machine_7_0_instance_options(machine); + s390_set_qemu_cpu_model(0x3906, 14, 2, qemu_cpu_feat); } static void ccw_machine_6_2_class_options(MachineClass *mc) diff --git a/hw/s390x/vhost-scsi-ccw.c b/hw/s390x/vhost-scsi-ccw.c new file mode 100644 index 0000000000..40dc14bbc7 --- /dev/null +++ b/hw/s390x/vhost-scsi-ccw.c @@ -0,0 +1,73 @@ +/* + * vhost ccw scsi implementation + * + * Copyright 2012, 2015 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "virtio-ccw.h" +#include "hw/virtio/vhost-scsi.h" + +#define TYPE_VHOST_SCSI_CCW "vhost-scsi-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VHostSCSICcw, VHOST_SCSI_CCW) + +struct VHostSCSICcw { + VirtioCcwDevice parent_obj; + VHostSCSI vdev; +}; + +static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_realize(vdev, BUS(&ccw_dev->bus), errp); +} + +static void vhost_ccw_scsi_instance_init(Object *obj) +{ + VHostSCSICcw *dev = VHOST_SCSI_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_SCSI); +} + +static Property vhost_ccw_scsi_properties[] = { + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = vhost_ccw_scsi_realize; + device_class_set_props(dc, vhost_ccw_scsi_properties); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo vhost_ccw_scsi = { + .name = TYPE_VHOST_SCSI_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VHostSCSICcw), + .instance_init = vhost_ccw_scsi_instance_init, + .class_init = vhost_ccw_scsi_class_init, +}; + +static void virtio_ccw_scsi_register(void) +{ + type_register_static(&vhost_ccw_scsi); +} + +type_init(virtio_ccw_scsi_register) diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c index 246416a8f9..07845a9a00 100644 --- a/hw/s390x/vhost-vsock-ccw.c +++ b/hw/s390x/vhost-vsock-ccw.c @@ -12,6 +12,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/vhost-vsock.h" + +#define TYPE_VHOST_VSOCK_CCW "vhost-vsock-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VHostVSockCCWState, VHOST_VSOCK_CCW) + +struct VHostVSockCCWState { + VirtioCcwDevice parent_obj; + VHostVSock vdev; +}; static Property vhost_vsock_ccw_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw-9p.c b/hw/s390x/virtio-ccw-9p.c index 88c8884fc5..6f931f5994 100644 --- a/hw/s390x/virtio-ccw-9p.c +++ b/hw/s390x/virtio-ccw-9p.c @@ -15,6 +15,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/9pfs/virtio-9p.h" + +#define TYPE_VIRTIO_9P_CCW "virtio-9p-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(V9fsCCWState, VIRTIO_9P_CCW) + +struct V9fsCCWState { + VirtioCcwDevice parent_obj; + V9fsVirtioState vdev; +}; static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-balloon.c b/hw/s390x/virtio-ccw-balloon.c index 4c7631a433..44287b9bbe 100644 --- a/hw/s390x/virtio-ccw-balloon.c +++ b/hw/s390x/virtio-ccw-balloon.c @@ -15,6 +15,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-balloon.h" + +#define TYPE_VIRTIO_BALLOON_CCW "virtio-balloon-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBalloonCcw, VIRTIO_BALLOON_CCW) + +struct VirtIOBalloonCcw { + VirtioCcwDevice parent_obj; + VirtIOBalloon vdev; +}; static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index 2294ce1ce4..8e0e58b77d 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -15,6 +15,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-blk.h" + +#define TYPE_VIRTIO_BLK_CCW "virtio-blk-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlkCcw, VIRTIO_BLK_CCW) + +struct VirtIOBlkCcw { + VirtioCcwDevice parent_obj; + VirtIOBlock vdev; +}; static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-crypto.c b/hw/s390x/virtio-ccw-crypto.c index 358c74fb4b..0fa2f89443 100644 --- a/hw/s390x/virtio-ccw-crypto.c +++ b/hw/s390x/virtio-ccw-crypto.c @@ -14,6 +14,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-crypto.h" + +#define TYPE_VIRTIO_CRYPTO_CCW "virtio-crypto-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOCryptoCcw, VIRTIO_CRYPTO_CCW) + +struct VirtIOCryptoCcw { + VirtioCcwDevice parent_obj; + VirtIOCrypto vdev; +}; static void virtio_ccw_crypto_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c index 5868a2a070..8d995fcb33 100644 --- a/hw/s390x/virtio-ccw-gpu.c +++ b/hw/s390x/virtio-ccw-gpu.c @@ -14,6 +14,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-gpu.h" + +#define TYPE_VIRTIO_GPU_CCW "virtio-gpu-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOGPUCcw, VIRTIO_GPU_CCW) + +struct VirtIOGPUCcw { + VirtioCcwDevice parent_obj; + VirtIOGPU vdev; +}; static void virtio_ccw_gpu_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-input.c b/hw/s390x/virtio-ccw-input.c index 83136fbba1..61a07ba38d 100644 --- a/hw/s390x/virtio-ccw-input.c +++ b/hw/s390x/virtio-ccw-input.c @@ -14,6 +14,26 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-input.h" + +#define TYPE_VIRTIO_INPUT_CCW "virtio-input-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOInputCcw, VIRTIO_INPUT_CCW) + +struct VirtIOInputCcw { + VirtioCcwDevice parent_obj; + VirtIOInput vdev; +}; + +#define TYPE_VIRTIO_INPUT_HID_CCW "virtio-input-hid-ccw" +#define TYPE_VIRTIO_KEYBOARD_CCW "virtio-keyboard-ccw" +#define TYPE_VIRTIO_MOUSE_CCW "virtio-mouse-ccw" +#define TYPE_VIRTIO_TABLET_CCW "virtio-tablet-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOInputHIDCcw, VIRTIO_INPUT_HID_CCW) + +struct VirtIOInputHIDCcw { + VirtioCcwDevice parent_obj; + VirtIOInputHID vdev; +}; static void virtio_ccw_input_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index 3860d4e6ea..484e617659 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -15,6 +15,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-net.h" + +#define TYPE_VIRTIO_NET_CCW "virtio-net-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIONetCcw, VIRTIO_NET_CCW) + +struct VirtIONetCcw { + VirtioCcwDevice parent_obj; + VirtIONet vdev; +}; static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-rng.c b/hw/s390x/virtio-ccw-rng.c index 2e3a9da5e8..a3fffb5138 100644 --- a/hw/s390x/virtio-ccw-rng.c +++ b/hw/s390x/virtio-ccw-rng.c @@ -15,6 +15,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-rng.h" + +#define TYPE_VIRTIO_RNG_CCW "virtio-rng-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIORNGCcw, VIRTIO_RNG_CCW) + +struct VirtIORNGCcw { + VirtioCcwDevice parent_obj; + VirtIORNG vdev; +}; static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw-scsi.c b/hw/s390x/virtio-ccw-scsi.c index 6e4beef700..d003f89f43 100644 --- a/hw/s390x/virtio-ccw-scsi.c +++ b/hw/s390x/virtio-ccw-scsi.c @@ -15,6 +15,15 @@ #include "qapi/error.h" #include "qemu/module.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-scsi.h" + +#define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSCSICcw, VIRTIO_SCSI_CCW) + +struct VirtIOSCSICcw { + VirtioCcwDevice parent_obj; + VirtIOSCSI vdev; +}; static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) { @@ -70,56 +79,9 @@ static const TypeInfo virtio_ccw_scsi = { .class_init = virtio_ccw_scsi_class_init, }; -#ifdef CONFIG_VHOST_SCSI - -static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_realize(vdev, BUS(&ccw_dev->bus), errp); -} - -static void vhost_ccw_scsi_instance_init(Object *obj) -{ - VHostSCSICcw *dev = VHOST_SCSI_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_SCSI); -} - -static Property vhost_ccw_scsi_properties[] = { - DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, - VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = vhost_ccw_scsi_realize; - device_class_set_props(dc, vhost_ccw_scsi_properties); - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo vhost_ccw_scsi = { - .name = TYPE_VHOST_SCSI_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VHostSCSICcw), - .instance_init = vhost_ccw_scsi_instance_init, - .class_init = vhost_ccw_scsi_class_init, -}; - -#endif - static void virtio_ccw_scsi_register(void) { type_register_static(&virtio_ccw_scsi); -#ifdef CONFIG_VHOST_SCSI - type_register_static(&vhost_ccw_scsi); -#endif } type_init(virtio_ccw_scsi_register) diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c index 61958228d1..bf8057880f 100644 --- a/hw/s390x/virtio-ccw-serial.c +++ b/hw/s390x/virtio-ccw-serial.c @@ -15,6 +15,15 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-serial.h" #include "virtio-ccw.h" +#include "hw/virtio/virtio-serial.h" + +#define TYPE_VIRTIO_SERIAL_CCW "virtio-serial-ccw" +OBJECT_DECLARE_SIMPLE_TYPE(VirtioSerialCcw, VIRTIO_SERIAL_CCW) + +struct VirtioSerialCcw { + VirtioCcwDevice parent_obj; + VirtIOSerial vdev; +}; static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp) { diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index c845a92c3a..15b458527e 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "exec/address-spaces.h" #include "sysemu/kvm.h" #include "net/net.h" #include "hw/virtio/virtio.h" @@ -19,6 +20,7 @@ #include "hw/virtio/virtio-net.h" #include "qemu/bitops.h" #include "qemu/error-report.h" +#include "qemu/log.h" #include "qemu/module.h" #include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-bus.h" diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 0168232e3b..fac186c8f6 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -13,24 +13,8 @@ #ifndef HW_S390X_VIRTIO_CCW_H #define HW_S390X_VIRTIO_CCW_H -#include "hw/virtio/virtio-blk.h" -#include "hw/virtio/virtio-net.h" -#include "hw/virtio/virtio-serial.h" -#include "hw/virtio/virtio-scsi.h" #include "qom/object.h" -#ifdef CONFIG_VHOST_SCSI -#include "hw/virtio/vhost-scsi.h" -#endif -#include "hw/virtio/virtio-balloon.h" -#include "hw/virtio/virtio-rng.h" -#include "hw/virtio/virtio-crypto.h" #include "hw/virtio/virtio-bus.h" -#ifdef CONFIG_VHOST_VSOCK -#include "hw/virtio/vhost-vsock.h" -#endif /* CONFIG_VHOST_VSOCK */ -#include "hw/virtio/virtio-gpu.h" -#include "hw/virtio/virtio-input.h" - #include "hw/s390x/s390_flic.h" #include "hw/s390x/css.h" #include "ccw-device.h" @@ -104,139 +88,6 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) return dev->max_rev; } -/* virtio-scsi-ccw */ - -#define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSCSICcw, VIRTIO_SCSI_CCW) - -struct VirtIOSCSICcw { - VirtioCcwDevice parent_obj; - VirtIOSCSI vdev; -}; - -#ifdef CONFIG_VHOST_SCSI -/* vhost-scsi-ccw */ - -#define TYPE_VHOST_SCSI_CCW "vhost-scsi-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VHostSCSICcw, VHOST_SCSI_CCW) - -struct VHostSCSICcw { - VirtioCcwDevice parent_obj; - VHostSCSI vdev; -}; -#endif - -/* virtio-blk-ccw */ - -#define TYPE_VIRTIO_BLK_CCW "virtio-blk-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlkCcw, VIRTIO_BLK_CCW) - -struct VirtIOBlkCcw { - VirtioCcwDevice parent_obj; - VirtIOBlock vdev; -}; - -/* virtio-balloon-ccw */ - -#define TYPE_VIRTIO_BALLOON_CCW "virtio-balloon-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBalloonCcw, VIRTIO_BALLOON_CCW) - -struct VirtIOBalloonCcw { - VirtioCcwDevice parent_obj; - VirtIOBalloon vdev; -}; - -/* virtio-serial-ccw */ - -#define TYPE_VIRTIO_SERIAL_CCW "virtio-serial-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtioSerialCcw, VIRTIO_SERIAL_CCW) - -struct VirtioSerialCcw { - VirtioCcwDevice parent_obj; - VirtIOSerial vdev; -}; - -/* virtio-net-ccw */ - -#define TYPE_VIRTIO_NET_CCW "virtio-net-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIONetCcw, VIRTIO_NET_CCW) - -struct VirtIONetCcw { - VirtioCcwDevice parent_obj; - VirtIONet vdev; -}; - -/* virtio-rng-ccw */ - -#define TYPE_VIRTIO_RNG_CCW "virtio-rng-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIORNGCcw, VIRTIO_RNG_CCW) - -struct VirtIORNGCcw { - VirtioCcwDevice parent_obj; - VirtIORNG vdev; -}; - -/* virtio-crypto-ccw */ - -#define TYPE_VIRTIO_CRYPTO_CCW "virtio-crypto-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOCryptoCcw, VIRTIO_CRYPTO_CCW) - -struct VirtIOCryptoCcw { - VirtioCcwDevice parent_obj; - VirtIOCrypto vdev; -}; - VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); -#ifdef CONFIG_VIRTFS -#include "hw/9pfs/virtio-9p.h" - -#define TYPE_VIRTIO_9P_CCW "virtio-9p-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(V9fsCCWState, VIRTIO_9P_CCW) - -struct V9fsCCWState { - VirtioCcwDevice parent_obj; - V9fsVirtioState vdev; -}; - -#endif /* CONFIG_VIRTFS */ - -#ifdef CONFIG_VHOST_VSOCK -#define TYPE_VHOST_VSOCK_CCW "vhost-vsock-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VHostVSockCCWState, VHOST_VSOCK_CCW) - -struct VHostVSockCCWState { - VirtioCcwDevice parent_obj; - VHostVSock vdev; -}; - -#endif /* CONFIG_VHOST_VSOCK */ - -#define TYPE_VIRTIO_GPU_CCW "virtio-gpu-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOGPUCcw, VIRTIO_GPU_CCW) - -struct VirtIOGPUCcw { - VirtioCcwDevice parent_obj; - VirtIOGPU vdev; -}; - -#define TYPE_VIRTIO_INPUT_CCW "virtio-input-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOInputCcw, VIRTIO_INPUT_CCW) - -struct VirtIOInputCcw { - VirtioCcwDevice parent_obj; - VirtIOInput vdev; -}; - -#define TYPE_VIRTIO_INPUT_HID_CCW "virtio-input-hid-ccw" -#define TYPE_VIRTIO_KEYBOARD_CCW "virtio-keyboard-ccw" -#define TYPE_VIRTIO_MOUSE_CCW "virtio-mouse-ccw" -#define TYPE_VIRTIO_TABLET_CCW "virtio-tablet-ccw" -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOInputHIDCcw, VIRTIO_INPUT_HID_CCW) - -struct VirtIOInputHIDCcw { - VirtioCcwDevice parent_obj; - VirtIOInputHID vdev; -}; - #endif diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 58d0edbd56..2d3c649567 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -195,6 +195,11 @@ static void esp_pdma_write(ESPState *s, uint8_t val) esp_set_tc(s, dmalen); } +static void esp_set_pdma_cb(ESPState *s, enum pdma_cb cb) +{ + s->pdma_cb = cb; +} + static int esp_select(ESPState *s) { int target; @@ -356,7 +361,7 @@ static void handle_satn(ESPState *s) s->dma_cb = handle_satn; return; } - s->pdma_cb = satn_pdma_cb; + esp_set_pdma_cb(s, SATN_PDMA_CB); cmdlen = get_cmd(s, ESP_CMDFIFO_SZ); if (cmdlen > 0) { s->cmdfifo_cdb_offset = 1; @@ -387,7 +392,7 @@ static void handle_s_without_atn(ESPState *s) s->dma_cb = handle_s_without_atn; return; } - s->pdma_cb = s_without_satn_pdma_cb; + esp_set_pdma_cb(s, S_WITHOUT_SATN_PDMA_CB); cmdlen = get_cmd(s, ESP_CMDFIFO_SZ); if (cmdlen > 0) { s->cmdfifo_cdb_offset = 0; @@ -422,7 +427,7 @@ static void handle_satn_stop(ESPState *s) s->dma_cb = handle_satn_stop; return; } - s->pdma_cb = satn_stop_pdma_cb; + esp_set_pdma_cb(s, SATN_STOP_PDMA_CB); cmdlen = get_cmd(s, 1); if (cmdlen > 0) { trace_esp_handle_satn_stop(fifo8_num_used(&s->cmdfifo)); @@ -464,7 +469,7 @@ static void write_response(ESPState *s) s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; s->rregs[ESP_RSEQ] = SEQ_CD; } else { - s->pdma_cb = write_response_pdma_cb; + esp_set_pdma_cb(s, WRITE_RESPONSE_PDMA_CB); esp_raise_drq(s); return; } @@ -604,7 +609,7 @@ static void esp_do_dma(ESPState *s) s->dma_memory_read(s->dma_opaque, buf, len); fifo8_push_all(&s->cmdfifo, buf, len); } else { - s->pdma_cb = do_dma_pdma_cb; + esp_set_pdma_cb(s, DO_DMA_PDMA_CB); esp_raise_drq(s); return; } @@ -646,7 +651,7 @@ static void esp_do_dma(ESPState *s) if (s->dma_memory_read) { s->dma_memory_read(s->dma_opaque, s->async_buf, len); } else { - s->pdma_cb = do_dma_pdma_cb; + esp_set_pdma_cb(s, DO_DMA_PDMA_CB); esp_raise_drq(s); return; } @@ -678,7 +683,7 @@ static void esp_do_dma(ESPState *s) } esp_set_tc(s, esp_get_tc(s) - len); - s->pdma_cb = do_dma_pdma_cb; + esp_set_pdma_cb(s, DO_DMA_PDMA_CB); esp_raise_drq(s); /* Indicate transfer to FIFO is complete */ @@ -777,6 +782,29 @@ static void esp_do_nodma(ESPState *s) esp_raise_irq(s); } +static void esp_pdma_cb(ESPState *s) +{ + switch (s->pdma_cb) { + case SATN_PDMA_CB: + satn_pdma_cb(s); + break; + case S_WITHOUT_SATN_PDMA_CB: + s_without_satn_pdma_cb(s); + break; + case SATN_STOP_PDMA_CB: + satn_stop_pdma_cb(s); + break; + case WRITE_RESPONSE_PDMA_CB: + write_response_pdma_cb(s); + break; + case DO_DMA_PDMA_CB: + do_dma_pdma_cb(s); + break; + default: + g_assert_not_reached(); + } +} + void esp_command_complete(SCSIRequest *req, size_t resid) { ESPState *s = req->hba_private; @@ -1181,6 +1209,33 @@ static int esp_post_load(void *opaque, int version_id) return 0; } +/* + * PDMA (or pseudo-DMA) is only used on the Macintosh and requires the + * guest CPU to perform the transfers between the SCSI bus and memory + * itself. This is indicated by the dma_memory_read and dma_memory_write + * functions being NULL (in contrast to the ESP PCI device) whilst + * dma_enabled is still set. + */ + +static bool esp_pdma_needed(void *opaque) +{ + ESPState *s = ESP(opaque); + + return s->dma_memory_read == NULL && s->dma_memory_write == NULL && + s->dma_enabled; +} + +static const VMStateDescription vmstate_esp_pdma = { + .name = "esp/pdma", + .version_id = 0, + .minimum_version_id = 0, + .needed = esp_pdma_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8(pdma_cb, ESPState), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_esp = { .name = "esp", .version_id = 6, @@ -1215,6 +1270,10 @@ const VMStateDescription vmstate_esp = { VMSTATE_UINT8_TEST(lun, ESPState, esp_is_version_6), VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * []) { + &vmstate_esp_pdma, + NULL + } }; static void sysbus_esp_mem_write(void *opaque, hwaddr addr, @@ -1263,7 +1322,7 @@ static void sysbus_esp_pdma_write(void *opaque, hwaddr addr, esp_pdma_write(s, val); break; } - s->pdma_cb(s); + esp_pdma_cb(s); } static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr, @@ -1285,11 +1344,20 @@ static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr, break; } if (fifo8_num_used(&s->fifo) < 2) { - s->pdma_cb(s); + esp_pdma_cb(s); } return val; } +static void *esp_load_request(QEMUFile *f, SCSIRequest *req) +{ + ESPState *s = container_of(req->bus, ESPState, bus); + + scsi_req_ref(req); + s->current_req = req; + return s; +} + static const MemoryRegionOps sysbus_esp_pdma_ops = { .read = sysbus_esp_pdma_read, .write = sysbus_esp_pdma_write, @@ -1305,6 +1373,7 @@ static const struct SCSIBusInfo esp_scsi_info = { .max_target = ESP_MAX_DEVS, .max_lun = 7, + .load_request = esp_load_request, .transfer_data = esp_transfer_data, .complete = esp_command_complete, .cancel = esp_request_cancelled diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 3666b8d946..072686ed58 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -26,6 +26,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/hw-version.h" +#include "qemu/memalign.h" #include "hw/scsi/scsi.h" #include "migration/qemu-file-types.h" #include "migration/vmstate.h" diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 0306ccc7b1..0ab00ef85c 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -183,7 +183,7 @@ static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len) uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk); assert(max_transfer); - max_transfer = MIN_NON_ZERO(max_transfer, max_iov * qemu_real_host_page_size) + max_transfer = MIN_NON_ZERO(max_transfer, max_iov * qemu_real_host_page_size()) / s->blocksize; stl_be_p(&r->buf[8], max_transfer); /* Also take care of the opt xfer len. */ diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index ae8551f279..20fb0dc162 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -249,7 +249,7 @@ lsi_bad_phase_interrupt(void) "Phase mismatch interrupt" lsi_bad_selection(uint32_t id) "Selected absent target %"PRIu32 lsi_do_dma_unavailable(void) "DMA no data available" lsi_do_dma(uint64_t addr, int len) "DMA addr=0x%"PRIx64" len=%d" -lsi_queue_command(uint32_t tag) "Queueing tag=0x%"PRId32 +lsi_queue_command(uint32_t tag) "Queueing tag=0x%"PRIx32 lsi_add_msg_byte_error(void) "MSG IN data too long" lsi_add_msg_byte(uint8_t data) "MSG IN 0x%02x" lsi_reselect(int id) "Reselected target %d" @@ -267,7 +267,7 @@ lsi_do_msgout_noop(void) "MSG: No Operation" lsi_do_msgout_extended(uint8_t msg, uint8_t len) "Extended message 0x%x (len %d)" lsi_do_msgout_ignored(const char *msg) "%s (ignored)" lsi_do_msgout_simplequeue(uint8_t select_tag) "SIMPLE queue tag=0x%x" -lsi_do_msgout_abort(uint32_t tag) "MSG: ABORT TAG tag=0x%"PRId32 +lsi_do_msgout_abort(uint32_t tag) "MSG: ABORT TAG tag=0x%"PRIx32 lsi_do_msgout_clearqueue(uint32_t tag) "MSG: CLEAR QUEUE tag=0x%"PRIx32 lsi_do_msgout_busdevicereset(uint32_t tag) "MSG: BUS DEVICE RESET tag=0x%"PRIx32 lsi_do_msgout_select(int id) "Select LUN %d" diff --git a/hw/sd/sd.c b/hw/sd/sd.c index cd67a7bac8..8e6fa09151 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -47,7 +47,6 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "qemu/module.h" -#include "qemu-common.h" #include "sdmmc-internal.h" #include "trace.h" diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index e0bbc90344..0e5e988927 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -741,7 +741,9 @@ static void sdhci_do_adma(SDHCIState *s) { unsigned int begin, length; const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK; + const MemTxAttrs attrs = { .memory = true }; ADMADescr dscr = {}; + MemTxResult res; int i; if (s->trnmod & SDHC_TRNS_BLK_CNT_EN && !s->blkcnt) { @@ -790,10 +792,13 @@ static void sdhci_do_adma(SDHCIState *s) s->data_count = block_size; length -= block_size - begin; } - dma_memory_write(s->dma_as, dscr.addr, - &s->fifo_buffer[begin], - s->data_count - begin, - MEMTXATTRS_UNSPECIFIED); + res = dma_memory_write(s->dma_as, dscr.addr, + &s->fifo_buffer[begin], + s->data_count - begin, + attrs); + if (res != MEMTX_OK) { + break; + } dscr.addr += s->data_count - begin; if (s->data_count == block_size) { s->data_count = 0; @@ -816,10 +821,13 @@ static void sdhci_do_adma(SDHCIState *s) s->data_count = block_size; length -= block_size - begin; } - dma_memory_read(s->dma_as, dscr.addr, - &s->fifo_buffer[begin], - s->data_count - begin, - MEMTXATTRS_UNSPECIFIED); + res = dma_memory_read(s->dma_as, dscr.addr, + &s->fifo_buffer[begin], + s->data_count - begin, + attrs); + if (res != MEMTX_OK) { + break; + } dscr.addr += s->data_count - begin; if (s->data_count == block_size) { sdbus_write_data(&s->sdbus, s->fifo_buffer, block_size); @@ -833,7 +841,16 @@ static void sdhci_do_adma(SDHCIState *s) } } } - s->admasysaddr += dscr.incr; + if (res != MEMTX_OK) { + if (s->errintstsen & SDHC_EISEN_ADMAERR) { + trace_sdhci_error("Set ADMA error flag"); + s->errintsts |= SDHC_EIS_ADMAERR; + s->norintsts |= SDHC_NIS_ERR; + } + sdhci_update_irq(s); + } else { + s->admasysaddr += dscr.incr; + } break; case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */ s->admasysaddr = dscr.addr; diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index 215944decc..df392e7869 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -30,3 +30,7 @@ config LSM303DLHC_MAG bool depends on I2C default y if I2C_DEVICES + +config ISL_PMBUS_VR + bool + depends on PMBUS diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c new file mode 100644 index 0000000000..e11e028884 --- /dev/null +++ b/hw/sensor/isl_pmbus_vr.c @@ -0,0 +1,279 @@ +/* + * PMBus device for Renesas Digital Multiphase Voltage Regulators + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/sensor/isl_pmbus_vr.h" +#include "hw/qdev-properties.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" + +static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev) +{ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from unsupported register: 0x%02x\n", + __func__, pmdev->code); + return PMBUS_ERR_BYTE; +} + +static int isl_pmbus_vr_write_data(PMBusDevice *pmdev, const uint8_t *buf, + uint8_t len) +{ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to unsupported register: 0x%02x\n", + __func__, pmdev->code); + return PMBUS_ERR_BYTE; +} + +/* TODO: Implement coefficients support in pmbus_device.c for qmp */ +static void isl_pmbus_vr_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + visit_type_uint16(v, name, (uint16_t *)opaque, errp); +} + +static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + uint16_t *internal = opaque; + uint16_t value; + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + *internal = value; + pmbus_check_limits(pmdev); +} + +static void isl_pmbus_vr_exit_reset(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + + pmdev->page = 0; + pmdev->capability = ISL_CAPABILITY_DEFAULT; + for (int i = 0; i < pmdev->num_pages; i++) { + pmdev->pages[i].operation = ISL_OPERATION_DEFAULT; + pmdev->pages[i].on_off_config = ISL_ON_OFF_CONFIG_DEFAULT; + pmdev->pages[i].vout_mode = ISL_VOUT_MODE_DEFAULT; + pmdev->pages[i].vout_command = ISL_VOUT_COMMAND_DEFAULT; + pmdev->pages[i].vout_max = ISL_VOUT_MAX_DEFAULT; + pmdev->pages[i].vout_margin_high = ISL_VOUT_MARGIN_HIGH_DEFAULT; + pmdev->pages[i].vout_margin_low = ISL_VOUT_MARGIN_LOW_DEFAULT; + pmdev->pages[i].vout_transition_rate = ISL_VOUT_TRANSITION_RATE_DEFAULT; + pmdev->pages[i].vout_ov_fault_limit = ISL_VOUT_OV_FAULT_LIMIT_DEFAULT; + pmdev->pages[i].ot_fault_limit = ISL_OT_FAULT_LIMIT_DEFAULT; + pmdev->pages[i].ot_warn_limit = ISL_OT_WARN_LIMIT_DEFAULT; + pmdev->pages[i].vin_ov_warn_limit = ISL_VIN_OV_WARN_LIMIT_DEFAULT; + pmdev->pages[i].vin_uv_warn_limit = ISL_VIN_UV_WARN_LIMIT_DEFAULT; + pmdev->pages[i].iin_oc_fault_limit = ISL_IIN_OC_FAULT_LIMIT_DEFAULT; + pmdev->pages[i].ton_delay = ISL_TON_DELAY_DEFAULT; + pmdev->pages[i].ton_rise = ISL_TON_RISE_DEFAULT; + pmdev->pages[i].toff_fall = ISL_TOFF_FALL_DEFAULT; + pmdev->pages[i].revision = ISL_REVISION_DEFAULT; + + pmdev->pages[i].read_vout = ISL_READ_VOUT_DEFAULT; + pmdev->pages[i].read_iout = ISL_READ_IOUT_DEFAULT; + pmdev->pages[i].read_pout = ISL_READ_POUT_DEFAULT; + pmdev->pages[i].read_vin = ISL_READ_VIN_DEFAULT; + pmdev->pages[i].read_iin = ISL_READ_IIN_DEFAULT; + pmdev->pages[i].read_pin = ISL_READ_PIN_DEFAULT; + pmdev->pages[i].read_temperature_1 = ISL_READ_TEMP_DEFAULT; + pmdev->pages[i].read_temperature_2 = ISL_READ_TEMP_DEFAULT; + pmdev->pages[i].read_temperature_3 = ISL_READ_TEMP_DEFAULT; + } +} + +/* The raa228000 uses different direct mode coefficents from most isl devices */ +static void raa228000_exit_reset(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + + isl_pmbus_vr_exit_reset(obj); + + pmdev->pages[0].read_iout = 0; + pmdev->pages[0].read_pout = 0; + pmdev->pages[0].read_vout = 0; + pmdev->pages[0].read_vin = 0; + pmdev->pages[0].read_iin = 0; + pmdev->pages[0].read_pin = 0; + pmdev->pages[0].read_temperature_1 = 0; + pmdev->pages[0].read_temperature_2 = 0; + pmdev->pages[0].read_temperature_3 = 0; +} + +static void isl_pmbus_vr_add_props(Object *obj, uint64_t *flags, uint8_t pages) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + for (int i = 0; i < pages; i++) { + if (flags[i] & PB_HAS_VIN) { + object_property_add(obj, "vin[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_vin); + } + + if (flags[i] & PB_HAS_VOUT) { + object_property_add(obj, "vout[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_vout); + } + + if (flags[i] & PB_HAS_IIN) { + object_property_add(obj, "iin[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_iin); + } + + if (flags[i] & PB_HAS_IOUT) { + object_property_add(obj, "iout[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_iout); + } + + if (flags[i] & PB_HAS_PIN) { + object_property_add(obj, "pin[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_pin); + } + + if (flags[i] & PB_HAS_POUT) { + object_property_add(obj, "pout[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_pout); + } + + if (flags[i] & PB_HAS_TEMPERATURE) { + object_property_add(obj, "temp1[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_temperature_1); + } + + if (flags[i] & PB_HAS_TEMP2) { + object_property_add(obj, "temp2[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_temperature_2); + } + + if (flags[i] & PB_HAS_TEMP3) { + object_property_add(obj, "temp3[*]", "uint16", + isl_pmbus_vr_get, + isl_pmbus_vr_set, + NULL, &pmdev->pages[i].read_temperature_3); + } + } +} + +static void raa22xx_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + uint64_t flags[2]; + + flags[0] = PB_HAS_VIN | PB_HAS_VOUT | PB_HAS_VOUT_MODE | + PB_HAS_VOUT_RATING | PB_HAS_VOUT_MARGIN | PB_HAS_IIN | + PB_HAS_IOUT | PB_HAS_PIN | PB_HAS_POUT | PB_HAS_TEMPERATURE | + PB_HAS_TEMP2 | PB_HAS_TEMP3 | PB_HAS_STATUS_MFR_SPECIFIC; + flags[1] = PB_HAS_IIN | PB_HAS_PIN | PB_HAS_TEMPERATURE | PB_HAS_TEMP3 | + PB_HAS_VOUT | PB_HAS_VOUT_MODE | PB_HAS_VOUT_MARGIN | + PB_HAS_VOUT_RATING | PB_HAS_IOUT | PB_HAS_POUT | + PB_HAS_STATUS_MFR_SPECIFIC; + + pmbus_page_config(pmdev, 0, flags[0]); + pmbus_page_config(pmdev, 1, flags[1]); + isl_pmbus_vr_add_props(obj, flags, ARRAY_SIZE(flags)); +} + +static void raa228000_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + uint64_t flags[1]; + + flags[0] = PB_HAS_VIN | PB_HAS_VOUT | PB_HAS_VOUT_MODE | + PB_HAS_VOUT_RATING | PB_HAS_VOUT_MARGIN | PB_HAS_IIN | + PB_HAS_IOUT | PB_HAS_PIN | PB_HAS_POUT | PB_HAS_TEMPERATURE | + PB_HAS_TEMP2 | PB_HAS_TEMP3 | PB_HAS_STATUS_MFR_SPECIFIC; + + pmbus_page_config(pmdev, 0, flags[0]); + isl_pmbus_vr_add_props(obj, flags, 1); +} + +static void isl_pmbus_vr_class_init(ObjectClass *klass, void *data, + uint8_t pages) +{ + PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); + k->write_data = isl_pmbus_vr_write_data; + k->receive_byte = isl_pmbus_vr_read_byte; + k->device_num_pages = pages; +} + +static void isl69260_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "Renesas ISL69260 Digital Multiphase Voltage Regulator"; + rc->phases.exit = isl_pmbus_vr_exit_reset; + isl_pmbus_vr_class_init(klass, data, 2); +} + +static void raa228000_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "Renesas 228000 Digital Multiphase Voltage Regulator"; + rc->phases.exit = raa228000_exit_reset; + isl_pmbus_vr_class_init(klass, data, 1); +} + +static void raa229004_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "Renesas 229004 Digital Multiphase Voltage Regulator"; + rc->phases.exit = isl_pmbus_vr_exit_reset; + isl_pmbus_vr_class_init(klass, data, 2); +} + +static const TypeInfo isl69260_info = { + .name = TYPE_ISL69260, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(ISLState), + .instance_init = raa22xx_init, + .class_init = isl69260_class_init, +}; + +static const TypeInfo raa229004_info = { + .name = TYPE_RAA229004, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(ISLState), + .instance_init = raa22xx_init, + .class_init = raa229004_class_init, +}; + +static const TypeInfo raa228000_info = { + .name = TYPE_RAA228000, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(ISLState), + .instance_init = raa228000_init, + .class_init = raa228000_class_init, +}; + +static void isl_pmbus_vr_register_types(void) +{ + type_register_static(&isl69260_info); + type_register_static(&raa228000_info); + type_register_static(&raa229004_info); +} + +type_init(isl_pmbus_vr_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index d1bba290da..12b6992bc8 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -5,3 +5,4 @@ softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) softmmu_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) +softmmu_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c')) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 72759413f3..39fc4f19d9 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -190,7 +190,7 @@ static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, { r2d_fpga_t *s; - s = g_malloc0(sizeof(r2d_fpga_t)); + s = g_new0(r2d_fpga_t, 1); s->irl = irl; @@ -248,7 +248,7 @@ static void r2d_init(MachineState *machine) cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; - reset_info = g_malloc0(sizeof(ResetData)); + reset_info = g_new0(ResetData, 1); reset_info->cpu = cpu; reset_info->vector = env->pc; qemu_register_reset(main_cpu_reset, reset_info); diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 43dfb6497b..c77792d150 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -770,7 +770,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) SysBusDevice *sb; MemoryRegion *mr, *alias; - s = g_malloc0(sizeof(SH7750State)); + s = g_new0(SH7750State, 1); s->cpu = cpu; s->periph_freq = 60000000; /* 60MHz */ memory_region_init_io(&s->iomem, NULL, &sh7750_mem_ops, s, diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 6013df1698..60349ee402 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -104,9 +104,11 @@ static struct { const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; uint64_t max_speed; uint64_t current_speed; + uint64_t processor_id; } type4 = { .max_speed = DEFAULT_CPU_SPEED, - .current_speed = DEFAULT_CPU_SPEED + .current_speed = DEFAULT_CPU_SPEED, + .processor_id = 0, }; static struct { @@ -327,6 +329,10 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = { .name = "part", .type = QEMU_OPT_STRING, .help = "part number", + }, { + .name = "processor-id", + .type = QEMU_OPT_NUMBER, + .help = "processor id", }, { /* end of list */ } }; @@ -549,9 +555,23 @@ bool smbios_skip_table(uint8_t type, bool required_table) return true; } +#define T0_BASE 0x000 +#define T1_BASE 0x100 +#define T2_BASE 0x200 +#define T3_BASE 0x300 +#define T4_BASE 0x400 +#define T11_BASE 0xe00 + +#define T16_BASE 0x1000 +#define T17_BASE 0x1100 +#define T19_BASE 0x1300 +#define T32_BASE 0x2000 +#define T41_BASE 0x2900 +#define T127_BASE 0x7F00 + static void smbios_build_type_0_table(void) { - SMBIOS_BUILD_TABLE_PRE(0, 0x000, false); /* optional, leave up to BIOS */ + SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */ SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); @@ -599,7 +619,7 @@ static void smbios_encode_uuid(struct smbios_uuid *uuid, QemuUUID *in) static void smbios_build_type_1_table(void) { - SMBIOS_BUILD_TABLE_PRE(1, 0x100, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */ SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer); SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product); @@ -619,7 +639,7 @@ static void smbios_build_type_1_table(void) static void smbios_build_type_2_table(void) { - SMBIOS_BUILD_TABLE_PRE(2, 0x200, false); /* optional */ + SMBIOS_BUILD_TABLE_PRE(2, T2_BASE, false); /* optional */ SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); SMBIOS_TABLE_SET_STR(2, product_str, type2.product); @@ -637,7 +657,7 @@ static void smbios_build_type_2_table(void) static void smbios_build_type_3_table(void) { - SMBIOS_BUILD_TABLE_PRE(3, 0x300, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(3, T3_BASE, true); /* required */ SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer); t->type = 0x01; /* Other */ @@ -662,15 +682,20 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance) { char sock_str[128]; - SMBIOS_BUILD_TABLE_PRE(4, 0x400 + instance, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(4, T4_BASE + instance, true); /* required */ snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); t->processor_type = 0x03; /* CPU */ t->processor_family = 0x01; /* Other */ SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); - t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); - t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); + if (type4.processor_id == 0) { + t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); + t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); + } else { + t->processor_id[0] = cpu_to_le32((uint32_t)type4.processor_id); + t->processor_id[1] = cpu_to_le32(type4.processor_id >> 32); + } SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); t->voltage = 0; t->external_clock = cpu_to_le16(0); /* Unknown */ @@ -702,7 +727,7 @@ static void smbios_build_type_11_table(void) return; } - SMBIOS_BUILD_TABLE_PRE(11, 0xe00, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(11, T11_BASE, true); /* required */ snprintf(count_str, sizeof(count_str), "%zu", type11.nvalues); t->count = type11.nvalues; @@ -722,7 +747,7 @@ static void smbios_build_type_16_table(unsigned dimm_cnt) { uint64_t size_kb; - SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(16, T16_BASE, true); /* required */ t->location = 0x01; /* Other */ t->use = 0x03; /* System memory */ @@ -749,7 +774,7 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size) char loc_str[128]; uint64_t size_mb; - SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(17, T17_BASE + instance, true); /* required */ t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ @@ -785,12 +810,13 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size) SMBIOS_BUILD_TABLE_POST; } -static void smbios_build_type_19_table(unsigned instance, +static void smbios_build_type_19_table(unsigned instance, unsigned offset, uint64_t start, uint64_t size) { uint64_t end, start_kb, end_kb; - SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(19, T19_BASE + offset + instance, + true); /* required */ end = start + size - 1; assert(end > start); @@ -814,7 +840,7 @@ static void smbios_build_type_19_table(unsigned instance, static void smbios_build_type_32_table(void) { - SMBIOS_BUILD_TABLE_PRE(32, 0x2000, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(32, T32_BASE, true); /* required */ memset(t->reserved, 0, 6); t->boot_status = 0; /* No errors detected */ @@ -828,7 +854,7 @@ static void smbios_build_type_41_table(Error **errp) struct type41_instance *t41; QTAILQ_FOREACH(t41, &type41, next) { - SMBIOS_BUILD_TABLE_PRE(41, 0x2900 + instance, true); + SMBIOS_BUILD_TABLE_PRE(41, T41_BASE + instance, true); SMBIOS_TABLE_SET_STR(41, reference_designation_str, t41->designation); t->device_type = t41->kind; @@ -871,7 +897,7 @@ static void smbios_build_type_41_table(Error **errp) static void smbios_build_type_127_table(void) { - SMBIOS_BUILD_TABLE_PRE(127, 0x7F00, true); /* required */ + SMBIOS_BUILD_TABLE_PRE(127, T127_BASE, true); /* required */ SMBIOS_BUILD_TABLE_POST; } @@ -982,7 +1008,7 @@ void smbios_get_tables(MachineState *ms, uint8_t **anchor, size_t *anchor_len, Error **errp) { - unsigned i, dimm_cnt; + unsigned i, dimm_cnt, offset; if (smbios_legacy) { *tables = *anchor = NULL; @@ -1012,6 +1038,16 @@ void smbios_get_tables(MachineState *ms, dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; + /* + * The offset determines if we need to keep additional space betweeen + * table 17 and table 19 header handle numbers so that they do + * not overlap. For example, for a VM with larger than 8 TB guest + * memory and DIMM like chunks of 16 GiB, the default space between + * the two tables (T19_BASE - T17_BASE = 512) is not enough. + */ + offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ + dimm_cnt - (T19_BASE - T17_BASE) : 0; + smbios_build_type_16_table(dimm_cnt); for (i = 0; i < dimm_cnt; i++) { @@ -1019,10 +1055,16 @@ void smbios_get_tables(MachineState *ms, } for (i = 0; i < mem_array_size; i++) { - smbios_build_type_19_table(i, mem_array[i].address, + smbios_build_type_19_table(i, offset, mem_array[i].address, mem_array[i].length); } + /* + * make sure 16 bit handle numbers in the headers of tables 19 + * and 32 do not overlap. + */ + assert((mem_array_size + offset) < (T32_BASE - T19_BASE)); + smbios_build_type_32_table(); smbios_build_type_38_table(); smbios_build_type_41_table(errp); @@ -1292,6 +1334,8 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) save_opt(&type4.serial, opts, "serial"); save_opt(&type4.asset, opts, "asset"); save_opt(&type4.part, opts, "part"); + /* If the value is 0, it will take the value from the CPU model. */ + type4.processor_id = qemu_opt_get_number(opts, "processor-id", 0); type4.max_speed = qemu_opt_get_number(opts, "max-speed", DEFAULT_CPU_SPEED); type4.current_speed = qemu_opt_get_number(opts, "current-speed", diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 7b4dec1721..1e39d2e2d0 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -26,7 +26,6 @@ #include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "cpu.h" #include "hw/irq.h" @@ -241,7 +240,7 @@ static void leon3_generic_hw_init(MachineState *machine) cpu_sparc_set_id(env, 0); /* Reset data */ - reset_info = g_malloc0(sizeof(ResetData)); + reset_info = g_new0(ResetData, 1); reset_info->cpu = cpu; reset_info->sp = LEON3_RAM_OFFSET + ram_size; qemu_register_reset(main_cpu_reset, reset_info); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 7f3a7c0027..fccaed1eb4 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -26,7 +26,6 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qemu/datadir.h" -#include "qemu-common.h" #include "cpu.h" #include "hw/sysbus.h" #include "qemu/error-report.h" diff --git a/hw/sparc64/sparc64.c b/hw/sparc64/sparc64.c index 8654e955eb..72f0849f50 100644 --- a/hw/sparc64/sparc64.c +++ b/hw/sparc64/sparc64.c @@ -81,7 +81,7 @@ static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu, QEMUBHFunc *cb, uint32_t frequency, uint64_t disabled_mask, uint64_t npt_mask) { - CPUTimer *timer = g_malloc0(sizeof(CPUTimer)); + CPUTimer *timer = g_new0(CPUTimer, 1); timer->name = name; timer->frequency = frequency; @@ -288,7 +288,7 @@ SPARCCPU *sparc64_cpu_devinit(const char *cpu_type, uint64_t prom_addr) hstick_frequency, TICK_INT_DIS, TICK_NPT_MASK); - reset_info = g_malloc0(sizeof(ResetData)); + reset_info = g_new0(ResetData, 1); reset_info->cpu = cpu; reset_info->prom_addr = prom_addr; qemu_register_reset(main_cpu_reset, reset_info); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index cda7df36e3..6fd08e2298 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -26,7 +26,6 @@ #include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "cpu.h" #include "hw/pci/pci.h" diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index ff154eb84f..68aa697164 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -224,7 +224,7 @@ static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, AspeedSegments seg; int i; - for (i = 0; i < asc->max_peripherals; i++) { + for (i = 0; i < asc->cs_num_max; i++) { if (i == cs) { continue; } @@ -259,6 +259,10 @@ static void aspeed_smc_flash_set_segment_region(AspeedSMCState *s, int cs, memory_region_set_enabled(&fl->mmio, !!seg.size); memory_region_transaction_commit(); + if (asc->segment_addr_mask) { + regval &= asc->segment_addr_mask; + } + s->regs[R_SEG_ADDR0 + cs] = regval; } @@ -286,7 +290,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, */ if ((asc->segments == aspeed_2500_spi1_segments || asc->segments == aspeed_2500_spi2_segments) && - cs == asc->max_peripherals && + cs == asc->cs_num_max && seg.addr + seg.size != asc->segments[cs].addr + asc->segments[cs].size) { aspeed_smc_error("Tried to change CS%d end address to 0x%" @@ -323,7 +327,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr, unsigned size) { - aspeed_smc_error("To 0x%" HWADDR_PRIx " of size %u" PRIx64, addr, size); + aspeed_smc_error("To 0x%" HWADDR_PRIx " of size %u", addr, size); return 0; } @@ -689,13 +693,13 @@ static void aspeed_smc_reset(DeviceState *d) } /* Unselect all peripherals */ - for (i = 0; i < s->num_cs; ++i) { + for (i = 0; i < asc->cs_num_max; ++i) { s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE; qemu_set_irq(s->cs_lines[i], true); } /* setup the default segment register values and regions for all */ - for (i = 0; i < asc->max_peripherals; ++i) { + for (i = 0; i < asc->cs_num_max; ++i) { aspeed_smc_flash_set_segment_region(s, i, asc->segment_to_reg(s, &asc->segments[i])); } @@ -725,8 +729,8 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) (aspeed_smc_has_dma(asc) && addr == R_DMA_LEN) || (aspeed_smc_has_dma(asc) && addr == R_DMA_CHECKSUM) || (addr >= R_SEG_ADDR0 && - addr < R_SEG_ADDR0 + asc->max_peripherals) || - (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->max_peripherals)) { + addr < R_SEG_ADDR0 + asc->cs_num_max) || + (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->cs_num_max)) { trace_aspeed_smc_read(addr << 2, size, s->regs[addr]); @@ -1038,11 +1042,11 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, addr < s->r_timings + asc->nregs_timings) || addr == s->r_ce_ctrl) { s->regs[addr] = value; - } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { + } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + asc->cs_num_max) { int cs = addr - s->r_ctrl0; aspeed_smc_flash_update_ctrl(&s->flashes[cs], value); } else if (addr >= R_SEG_ADDR0 && - addr < R_SEG_ADDR0 + asc->max_peripherals) { + addr < R_SEG_ADDR0 + asc->cs_num_max) { int cs = addr - R_SEG_ADDR0; if (value != s->regs[R_SEG_ADDR0 + cs]) { @@ -1086,7 +1090,7 @@ static void aspeed_smc_instance_init(Object *obj) AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); int i; - for (i = 0; i < asc->max_peripherals; i++) { + for (i = 0; i < asc->cs_num_max; i++) { object_initialize_child(obj, "flash[*]", &s->flashes[i], TYPE_ASPEED_SMC_FLASH); } @@ -1123,21 +1127,15 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) s->r_timings = asc->r_timings; s->conf_enable_w0 = asc->conf_enable_w0; - /* Enforce some real HW limits */ - if (s->num_cs > asc->max_peripherals) { - aspeed_smc_error("num_cs cannot exceed: %d", asc->max_peripherals); - s->num_cs = asc->max_peripherals; - } - /* DMA irq. Keep it first for the initialization in the SoC */ sysbus_init_irq(sbd, &s->irq); - s->spi = ssi_create_bus(dev, "spi"); + s->spi = ssi_create_bus(dev, NULL); /* Setup cs_lines for peripherals */ - s->cs_lines = g_new0(qemu_irq, s->num_cs); + s->cs_lines = g_new0(qemu_irq, asc->cs_num_max); - for (i = 0; i < s->num_cs; ++i) { + for (i = 0; i < asc->cs_num_max; ++i) { sysbus_init_irq(sbd, &s->cs_lines[i]); } @@ -1170,7 +1168,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) * module behind to handle the memory accesses. This depends on * the board configuration. */ - for (i = 0; i < asc->max_peripherals; ++i) { + for (i = 0; i < asc->cs_num_max; ++i) { AspeedSMCFlash *fl = &s->flashes[i]; if (!object_property_set_link(OBJECT(fl), "controller", OBJECT(s), @@ -1207,7 +1205,6 @@ static const VMStateDescription vmstate_aspeed_smc = { }; static Property aspeed_smc_properties[] = { - DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1), DEFINE_PROP_BOOL("inject-failure", AspeedSMCState, inject_failure, false), DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), @@ -1317,7 +1314,7 @@ static void aspeed_2400_smc_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 1; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 1; + asc->cs_num_max = 1; asc->segments = aspeed_2400_smc_segments; asc->flash_window_base = 0x10000000; asc->flash_window_size = 0x6000000; @@ -1362,8 +1359,9 @@ static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 1; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 5; + asc->cs_num_max = 5; asc->segments = aspeed_2400_fmc_segments; + asc->segment_addr_mask = 0xffff0000; asc->resets = aspeed_2400_fmc_resets; asc->flash_window_base = 0x20000000; asc->flash_window_size = 0x10000000; @@ -1403,7 +1401,7 @@ static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) asc->r_timings = R_SPI_TIMINGS; asc->nregs_timings = 1; asc->conf_enable_w0 = SPI_CONF_ENABLE_W0; - asc->max_peripherals = 1; + asc->cs_num_max = 1; asc->segments = aspeed_2400_spi1_segments; asc->flash_window_base = 0x30000000; asc->flash_window_size = 0x10000000; @@ -1444,8 +1442,9 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 1; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 3; + asc->cs_num_max = 3; asc->segments = aspeed_2500_fmc_segments; + asc->segment_addr_mask = 0xffff0000; asc->resets = aspeed_2500_fmc_resets; asc->flash_window_base = 0x20000000; asc->flash_window_size = 0x10000000; @@ -1481,8 +1480,9 @@ static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 1; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 2; + asc->cs_num_max = 2; asc->segments = aspeed_2500_spi1_segments; + asc->segment_addr_mask = 0xffff0000; asc->flash_window_base = 0x30000000; asc->flash_window_size = 0x8000000; asc->features = 0x0; @@ -1515,8 +1515,9 @@ static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 1; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 2; + asc->cs_num_max = 2; asc->segments = aspeed_2500_spi2_segments; + asc->segment_addr_mask = 0xffff0000; asc->flash_window_base = 0x38000000; asc->flash_window_size = 0x8000000; asc->features = 0x0; @@ -1596,8 +1597,9 @@ static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 1; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 3; + asc->cs_num_max = 3; asc->segments = aspeed_2600_fmc_segments; + asc->segment_addr_mask = 0x0ff00ff0; asc->resets = aspeed_2600_fmc_resets; asc->flash_window_base = 0x20000000; asc->flash_window_size = 0x10000000; @@ -1634,8 +1636,9 @@ static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 2; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 2; + asc->cs_num_max = 2; asc->segments = aspeed_2600_spi1_segments; + asc->segment_addr_mask = 0x0ff00ff0; asc->flash_window_base = 0x30000000; asc->flash_window_size = 0x10000000; asc->features = ASPEED_SMC_FEATURE_DMA | @@ -1672,8 +1675,9 @@ static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data) asc->r_timings = R_TIMINGS; asc->nregs_timings = 3; asc->conf_enable_w0 = CONF_ENABLE_W0; - asc->max_peripherals = 3; + asc->cs_num_max = 3; asc->segments = aspeed_2600_spi2_segments; + asc->segment_addr_mask = 0x0ff00ff0; asc->flash_window_base = 0x50000000; asc->flash_window_size = 0x10000000; asc->features = ASPEED_SMC_FEATURE_DMA | @@ -1692,6 +1696,160 @@ static const TypeInfo aspeed_2600_spi2_info = { .class_init = aspeed_2600_spi2_class_init, }; +/* + * The FMC Segment Registers of the AST1030 have a 512KB unit. + * Only bits [27:19] are used for decoding. + */ +#define AST1030_SEG_ADDR_MASK 0x0ff80000 + +static uint32_t aspeed_1030_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg) +{ + uint32_t reg = 0; + + /* Disabled segments have a nil register */ + if (!seg->size) { + return 0; + } + + reg |= (seg->addr & AST1030_SEG_ADDR_MASK) >> 16; /* start offset */ + reg |= (seg->addr + seg->size - 1) & AST1030_SEG_ADDR_MASK; /* end offset */ + return reg; +} + +static void aspeed_1030_smc_reg_to_segment(const AspeedSMCState *s, + uint32_t reg, AspeedSegments *seg) +{ + uint32_t start_offset = (reg << 16) & AST1030_SEG_ADDR_MASK; + uint32_t end_offset = reg & AST1030_SEG_ADDR_MASK; + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + + if (reg) { + seg->addr = asc->flash_window_base + start_offset; + seg->size = end_offset + (512 * KiB) - start_offset; + } else { + seg->addr = asc->flash_window_base; + seg->size = 0; + } +} + +static const uint32_t aspeed_1030_fmc_resets[ASPEED_SMC_R_MAX] = { + [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 | + CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1), +}; + +static const AspeedSegments aspeed_1030_fmc_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 128 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_1030_fmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 1030 FMC Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_1030_fmc_segments; + asc->segment_addr_mask = 0x0ff80ff8; + asc->resets = aspeed_1030_fmc_resets; + asc->flash_window_base = 0x80000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x000BFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_1030_smc_segment_to_reg; + asc->reg_to_segment = aspeed_1030_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; +} + +static const TypeInfo aspeed_1030_fmc_info = { + .name = "aspeed.fmc-ast1030", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_1030_fmc_class_init, +}; + +static const AspeedSegments aspeed_1030_spi1_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_1030_spi1_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 1030 SPI1 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_1030_spi1_segments; + asc->segment_addr_mask = 0x0ff00ff0; + asc->flash_window_base = 0x90000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x000BFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; +} + +static const TypeInfo aspeed_1030_spi1_info = { + .name = "aspeed.spi1-ast1030", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_1030_spi1_class_init, +}; +static const AspeedSegments aspeed_1030_spi2_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_1030_spi2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 1030 SPI2 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_1030_spi2_segments; + asc->segment_addr_mask = 0x0ff00ff0; + asc->flash_window_base = 0xb0000000; + asc->flash_window_size = 0x10000000; + asc->features = ASPEED_SMC_FEATURE_DMA; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0x000BFFFC; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; +} + +static const TypeInfo aspeed_1030_spi2_info = { + .name = "aspeed.spi2-ast1030", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_1030_spi2_class_init, +}; + static void aspeed_smc_register_types(void) { type_register_static(&aspeed_smc_flash_info); @@ -1705,6 +1863,9 @@ static void aspeed_smc_register_types(void) type_register_static(&aspeed_2600_fmc_info); type_register_static(&aspeed_2600_spi1_info); type_register_static(&aspeed_2600_spi2_info); + type_register_static(&aspeed_1030_fmc_info); + type_register_static(&aspeed_1030_spi1_info); + type_register_static(&aspeed_1030_spi2_info); } type_init(aspeed_smc_register_types) diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c new file mode 100644 index 0000000000..d14580b409 --- /dev/null +++ b/hw/ssi/ibex_spi_host.c @@ -0,0 +1,612 @@ +/* + * QEMU model of the Ibex SPI Controller + * SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/ + * + * Copyright (C) 2022 Western Digital + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/ssi/ibex_spi_host.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "migration/vmstate.h" +#include "trace.h" + +REG32(INTR_STATE, 0x00) + FIELD(INTR_STATE, ERROR, 0, 1) + FIELD(INTR_STATE, SPI_EVENT, 1, 1) +REG32(INTR_ENABLE, 0x04) + FIELD(INTR_ENABLE, ERROR, 0, 1) + FIELD(INTR_ENABLE, SPI_EVENT, 1, 1) +REG32(INTR_TEST, 0x08) + FIELD(INTR_TEST, ERROR, 0, 1) + FIELD(INTR_TEST, SPI_EVENT, 1, 1) +REG32(ALERT_TEST, 0x0c) + FIELD(ALERT_TEST, FETAL_TEST, 0, 1) +REG32(CONTROL, 0x10) + FIELD(CONTROL, RX_WATERMARK, 0, 8) + FIELD(CONTROL, TX_WATERMARK, 1, 8) + FIELD(CONTROL, OUTPUT_EN, 29, 1) + FIELD(CONTROL, SW_RST, 30, 1) + FIELD(CONTROL, SPIEN, 31, 1) +REG32(STATUS, 0x14) + FIELD(STATUS, TXQD, 0, 8) + FIELD(STATUS, RXQD, 18, 8) + FIELD(STATUS, CMDQD, 16, 3) + FIELD(STATUS, RXWM, 20, 1) + FIELD(STATUS, BYTEORDER, 22, 1) + FIELD(STATUS, RXSTALL, 23, 1) + FIELD(STATUS, RXEMPTY, 24, 1) + FIELD(STATUS, RXFULL, 25, 1) + FIELD(STATUS, TXWM, 26, 1) + FIELD(STATUS, TXSTALL, 27, 1) + FIELD(STATUS, TXEMPTY, 28, 1) + FIELD(STATUS, TXFULL, 29, 1) + FIELD(STATUS, ACTIVE, 30, 1) + FIELD(STATUS, READY, 31, 1) +REG32(CONFIGOPTS, 0x18) + FIELD(CONFIGOPTS, CLKDIV_0, 0, 16) + FIELD(CONFIGOPTS, CSNIDLE_0, 16, 4) + FIELD(CONFIGOPTS, CSNTRAIL_0, 20, 4) + FIELD(CONFIGOPTS, CSNLEAD_0, 24, 4) + FIELD(CONFIGOPTS, FULLCYC_0, 29, 1) + FIELD(CONFIGOPTS, CPHA_0, 30, 1) + FIELD(CONFIGOPTS, CPOL_0, 31, 1) +REG32(CSID, 0x1c) + FIELD(CSID, CSID, 0, 32) +REG32(COMMAND, 0x20) + FIELD(COMMAND, LEN, 0, 8) + FIELD(COMMAND, CSAAT, 9, 1) + FIELD(COMMAND, SPEED, 10, 2) + FIELD(COMMAND, DIRECTION, 12, 2) +REG32(ERROR_ENABLE, 0x2c) + FIELD(ERROR_ENABLE, CMDBUSY, 0, 1) + FIELD(ERROR_ENABLE, OVERFLOW, 1, 1) + FIELD(ERROR_ENABLE, UNDERFLOW, 2, 1) + FIELD(ERROR_ENABLE, CMDINVAL, 3, 1) + FIELD(ERROR_ENABLE, CSIDINVAL, 4, 1) +REG32(ERROR_STATUS, 0x30) + FIELD(ERROR_STATUS, CMDBUSY, 0, 1) + FIELD(ERROR_STATUS, OVERFLOW, 1, 1) + FIELD(ERROR_STATUS, UNDERFLOW, 2, 1) + FIELD(ERROR_STATUS, CMDINVAL, 3, 1) + FIELD(ERROR_STATUS, CSIDINVAL, 4, 1) + FIELD(ERROR_STATUS, ACCESSINVAL, 5, 1) +REG32(EVENT_ENABLE, 0x30) + FIELD(EVENT_ENABLE, RXFULL, 0, 1) + FIELD(EVENT_ENABLE, TXEMPTY, 1, 1) + FIELD(EVENT_ENABLE, RXWM, 2, 1) + FIELD(EVENT_ENABLE, TXWM, 3, 1) + FIELD(EVENT_ENABLE, READY, 4, 1) + FIELD(EVENT_ENABLE, IDLE, 5, 1) + +static inline uint8_t div4_round_up(uint8_t dividend) +{ + return (dividend + 3) / 4; +} + +static void ibex_spi_rxfifo_reset(IbexSPIHostState *s) +{ + /* Empty the RX FIFO and assert RXEMPTY */ + fifo8_reset(&s->rx_fifo); + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK; + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK; +} + +static void ibex_spi_txfifo_reset(IbexSPIHostState *s) +{ + /* Empty the TX FIFO and assert TXEMPTY */ + fifo8_reset(&s->tx_fifo); + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK; + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXEMPTY_MASK; +} + +static void ibex_spi_host_reset(DeviceState *dev) +{ + IbexSPIHostState *s = IBEX_SPI_HOST(dev); + trace_ibex_spi_host_reset("Resetting Ibex SPI"); + + /* SPI Host Register Reset */ + s->regs[IBEX_SPI_HOST_INTR_STATE] = 0x00; + s->regs[IBEX_SPI_HOST_INTR_ENABLE] = 0x00; + s->regs[IBEX_SPI_HOST_INTR_TEST] = 0x00; + s->regs[IBEX_SPI_HOST_ALERT_TEST] = 0x00; + s->regs[IBEX_SPI_HOST_CONTROL] = 0x7f; + s->regs[IBEX_SPI_HOST_STATUS] = 0x00; + s->regs[IBEX_SPI_HOST_CONFIGOPTS] = 0x00; + s->regs[IBEX_SPI_HOST_CSID] = 0x00; + s->regs[IBEX_SPI_HOST_COMMAND] = 0x00; + /* RX/TX Modelled by FIFO */ + s->regs[IBEX_SPI_HOST_RXDATA] = 0x00; + s->regs[IBEX_SPI_HOST_TXDATA] = 0x00; + + s->regs[IBEX_SPI_HOST_ERROR_ENABLE] = 0x1F; + s->regs[IBEX_SPI_HOST_ERROR_STATUS] = 0x00; + s->regs[IBEX_SPI_HOST_EVENT_ENABLE] = 0x00; + + ibex_spi_rxfifo_reset(s); + ibex_spi_txfifo_reset(s); + + s->init_status = true; + return; +} + +/* + * Check if we need to trigger an interrupt. + * The two interrupts lines (host_err and event) can + * be enabled separately in 'IBEX_SPI_HOST_INTR_ENABLE'. + * + * Interrupts are triggered based on the ones + * enabled in the `IBEX_SPI_HOST_EVENT_ENABLE` and `IBEX_SPI_HOST_ERROR_ENABLE`. + */ +static void ibex_spi_host_irq(IbexSPIHostState *s) +{ + bool error_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE] + & R_INTR_ENABLE_ERROR_MASK; + bool event_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE] + & R_INTR_ENABLE_SPI_EVENT_MASK; + bool err_pending = s->regs[IBEX_SPI_HOST_INTR_STATE] + & R_INTR_STATE_ERROR_MASK; + bool status_pending = s->regs[IBEX_SPI_HOST_INTR_STATE] + & R_INTR_STATE_SPI_EVENT_MASK; + int err_irq = 0, event_irq = 0; + + /* Error IRQ enabled and Error IRQ Cleared*/ + if (error_en && !err_pending) { + /* Event enabled, Interrupt Test Error */ + if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_ERROR_MASK) { + err_irq = 1; + } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] + & R_ERROR_ENABLE_CMDBUSY_MASK) && + s->regs[IBEX_SPI_HOST_ERROR_STATUS] + & R_ERROR_STATUS_CMDBUSY_MASK) { + /* Wrote to COMMAND when not READY */ + err_irq = 1; + } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] + & R_ERROR_ENABLE_CMDINVAL_MASK) && + s->regs[IBEX_SPI_HOST_ERROR_STATUS] + & R_ERROR_STATUS_CMDINVAL_MASK) { + /* Invalid command segment */ + err_irq = 1; + } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] + & R_ERROR_ENABLE_CSIDINVAL_MASK) && + s->regs[IBEX_SPI_HOST_ERROR_STATUS] + & R_ERROR_STATUS_CSIDINVAL_MASK) { + /* Invalid value for CSID */ + err_irq = 1; + } + if (err_irq) { + s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_ERROR_MASK; + } + qemu_set_irq(s->host_err, err_irq); + } + + /* Event IRQ Enabled and Event IRQ Cleared */ + if (event_en && !status_pending) { + if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_SPI_EVENT_MASK) { + /* Event enabled, Interrupt Test Event */ + event_irq = 1; + } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] + & R_EVENT_ENABLE_READY_MASK) && + (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) { + /* SPI Host ready for next command */ + event_irq = 1; + } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] + & R_EVENT_ENABLE_TXEMPTY_MASK) && + (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_TXEMPTY_MASK)) { + /* SPI TXEMPTY, TXFIFO drained */ + event_irq = 1; + } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] + & R_EVENT_ENABLE_RXFULL_MASK) && + (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_RXFULL_MASK)) { + /* SPI RXFULL, RXFIFO full */ + event_irq = 1; + } + if (event_irq) { + s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_SPI_EVENT_MASK; + } + qemu_set_irq(s->event, event_irq); + } +} + +static void ibex_spi_host_transfer(IbexSPIHostState *s) +{ + uint32_t rx, tx; + /* Get num of one byte transfers */ + uint8_t segment_len = ((s->regs[IBEX_SPI_HOST_COMMAND] & R_COMMAND_LEN_MASK) + >> R_COMMAND_LEN_SHIFT); + while (segment_len > 0) { + if (fifo8_is_empty(&s->tx_fifo)) { + /* Assert Stall */ + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXSTALL_MASK; + break; + } else if (fifo8_is_full(&s->rx_fifo)) { + /* Assert Stall */ + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXSTALL_MASK; + break; + } else { + tx = fifo8_pop(&s->tx_fifo); + } + + rx = ssi_transfer(s->ssi, tx); + + trace_ibex_spi_host_transfer(tx, rx); + + if (!fifo8_is_full(&s->rx_fifo)) { + fifo8_push(&s->rx_fifo, rx); + } else { + /* Assert RXFULL */ + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXFULL_MASK; + } + --segment_len; + } + + /* Assert Ready */ + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK; + /* Set RXQD */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXQD_MASK; + s->regs[IBEX_SPI_HOST_STATUS] |= (R_STATUS_RXQD_MASK + & div4_round_up(segment_len)); + /* Set TXQD */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK; + s->regs[IBEX_SPI_HOST_STATUS] |= (fifo8_num_used(&s->tx_fifo) / 4) + & R_STATUS_TXQD_MASK; + /* Clear TXFULL */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK; + /* Assert TXEMPTY and drop remaining bytes that exceed segment_len */ + ibex_spi_txfifo_reset(s); + /* Reset RXEMPTY */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXEMPTY_MASK; + + ibex_spi_host_irq(s); +} + +static uint64_t ibex_spi_host_read(void *opaque, hwaddr addr, + unsigned int size) +{ + IbexSPIHostState *s = opaque; + uint32_t rc = 0; + uint8_t rx_byte = 0; + + trace_ibex_spi_host_read(addr, size); + + /* Match reg index */ + addr = addr >> 2; + switch (addr) { + /* Skipping any W/O registers */ + case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE: + case IBEX_SPI_HOST_CONTROL...IBEX_SPI_HOST_STATUS: + rc = s->regs[addr]; + break; + case IBEX_SPI_HOST_CSID: + rc = s->regs[addr]; + break; + case IBEX_SPI_HOST_CONFIGOPTS: + rc = s->config_opts[s->regs[IBEX_SPI_HOST_CSID]]; + break; + case IBEX_SPI_HOST_TXDATA: + rc = s->regs[addr]; + break; + case IBEX_SPI_HOST_RXDATA: + /* Clear RXFULL */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK; + + for (int i = 0; i < 4; ++i) { + if (fifo8_is_empty(&s->rx_fifo)) { + /* Assert RXEMPTY, no IRQ */ + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK; + s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= + R_ERROR_STATUS_UNDERFLOW_MASK; + return rc; + } + rx_byte = fifo8_pop(&s->rx_fifo); + rc |= rx_byte << (i * 8); + } + break; + case IBEX_SPI_HOST_ERROR_ENABLE...IBEX_SPI_HOST_EVENT_ENABLE: + rc = s->regs[addr]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n", + addr << 2); + } + return rc; +} + + +static void ibex_spi_host_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + IbexSPIHostState *s = opaque; + uint32_t val32 = val64; + uint32_t shift_mask = 0xff; + uint8_t txqd_len; + + trace_ibex_spi_host_write(addr, size, val64); + + /* Match reg index */ + addr = addr >> 2; + + switch (addr) { + /* Skipping any R/O registers */ + case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE: + s->regs[addr] = val32; + break; + case IBEX_SPI_HOST_INTR_TEST: + s->regs[addr] = val32; + ibex_spi_host_irq(s); + break; + case IBEX_SPI_HOST_ALERT_TEST: + s->regs[addr] = val32; + qemu_log_mask(LOG_UNIMP, + "%s: SPI_ALERT_TEST is not supported\n", __func__); + break; + case IBEX_SPI_HOST_CONTROL: + s->regs[addr] = val32; + + if (val32 & R_CONTROL_SW_RST_MASK) { + ibex_spi_host_reset((DeviceState *)s); + /* Clear active if any */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_ACTIVE_MASK; + } + + if (val32 & R_CONTROL_OUTPUT_EN_MASK) { + qemu_log_mask(LOG_UNIMP, + "%s: CONTROL_OUTPUT_EN is not supported\n", __func__); + } + break; + case IBEX_SPI_HOST_CONFIGOPTS: + /* Update the respective config-opts register based on CSIDth index */ + s->config_opts[s->regs[IBEX_SPI_HOST_CSID]] = val32; + qemu_log_mask(LOG_UNIMP, + "%s: CONFIGOPTS Hardware settings not supported\n", + __func__); + break; + case IBEX_SPI_HOST_CSID: + if (val32 >= s->num_cs) { + /* CSID exceeds max num_cs */ + s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= + R_ERROR_STATUS_CSIDINVAL_MASK; + ibex_spi_host_irq(s); + return; + } + s->regs[addr] = val32; + break; + case IBEX_SPI_HOST_COMMAND: + s->regs[addr] = val32; + + /* STALL, IP not enabled */ + if (!(s->regs[IBEX_SPI_HOST_CONTROL] & R_CONTROL_SPIEN_MASK)) { + return; + } + + /* SPI not ready, IRQ Error */ + if (!(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) { + s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK; + ibex_spi_host_irq(s); + return; + } + /* Assert Not Ready */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK; + + if (((val32 & R_COMMAND_DIRECTION_MASK) >> R_COMMAND_DIRECTION_SHIFT) + != BIDIRECTIONAL_TRANSFER) { + qemu_log_mask(LOG_UNIMP, + "%s: Rx Only/Tx Only are not supported\n", __func__); + } + + if (val32 & R_COMMAND_CSAAT_MASK) { + qemu_log_mask(LOG_UNIMP, + "%s: CSAAT is not supported\n", __func__); + } + if (val32 & R_COMMAND_SPEED_MASK) { + qemu_log_mask(LOG_UNIMP, + "%s: SPEED is not supported\n", __func__); + } + + /* Set Transfer Callback */ + timer_mod(s->fifo_trigger_handle, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (TX_INTERRUPT_TRIGGER_DELAY_NS)); + + break; + case IBEX_SPI_HOST_TXDATA: + /* + * This is a hardware `feature` where + * the first word written TXDATA after init is omitted entirely + */ + if (s->init_status) { + s->init_status = false; + return; + } + + for (int i = 0; i < 4; ++i) { + /* Attempting to write when TXFULL */ + if (fifo8_is_full(&s->tx_fifo)) { + /* Assert RXEMPTY, no IRQ */ + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXFULL_MASK; + s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= + R_ERROR_STATUS_OVERFLOW_MASK; + ibex_spi_host_irq(s); + return; + } + /* Byte ordering is set by the IP */ + if ((s->regs[IBEX_SPI_HOST_STATUS] & + R_STATUS_BYTEORDER_MASK) == 0) { + /* LE: LSB transmitted first (default for ibex processor) */ + shift_mask = 0xff << (i * 8); + } else { + /* BE: MSB transmitted first */ + qemu_log_mask(LOG_UNIMP, + "%s: Big endian is not supported\n", __func__); + } + + fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8)); + } + + /* Reset TXEMPTY */ + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXEMPTY_MASK; + /* Update TXQD */ + txqd_len = (s->regs[IBEX_SPI_HOST_STATUS] & + R_STATUS_TXQD_MASK) >> R_STATUS_TXQD_SHIFT; + /* Partial bytes (size < 4) are padded, in words. */ + txqd_len += 1; + s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK; + s->regs[IBEX_SPI_HOST_STATUS] |= txqd_len; + /* Assert Ready */ + s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK; + break; + case IBEX_SPI_HOST_ERROR_ENABLE: + s->regs[addr] = val32; + + if (val32 & R_ERROR_ENABLE_CMDINVAL_MASK) { + qemu_log_mask(LOG_UNIMP, + "%s: Segment Length is not supported\n", __func__); + } + break; + case IBEX_SPI_HOST_ERROR_STATUS: + /* + * Indicates that any errors that have occurred. + * When an error occurs, the corresponding bit must be cleared + * here before issuing any further commands + */ + s->regs[addr] = val32; + break; + case IBEX_SPI_HOST_EVENT_ENABLE: + /* Controls which classes of SPI events raise an interrupt. */ + s->regs[addr] = val32; + + if (val32 & R_EVENT_ENABLE_RXWM_MASK) { + qemu_log_mask(LOG_UNIMP, + "%s: RXWM is not supported\n", __func__); + } + if (val32 & R_EVENT_ENABLE_TXWM_MASK) { + qemu_log_mask(LOG_UNIMP, + "%s: TXWM is not supported\n", __func__); + } + + if (val32 & R_EVENT_ENABLE_IDLE_MASK) { + qemu_log_mask(LOG_UNIMP, + "%s: IDLE is not supported\n", __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n", + addr << 2); + } +} + +static const MemoryRegionOps ibex_spi_ops = { + .read = ibex_spi_host_read, + .write = ibex_spi_host_write, + /* Ibex default LE */ + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static Property ibex_spi_properties[] = { + DEFINE_PROP_UINT32("num_cs", IbexSPIHostState, num_cs, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_ibex = { + .name = TYPE_IBEX_SPI_HOST, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IbexSPIHostState, IBEX_SPI_HOST_MAX_REGS), + VMSTATE_VARRAY_UINT32(config_opts, IbexSPIHostState, + num_cs, 0, vmstate_info_uint32, uint32_t), + VMSTATE_FIFO8(rx_fifo, IbexSPIHostState), + VMSTATE_FIFO8(tx_fifo, IbexSPIHostState), + VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexSPIHostState), + VMSTATE_BOOL(init_status, IbexSPIHostState), + VMSTATE_END_OF_LIST() + } +}; + +static void fifo_trigger_update(void *opaque) +{ + IbexSPIHostState *s = opaque; + ibex_spi_host_transfer(s); +} + +static void ibex_spi_host_realize(DeviceState *dev, Error **errp) +{ + IbexSPIHostState *s = IBEX_SPI_HOST(dev); + int i; + + s->ssi = ssi_create_bus(dev, "ssi"); + s->cs_lines = g_new0(qemu_irq, s->num_cs); + + for (i = 0; i < s->num_cs; ++i) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]); + } + + /* Setup CONFIGOPTS Multi-register */ + s->config_opts = g_new0(uint32_t, s->num_cs); + + /* Setup FIFO Interrupt Timer */ + s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, + fifo_trigger_update, s); + + /* FIFO sizes as per OT Spec */ + fifo8_create(&s->tx_fifo, IBEX_SPI_HOST_TXFIFO_LEN); + fifo8_create(&s->rx_fifo, IBEX_SPI_HOST_RXFIFO_LEN); +} + +static void ibex_spi_host_init(Object *obj) +{ + IbexSPIHostState *s = IBEX_SPI_HOST(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->host_err); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->event); + + memory_region_init_io(&s->mmio, obj, &ibex_spi_ops, s, + TYPE_IBEX_SPI_HOST, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void ibex_spi_host_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = ibex_spi_host_realize; + dc->reset = ibex_spi_host_reset; + dc->vmsd = &vmstate_ibex; + device_class_set_props(dc, ibex_spi_properties); +} + +static const TypeInfo ibex_spi_host_info = { + .name = TYPE_IBEX_SPI_HOST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IbexSPIHostState), + .instance_init = ibex_spi_host_init, + .class_init = ibex_spi_host_class_init, +}; + +static void ibex_spi_host_register_types(void) +{ + type_register_static(&ibex_spi_host_info); +} + +type_init(ibex_spi_host_register_types) diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 0ded9cd092..702aa5e4df 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -10,3 +10,4 @@ softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) +softmmu_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index 612d3d6087..c707d4aaba 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -20,3 +20,10 @@ npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset: npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64 npcm7xx_fiu_flash_write(const char *id, unsigned cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64 + +# ibex_spi_host.c + +ibex_spi_host_reset(const char *msg) "%s" +ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data: 0x%" PRIx32 " rx_data: @0x%" PRIx32 +ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 +ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size %u:" diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c index 7ecd148fdf..c762e0b367 100644 --- a/hw/ssi/xlnx-versal-ospi.c +++ b/hw/ssi/xlnx-versal-ospi.c @@ -1800,7 +1800,6 @@ static const VMStateDescription vmstate_xlnx_versal_ospi = { .name = TYPE_XILINX_VERSAL_OSPI, .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi), VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi), diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 15caff0e41..84cf2726bb 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -176,7 +176,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq) { arm_timer_state *s; - s = (arm_timer_state *)g_malloc0(sizeof(arm_timer_state)); + s = g_new0(arm_timer_state, 1); s->freq = freq; s->control = TIMER_CTRL_IE; diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 42c47d2ce6..9c20b3d6ad 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -745,12 +745,29 @@ static const TypeInfo aspeed_2600_timer_info = { .class_init = aspeed_2600_timer_class_init, }; +static void aspeed_1030_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); + + dc->desc = "ASPEED 1030 Timer"; + awc->read = aspeed_2600_timer_read; + awc->write = aspeed_2600_timer_write; +} + +static const TypeInfo aspeed_1030_timer_info = { + .name = TYPE_ASPEED_1030_TIMER, + .parent = TYPE_ASPEED_TIMER, + .class_init = aspeed_1030_timer_class_init, +}; + static void aspeed_timer_register_types(void) { type_register_static(&aspeed_timer_info); type_register_static(&aspeed_2400_timer_info); type_register_static(&aspeed_2500_timer_info); type_register_static(&aspeed_2600_timer_info); + type_register_static(&aspeed_1030_timer_info); } type_init(aspeed_timer_register_types) diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c index 64108241ba..e57a0f5f09 100644 --- a/hw/timer/cadence_ttc.c +++ b/hw/timer/cadence_ttc.c @@ -24,6 +24,8 @@ #include "qemu/timer.h" #include "qom/object.h" +#include "hw/timer/cadence_ttc.h" + #ifdef CADENCE_TTC_ERR_DEBUG #define DB_PRINT(...) do { \ fprintf(stderr, ": %s: ", __func__); \ @@ -49,36 +51,6 @@ #define CLOCK_CTRL_PS_EN 0x00000001 #define CLOCK_CTRL_PS_V 0x0000001e -typedef struct { - QEMUTimer *timer; - int freq; - - uint32_t reg_clock; - uint32_t reg_count; - uint32_t reg_value; - uint16_t reg_interval; - uint16_t reg_match[3]; - uint32_t reg_intr; - uint32_t reg_intr_en; - uint32_t reg_event_ctrl; - uint32_t reg_event; - - uint64_t cpu_time; - unsigned int cpu_time_valid; - - qemu_irq irq; -} CadenceTimerState; - -#define TYPE_CADENCE_TTC "cadence_ttc" -OBJECT_DECLARE_SIMPLE_TYPE(CadenceTTCState, CADENCE_TTC) - -struct CadenceTTCState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - CadenceTimerState timer[3]; -}; - static void cadence_timer_update(CadenceTimerState *s) { qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en)); diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 03e33fc592..90fdce4c44 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -400,7 +400,7 @@ static void slavio_timer_init(Object *obj) uint64_t size; char timer_name[20]; - tc = g_malloc0(sizeof(TimerContext)); + tc = g_new0(TimerContext, 1); tc->s = s; tc->timer_index = i; diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c index 6dbb9f41e4..7f74e26ec6 100644 --- a/hw/tpm/tpm_ppi.c +++ b/hw/tpm/tpm_ppi.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" - +#include "qemu/memalign.h" #include "qapi/error.h" #include "sysemu/memory_mapping.h" #include "migration/vmstate.h" @@ -47,7 +47,7 @@ void tpm_ppi_reset(TPMPPI *tpmppi) void tpm_ppi_init(TPMPPI *tpmppi, MemoryRegion *m, hwaddr addr, Object *obj) { - tpmppi->buf = qemu_memalign(qemu_real_host_page_size, + tpmppi->buf = qemu_memalign(qemu_real_host_page_size(), HOST_PAGE_ALIGN(TPM_PPI_ADDR_SIZE)); memory_region_init_ram_device_ptr(&tpmppi->ram, obj, "tpm-ppi", TPM_PPI_ADDR_SIZE, tpmppi->buf); diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index 10d8a14f19..3477afd735 100644 --- a/hw/tpm/tpm_tis_isa.c +++ b/hw/tpm/tpm_tis_isa.c @@ -127,7 +127,7 @@ static void tpm_tis_isa_realizefn(DeviceState *dev, Error **errp) return; } - isa_init_irq(ISA_DEVICE(dev), &s->irq, s->irq_num); + s->irq = isa_get_irq(ISA_DEVICE(dev), s->irq_num); memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), TPM_TIS_ADDR_BASE, &s->mmio); diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index fa3040fb71..f530ab2565 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/units.h" #include #include "chardev/char-fe.h" diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 1e6ac76bef..5831395cef 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -10,7 +10,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/error-report.h" #include @@ -1607,7 +1606,7 @@ static void usb_mtp_write_data(MTPState *s, uint32_t handle) usb_mtp_object_lookup(s, s->dataset.parent_handle); char *path = NULL; uint64_t rc; - mode_t mask = 0644; + mode_t mask = 0755; int ret = 0; assert(d != NULL); @@ -1635,7 +1634,7 @@ static void usb_mtp_write_data(MTPState *s, uint32_t handle) } d->fd = open(path, O_CREAT | O_WRONLY | - O_CLOEXEC | O_NOFOLLOW, mask); + O_CLOEXEC | O_NOFOLLOW, mask & 0666); if (d->fd == -1) { ret = 1; goto done; diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 91ffd9f8ae..28164d89be 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -37,7 +37,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/qdev-properties.h" diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index a93d6b2e98..895b29fb86 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -58,8 +58,6 @@ struct ohci_hcca { #define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) #define ED_WBACK_SIZE 4 -static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); - /* Bitfields for the first word of an Endpoint Desciptor. */ #define OHCI_ED_FA_SHIFT 0 #define OHCI_ED_FA_MASK (0x7f<opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - - /* set connect status */ - port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; - - /* update speed */ - if (port->port.dev->speed == USB_SPEED_LOW) { - port->ctrl |= OHCI_PORT_LSDA; - } else { - port->ctrl &= ~OHCI_PORT_LSDA; - } - - /* notify of remote-wakeup */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - ohci_set_interrupt(s, OHCI_INTR_RD); - } - - trace_usb_ohci_port_attach(port1->index); - - if (old_state != port->ctrl) { - ohci_set_interrupt(s, OHCI_INTR_RHSC); - } -} - -static void ohci_detach(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - - ohci_async_cancel_device(s, port1->dev); - - /* set connect status */ - if (port->ctrl & OHCI_PORT_CCS) { - port->ctrl &= ~OHCI_PORT_CCS; - port->ctrl |= OHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & OHCI_PORT_PES) { - port->ctrl &= ~OHCI_PORT_PES; - port->ctrl |= OHCI_PORT_PESC; - } - trace_usb_ohci_port_detach(port1->index); - - if (old_state != port->ctrl) { - ohci_set_interrupt(s, OHCI_INTR_RHSC); - } -} - -static void ohci_wakeup(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t intr = 0; - if (port->ctrl & OHCI_PORT_PSS) { - trace_usb_ohci_port_wakeup(port1->index); - port->ctrl |= OHCI_PORT_PSSC; - port->ctrl &= ~OHCI_PORT_PSS; - intr = OHCI_INTR_RHSC; - } - /* Note that the controller can be suspended even if this port is not */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - trace_usb_ohci_remote_wakeup(s->name); - /* This is the one state transition the controller can do by itself */ - s->ctl &= ~OHCI_CTL_HCFS; - s->ctl |= OHCI_USB_RESUME; - /* In suspend mode only ResumeDetected is possible, not RHSC: - * see the OHCI spec 5.1.2.3. - */ - intr = OHCI_INTR_RD; - } - ohci_set_interrupt(s, intr); -} - -static void ohci_child_detach(USBPort *port1, USBDevice *child) -{ - OHCIState *s = port1->opaque; - - ohci_async_cancel_device(s, child); -} - static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) { USBDevice *dev; @@ -369,6 +281,10 @@ void ohci_stop_endpoints(OHCIState *ohci) USBDevice *dev; int i, j; + if (ohci->async_td) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; + } for (i = 0; i < ohci->num_ports; i++) { dev = ohci->rhport[i].port.dev; if (dev && dev->attached) { @@ -398,10 +314,6 @@ static void ohci_roothub_reset(OHCIState *ohci) usb_port_reset(&port->port); } } - if (ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } ohci_stop_endpoints(ohci); } @@ -634,21 +546,9 @@ static int ohci_copy_iso_td(OHCIState *ohci, return 0; } -static void ohci_process_lists(OHCIState *ohci, int completion); - -static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) -{ - OHCIState *ohci = container_of(packet, OHCIState, usb_packet); - - trace_usb_ohci_async_complete(); - ohci->async_complete = true; - ohci_process_lists(ohci, 1); -} - #define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b))) -static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, - int completion) +static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) { int dir; size_t len = 0; @@ -658,6 +558,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, int i; USBDevice *dev; USBEndpoint *ep; + USBPacket *pkt; + uint8_t buf[8192]; + bool int_req; struct ohci_iso_td iso_td; uint32_t addr; uint16_t starting_frame; @@ -792,40 +695,42 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } else { len = end_addr - start_addr + 1; } - if (len > sizeof(ohci->usb_buf)) { - len = sizeof(ohci->usb_buf); + if (len > sizeof(buf)) { + len = sizeof(buf); } if (len && dir != OHCI_TD_DIR_IN) { - if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, + if (ohci_copy_iso_td(ohci, start_addr, end_addr, buf, len, DMA_DIRECTION_TO_DEVICE)) { ohci_die(ohci); return 1; } } - if (!completion) { - bool int_req = relative_frame_number == frame_count && - OHCI_BM(iso_td.flags, TD_DI) == 0; - dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); - if (dev == NULL) { - trace_usb_ohci_td_dev_error(); - return 1; - } - ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); - usb_handle_packet(dev, &ohci->usb_packet); - if (ohci->usb_packet.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, ep); - return 1; - } + dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + if (dev == NULL) { + trace_usb_ohci_td_dev_error(); + return 1; } - if (ohci->usb_packet.status == USB_RET_SUCCESS) { - ret = ohci->usb_packet.actual_length; + ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); + pkt = g_new0(USBPacket, 1); + usb_packet_init(pkt); + int_req = relative_frame_number == frame_count && + OHCI_BM(iso_td.flags, TD_DI) == 0; + usb_packet_setup(pkt, pid, ep, 0, addr, false, int_req); + usb_packet_addbuf(pkt, buf, len); + usb_handle_packet(dev, pkt); + if (pkt->status == USB_RET_ASYNC) { + usb_device_flush_ep_queue(dev, ep); + g_free(pkt); + return 1; + } + if (pkt->status == USB_RET_SUCCESS) { + ret = pkt->actual_length; } else { - ret = ohci->usb_packet.status; + ret = pkt->status; } + g_free(pkt); trace_usb_ohci_iso_td_so(start_offset, end_offset, start_addr, end_addr, str, len, ret); @@ -833,7 +738,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, /* Writeback */ if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) { /* IN transfer succeeded */ - if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, + if (ohci_copy_iso_td(ohci, start_addr, end_addr, buf, ret, DMA_DIRECTION_FROM_DEVICE)) { ohci_die(ohci); return 1; @@ -1033,21 +938,21 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) ohci->async_td = 0; ohci->async_complete = false; } else { - if (ohci->async_td) { - /* ??? The hardware should allow one active packet per - endpoint. We only allow one active packet per controller. - This should be sufficient as long as devices respond in a - timely manner. - */ - trace_usb_ohci_td_too_many_pending(); - return 1; - } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); if (dev == NULL) { trace_usb_ohci_td_dev_error(); return 1; } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); + if (ohci->async_td) { + /* ??? The hardware should allow one active packet per + endpoint. We only allow one active packet per controller. + This should be sufficient as long as devices respond in a + timely manner. + */ + trace_usb_ohci_td_too_many_pending(ep->nr); + return 1; + } usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, OHCI_BM(td.flags, TD_DI) == 0); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); @@ -1156,7 +1061,7 @@ exit_no_retire: } /* Service an endpoint list. Returns nonzero if active TD were found. */ -static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) +static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) { struct ohci_ed ed; uint32_t next_ed; @@ -1207,8 +1112,9 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) break; } else { /* Handle isochronous endpoints */ - if (ohci_service_iso_td(ohci, &ed, completion)) + if (ohci_service_iso_td(ohci, &ed)) { break; + } } } @@ -1235,20 +1141,20 @@ static void ohci_sof(OHCIState *ohci) } /* Process Control and Bulk lists. */ -static void ohci_process_lists(OHCIState *ohci, int completion) +static void ohci_process_lists(OHCIState *ohci) { if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) { trace_usb_ohci_process_lists(ohci->ctrl_head, ohci->ctrl_cur); } - if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) { + if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) { ohci->ctrl_cur = 0; ohci->status &= ~OHCI_STATUS_CLF; } } if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) { - if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) { + if (!ohci_service_ed_list(ohci, ohci->bulk_head)) { ohci->bulk_cur = 0; ohci->status &= ~OHCI_STATUS_BLF; } @@ -1272,19 +1178,15 @@ static void ohci_frame_boundary(void *opaque) int n; n = ohci->frame_number & 0x1f; - ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0); + ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n])); } /* Cancel all pending packets if either of the lists has been disabled. */ if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { - if (ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } ohci_stop_endpoints(ohci); } ohci->old_ctl = ohci->ctl; - ohci_process_lists(ohci, 0); + ohci_process_lists(ohci); /* Stop if UnrecoverableError happened or ohci_sof will crash */ if (ohci->intr_status & OHCI_INTR_UE) { @@ -1793,8 +1695,45 @@ static void ohci_mem_write(void *opaque, } } -static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) +static const MemoryRegionOps ohci_mem_ops = { + .read = ohci_mem_read, + .write = ohci_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* USBPortOps */ +static void ohci_attach(USBPort *port1) { + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; + + /* set connect status */ + port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; + + /* update speed */ + if (port->port.dev->speed == USB_SPEED_LOW) { + port->ctrl |= OHCI_PORT_LSDA; + } else { + port->ctrl &= ~OHCI_PORT_LSDA; + } + + /* notify of remote-wakeup */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + ohci_set_interrupt(s, OHCI_INTR_RD); + } + + trace_usb_ohci_port_attach(port1->index); + + if (old_state != port->ctrl) { + ohci_set_interrupt(s, OHCI_INTR_RHSC); + } +} + +static void ohci_child_detach(USBPort *port1, USBDevice *dev) +{ + OHCIState *ohci = port1->opaque; + if (ohci->async_td && usb_packet_is_inflight(&ohci->usb_packet) && ohci->usb_packet.ep->dev == dev) { @@ -1803,11 +1742,65 @@ static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) } } -static const MemoryRegionOps ohci_mem_ops = { - .read = ohci_mem_read, - .write = ohci_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; +static void ohci_detach(USBPort *port1) +{ + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; + + ohci_child_detach(port1, port1->dev); + + /* set connect status */ + if (port->ctrl & OHCI_PORT_CCS) { + port->ctrl &= ~OHCI_PORT_CCS; + port->ctrl |= OHCI_PORT_CSC; + } + /* disable port */ + if (port->ctrl & OHCI_PORT_PES) { + port->ctrl &= ~OHCI_PORT_PES; + port->ctrl |= OHCI_PORT_PESC; + } + trace_usb_ohci_port_detach(port1->index); + + if (old_state != port->ctrl) { + ohci_set_interrupt(s, OHCI_INTR_RHSC); + } +} + +static void ohci_wakeup(USBPort *port1) +{ + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; + uint32_t intr = 0; + if (port->ctrl & OHCI_PORT_PSS) { + trace_usb_ohci_port_wakeup(port1->index); + port->ctrl |= OHCI_PORT_PSSC; + port->ctrl &= ~OHCI_PORT_PSS; + intr = OHCI_INTR_RHSC; + } + /* Note that the controller can be suspended even if this port is not */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + trace_usb_ohci_remote_wakeup(s->name); + /* This is the one state transition the controller can do by itself */ + s->ctl &= ~OHCI_CTL_HCFS; + s->ctl |= OHCI_USB_RESUME; + /* + * In suspend mode only ResumeDetected is possible, not RHSC: + * see the OHCI spec 5.1.2.3. + */ + intr = OHCI_INTR_RD; + } + ohci_set_interrupt(s, intr); +} + +static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) +{ + OHCIState *ohci = container_of(packet, OHCIState, usb_packet); + + trace_usb_ohci_async_complete(); + ohci->async_complete = true; + ohci_process_lists(ohci); +} static USBPortOps ohci_port_ops = { .attach = ohci_attach, diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 14bdb89676..0cd0a5e540 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2523,7 +2523,7 @@ static void xhci_process_commands(XHCIState *xhci) case CR_VENDOR_NEC_FIRMWARE_REVISION: if (xhci->nec_quirks) { event.type = 48; /* NEC reply */ - event.length = 0x3025; + event.length = 0x3034; } else { event.ccode = CC_TRB_ERROR; } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 5f0ef9cb3b..3bc4dee7fe 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -26,7 +26,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/units.h" #include "qapi/error.h" #include "qemu/timer.h" @@ -1239,7 +1239,11 @@ static void usbredir_create_parser(USBRedirDevice *dev) DPRINTF("creating usbredirparser\n"); - dev->parser = qemu_oom_check(usbredirparser_create()); + dev->parser = usbredirparser_create(); + if (!dev->parser) { + error_report("usbredirparser_create() failed"); + exit(1); + } dev->parser->priv = dev; dev->parser->log_func = usbredir_log; dev->parser->read_func = usbredir_read; @@ -2239,7 +2243,10 @@ static int usbredir_put_parser(QEMUFile *f, void *priv, size_t unused, } usbredirparser_serialize(dev->parser, &data, &len); - qemu_oom_check(data); + if (!data) { + error_report("usbredirparser_serialize failed"); + exit(1); + } qemu_put_be32(f, len); qemu_put_buffer(f, data, len); @@ -2330,7 +2337,11 @@ static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused, bufp->len = qemu_get_be32(f); bufp->status = qemu_get_be32(f); bufp->offset = 0; - bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */ + bufp->data = malloc(bufp->len); /* regular malloc! */ + if (!bufp->data) { + error_report("usbredir_get_bufpq: out of memory"); + exit(1); + } bufp->free_on_destroy = bufp->data; qemu_get_buffer(f, bufp->data, bufp->len); QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next); diff --git a/hw/usb/trace-events b/hw/usb/trace-events index b8287b63f1..9773cb5330 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -51,7 +51,7 @@ usb_ohci_td_skip_async(void) "" usb_ohci_td_pkt_hdr(uint32_t addr, int64_t pktlen, int64_t len, const char *s, int flag_r, uint32_t cbp, uint32_t be) " TD @ 0x%.8x %" PRId64 " of %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x" usb_ohci_td_pkt_short(const char *dir, const char *buf) "%s data: %s" usb_ohci_td_pkt_full(const char *dir, const char *buf) "%s data: %s" -usb_ohci_td_too_many_pending(void) "" +usb_ohci_td_too_many_pending(int ep) "ep=%d" usb_ohci_td_packet_status(int status) "status=%d" usb_ohci_ed_read_error(uint32_t addr) "ED read error at 0x%x" usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 080046e3f5..2b1f78fdfa 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -397,7 +397,7 @@ static int vfio_dma_unmap_bitmap(VFIOContainer *container, { struct vfio_iommu_type1_dma_unmap *unmap; struct vfio_bitmap *bitmap; - uint64_t pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size; + uint64_t pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size(); int ret; unmap = g_malloc0(sizeof(*unmap) + sizeof(*bitmap)); @@ -414,7 +414,7 @@ static int vfio_dma_unmap_bitmap(VFIOContainer *container, * to qemu_real_host_page_size. */ - bitmap->pgsize = qemu_real_host_page_size; + bitmap->pgsize = qemu_real_host_page_size(); bitmap->size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / BITS_PER_BYTE; @@ -882,8 +882,8 @@ static void vfio_listener_region_add(MemoryListener *listener, } if (unlikely((section->offset_within_address_space & - ~qemu_real_host_page_mask) != - (section->offset_within_region & ~qemu_real_host_page_mask))) { + ~qemu_real_host_page_mask()) != + (section->offset_within_region & ~qemu_real_host_page_mask()))) { error_report("%s received unaligned region", __func__); return; } @@ -891,7 +891,7 @@ static void vfio_listener_region_add(MemoryListener *listener, iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); + llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask())); if (int128_ge(int128_make64(iova), llend)) { if (memory_region_is_ram_device(section->mr)) { @@ -899,7 +899,7 @@ static void vfio_listener_region_add(MemoryListener *listener, memory_region_name(section->mr), section->offset_within_address_space, int128_getlo(section->size), - qemu_real_host_page_size); + qemu_real_host_page_size()); } return; } @@ -1118,8 +1118,8 @@ static void vfio_listener_region_del(MemoryListener *listener, } if (unlikely((section->offset_within_address_space & - ~qemu_real_host_page_mask) != - (section->offset_within_region & ~qemu_real_host_page_mask))) { + ~qemu_real_host_page_mask()) != + (section->offset_within_region & ~qemu_real_host_page_mask()))) { error_report("%s received unaligned region", __func__); return; } @@ -1150,7 +1150,7 @@ static void vfio_listener_region_del(MemoryListener *listener, iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); + llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask())); if (int128_ge(int128_make64(iova), llend)) { return; @@ -1272,9 +1272,9 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, * qemu_real_host_page_size to mark those dirty. Hence set bitmap's pgsize * to qemu_real_host_page_size. */ - range->bitmap.pgsize = qemu_real_host_page_size; + range->bitmap.pgsize = qemu_real_host_page_size(); - pages = REAL_HOST_PAGE_ALIGN(range->size) / qemu_real_host_page_size; + pages = REAL_HOST_PAGE_ALIGN(range->size) / qemu_real_host_page_size(); range->bitmap.size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / BITS_PER_BYTE; range->bitmap.data = g_try_malloc0(range->bitmap.size); @@ -1970,7 +1970,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of * qemu_real_host_page_size to mark those dirty. */ - if (cap_mig->pgsize_bitmap & qemu_real_host_page_size) { + if (cap_mig->pgsize_bitmap & qemu_real_host_page_size()) { container->dirty_pages_supported = true; container->max_dirty_bitmap_size = cap_mig->max_dirty_bitmap_size; container->dirty_pgsizes = cap_mig->pgsize_bitmap; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 7b45353ce2..9fd9faee1d 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -412,6 +412,7 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, int vector_n, bool msix) { + KVMRouteChange c; int virq; if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi)) { @@ -422,11 +423,13 @@ static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, return; } - virq = kvm_irqchip_add_msi_route(kvm_state, vector_n, &vdev->pdev); + c = kvm_irqchip_begin_route_changes(kvm_state); + virq = kvm_irqchip_add_msi_route(&c, vector_n, &vdev->pdev); if (virq < 0) { event_notifier_cleanup(&vector->kvm_interrupt); return; } + kvm_irqchip_commit_route_changes(&c); if (kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt, NULL, virq) < 0) { @@ -1084,8 +1087,8 @@ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) /* If BAR is mapped and page aligned, update to fill PAGE_SIZE */ if (bar_addr != PCI_BAR_UNMAPPED && - !(bar_addr & ~qemu_real_host_page_mask)) { - size = qemu_real_host_page_size; + !(bar_addr & ~qemu_real_host_page_mask())) { + size = qemu_real_host_page_size(); } memory_region_transaction_begin(); @@ -1201,7 +1204,7 @@ void vfio_pci_write_config(PCIDevice *pdev, for (bar = 0; bar < PCI_ROM_SLOT; bar++) { if (old_addr[bar] != pdev->io_regions[bar].addr && vdev->bars[bar].region.size > 0 && - vdev->bars[bar].region.size < qemu_real_host_page_size) { + vdev->bars[bar].region.size < qemu_real_host_page_size()) { vfio_sub_page_bar_update_mapping(pdev, bar); } } @@ -1289,7 +1292,7 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) } /* MSI-X table start and end aligned to host page size */ - start = vdev->msix->table_offset & qemu_real_host_page_mask; + start = vdev->msix->table_offset & qemu_real_host_page_mask(); end = REAL_HOST_PAGE_ALIGN((uint64_t)vdev->msix->table_offset + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE)); @@ -1529,8 +1532,8 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) int ret; Error *err = NULL; - vdev->msix->pending = g_malloc0(BITS_TO_LONGS(vdev->msix->entries) * - sizeof(unsigned long)); + vdev->msix->pending = g_new0(unsigned long, + BITS_TO_LONGS(vdev->msix->entries)); ret = msix_init(&vdev->pdev, vdev->msix->entries, vdev->bars[vdev->msix->table_bar].mr, vdev->msix->table_bar, vdev->msix->table_offset, @@ -2475,7 +2478,7 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) */ if (old_addr[bar] != pdev->io_regions[bar].addr && vdev->bars[bar].region.size > 0 && - vdev->bars[bar].region.size < qemu_real_host_page_size) { + vdev->bars[bar].region.size < qemu_real_host_page_size()) { vfio_sub_page_bar_update_mapping(pdev, bar); } } diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index f8f08a0f36..5af73f9287 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -71,7 +71,7 @@ static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev, sysbus_init_irq(sbdev, &intp->qemuirq); /* Get an eventfd for trigger */ - intp->interrupt = g_malloc0(sizeof(EventNotifier)); + intp->interrupt = g_new0(EventNotifier, 1); ret = event_notifier_init(intp->interrupt, 0); if (ret) { g_free(intp->interrupt); @@ -82,7 +82,7 @@ static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev, } if (vfio_irq_is_automasked(intp)) { /* Get an eventfd for resample/unmask */ - intp->unmask = g_malloc0(sizeof(EventNotifier)); + intp->unmask = g_new0(EventNotifier, 1); ret = event_notifier_init(intp->unmask, 0); if (ret) { g_free(intp->interrupt); diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 04c6e67f8f..9ec1e95f6d 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -44,7 +44,7 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; - hwaddr page_mask = qemu_real_host_page_mask; + hwaddr page_mask = qemu_real_host_page_mask(); struct vfio_iommu_spapr_register_memory reg = { .argsz = sizeof(reg), .flags = 0, @@ -102,7 +102,7 @@ static void vfio_prereg_listener_region_del(MemoryListener *listener, const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; - hwaddr page_mask = qemu_real_host_page_mask; + hwaddr page_mask = qemu_real_host_page_mask(); struct vfio_iommu_spapr_register_memory reg = { .argsz = sizeof(reg), .flags = 0, @@ -199,12 +199,12 @@ int vfio_spapr_create_window(VFIOContainer *container, * Below we look at qemu_real_host_page_size as TCEs are allocated from * system pages. */ - bits_per_level = ctz64(qemu_real_host_page_size) + 8; + bits_per_level = ctz64(qemu_real_host_page_size()) + 8; create.levels = bits_total / bits_per_level; if (bits_total % bits_per_level) { ++create.levels; } - max_levels = (64 - create.page_shift) / ctz64(qemu_real_host_page_size); + max_levels = (64 - create.page_shift) / ctz64(qemu_real_host_page_size()); for ( ; create.levels <= max_levels; ++create.levels) { ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); if (!ret) { diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 521f7d64a8..67dc77e00f 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -11,9 +11,9 @@ softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) virtio_ss = ss.source_set() virtio_ss.add(files('virtio.c')) -virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c')) +virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c', 'vhost-iova-tree.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user.c')) -virtio_ss.add(when: 'CONFIG_VHOST_VDPA', if_true: files('vhost-vdpa.c')) +virtio_ss.add(when: 'CONFIG_VHOST_VDPA', if_true: files('vhost-shadow-virtqueue.c', 'vhost-vdpa.c')) virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c')) virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c')) virtio_ss.add(when: ['CONFIG_VIRTIO_CRYPTO', 'CONFIG_VIRTIO_PCI'], if_true: files('virtio-crypto-pci.c')) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index f7ad6be5fb..333348d9d5 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -25,6 +25,8 @@ vhost_user_postcopy_waker_nomatch(const char *rb, uint64_t rb_offset) "%s + 0x%" # vhost-vdpa.c vhost_vdpa_dma_map(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint64_t uaddr, uint8_t perm, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" uaddr: 0x%"PRIx64" perm: 0x%"PRIx8" type: %"PRIu8 vhost_vdpa_dma_unmap(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" type: %"PRIu8 +vhost_vdpa_listener_begin_batch(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8 +vhost_vdpa_listener_commit(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8 vhost_vdpa_listener_region_add(void *vdpa, uint64_t iova, uint64_t llend, void *vaddr, bool readonly) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64" vaddr: %p read-only: %d" vhost_vdpa_listener_region_del(void *vdpa, uint64_t iova, uint64_t llend) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64 vhost_vdpa_add_status(void *dev, uint8_t status) "dev: %p status: 0x%"PRIx8 @@ -89,9 +91,11 @@ virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d" # virtio-iommu.c virtio_iommu_device_reset(void) "reset!" +virtio_iommu_system_reset(void) "system reset!" virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64 virtio_iommu_device_status(uint8_t status) "driver status = %d" -virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x" +virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size, uint8_t bypass) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x bypass=0x%x" +virtio_iommu_set_config(uint8_t bypass) "bypass=0x%x" virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d" diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c new file mode 100644 index 0000000000..67bf6d57ab --- /dev/null +++ b/hw/virtio/vhost-iova-tree.c @@ -0,0 +1,110 @@ +/* + * vhost software live migration iova tree + * + * SPDX-FileCopyrightText: Red Hat, Inc. 2021 + * SPDX-FileContributor: Author: Eugenio Pérez + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/iova-tree.h" +#include "vhost-iova-tree.h" + +#define iova_min_addr qemu_real_host_page_size() + +/** + * VhostIOVATree, able to: + * - Translate iova address + * - Reverse translate iova address (from translated to iova) + * - Allocate IOVA regions for translated range (linear operation) + */ +struct VhostIOVATree { + /* First addressable iova address in the device */ + uint64_t iova_first; + + /* Last addressable iova address in the device */ + uint64_t iova_last; + + /* IOVA address to qemu memory maps. */ + IOVATree *iova_taddr_map; +}; + +/** + * Create a new IOVA tree + * + * Returns the new IOVA tree + */ +VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) +{ + VhostIOVATree *tree = g_new(VhostIOVATree, 1); + + /* Some devices do not like 0 addresses */ + tree->iova_first = MAX(iova_first, iova_min_addr); + tree->iova_last = iova_last; + + tree->iova_taddr_map = iova_tree_new(); + return tree; +} + +/** + * Delete an iova tree + */ +void vhost_iova_tree_delete(VhostIOVATree *iova_tree) +{ + iova_tree_destroy(iova_tree->iova_taddr_map); + g_free(iova_tree); +} + +/** + * Find the IOVA address stored from a memory address + * + * @tree: The iova tree + * @map: The map with the memory address + * + * Return the stored mapping, or NULL if not found. + */ +const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, + const DMAMap *map) +{ + return iova_tree_find_iova(tree->iova_taddr_map, map); +} + +/** + * Allocate a new mapping + * + * @tree: The iova tree + * @map: The iova map + * + * Returns: + * - IOVA_OK if the map fits in the container + * - IOVA_ERR_INVALID if the map does not make sense (like size overflow) + * - IOVA_ERR_NOMEM if tree cannot allocate more space. + * + * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK. + */ +int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map) +{ + /* Some vhost devices do not like addr 0. Skip first page */ + hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size(); + + if (map->translated_addr + map->size < map->translated_addr || + map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* Allocate a node in IOVA address */ + return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first, + tree->iova_last); +} + +/** + * Remove existing mappings from iova tree + * + * @iova_tree: The vhost iova tree + * @map: The map to remove + */ +void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map) +{ + iova_tree_remove(iova_tree->iova_taddr_map, map); +} diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h new file mode 100644 index 0000000000..6a4f24e0f9 --- /dev/null +++ b/hw/virtio/vhost-iova-tree.h @@ -0,0 +1,27 @@ +/* + * vhost software live migration iova tree + * + * SPDX-FileCopyrightText: Red Hat, Inc. 2021 + * SPDX-FileContributor: Author: Eugenio Pérez + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VIRTIO_VHOST_IOVA_TREE_H +#define HW_VIRTIO_VHOST_IOVA_TREE_H + +#include "qemu/iova-tree.h" +#include "exec/memory.h" + +typedef struct VhostIOVATree VhostIOVATree; + +VhostIOVATree *vhost_iova_tree_new(uint64_t iova_first, uint64_t iova_last); +void vhost_iova_tree_delete(VhostIOVATree *iova_tree); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostIOVATree, vhost_iova_tree_delete); + +const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree, + const DMAMap *map); +int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map); +void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map); + +#endif diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c new file mode 100644 index 0000000000..1e5cfe2af6 --- /dev/null +++ b/hw/virtio/vhost-shadow-virtqueue.c @@ -0,0 +1,636 @@ +/* + * vhost shadow virtqueue + * + * SPDX-FileCopyrightText: Red Hat, Inc. 2021 + * SPDX-FileContributor: Author: Eugenio Pérez + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/virtio/vhost-shadow-virtqueue.h" + +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "qemu/log.h" +#include "qemu/memalign.h" +#include "linux-headers/linux/vhost.h" + +/** + * Validate the transport device features that both guests can use with the SVQ + * and SVQs can use with the device. + * + * @dev_features: The features + * @errp: Error pointer + */ +bool vhost_svq_valid_features(uint64_t features, Error **errp) +{ + bool ok = true; + uint64_t svq_features = features; + + for (uint64_t b = VIRTIO_TRANSPORT_F_START; b <= VIRTIO_TRANSPORT_F_END; + ++b) { + switch (b) { + case VIRTIO_F_ANY_LAYOUT: + continue; + + case VIRTIO_F_ACCESS_PLATFORM: + /* SVQ trust in the host's IOMMU to translate addresses */ + case VIRTIO_F_VERSION_1: + /* SVQ trust that the guest vring is little endian */ + if (!(svq_features & BIT_ULL(b))) { + svq_features |= BIT_ULL(b); + ok = false; + } + continue; + + default: + if (svq_features & BIT_ULL(b)) { + svq_features &= ~BIT_ULL(b); + ok = false; + } + } + } + + if (!ok) { + error_setg(errp, "SVQ Invalid device feature flags, offer: 0x%"PRIx64 + ", ok: 0x%"PRIx64, features, svq_features); + } + return ok; +} + +/** + * Number of descriptors that the SVQ can make available from the guest. + * + * @svq: The svq + */ +static uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq) +{ + return svq->vring.num - (svq->shadow_avail_idx - svq->shadow_used_idx); +} + +/** + * Translate addresses between the qemu's virtual address and the SVQ IOVA + * + * @svq: Shadow VirtQueue + * @vaddr: Translated IOVA addresses + * @iovec: Source qemu's VA addresses + * @num: Length of iovec and minimum length of vaddr + */ +static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, + hwaddr *addrs, const struct iovec *iovec, + size_t num) +{ + if (num == 0) { + return true; + } + + for (size_t i = 0; i < num; ++i) { + DMAMap needle = { + .translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base, + .size = iovec[i].iov_len, + }; + Int128 needle_last, map_last; + size_t off; + + const DMAMap *map = vhost_iova_tree_find_iova(svq->iova_tree, &needle); + /* + * Map cannot be NULL since iova map contains all guest space and + * qemu already has a physical address mapped + */ + if (unlikely(!map)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid address 0x%"HWADDR_PRIx" given by guest", + needle.translated_addr); + return false; + } + + off = needle.translated_addr - map->translated_addr; + addrs[i] = map->iova + off; + + needle_last = int128_add(int128_make64(needle.translated_addr), + int128_make64(iovec[i].iov_len)); + map_last = int128_make64(map->translated_addr + map->size); + if (unlikely(int128_gt(needle_last, map_last))) { + qemu_log_mask(LOG_GUEST_ERROR, + "Guest buffer expands over iova range"); + return false; + } + } + + return true; +} + +static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, + const struct iovec *iovec, size_t num, + bool more_descs, bool write) +{ + uint16_t i = svq->free_head, last = svq->free_head; + unsigned n; + uint16_t flags = write ? cpu_to_le16(VRING_DESC_F_WRITE) : 0; + vring_desc_t *descs = svq->vring.desc; + + if (num == 0) { + return; + } + + for (n = 0; n < num; n++) { + if (more_descs || (n + 1 < num)) { + descs[i].flags = flags | cpu_to_le16(VRING_DESC_F_NEXT); + } else { + descs[i].flags = flags; + } + descs[i].addr = cpu_to_le64(sg[n]); + descs[i].len = cpu_to_le32(iovec[n].iov_len); + + last = i; + i = cpu_to_le16(descs[i].next); + } + + svq->free_head = le16_to_cpu(descs[last].next); +} + +static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, + VirtQueueElement *elem, unsigned *head) +{ + unsigned avail_idx; + vring_avail_t *avail = svq->vring.avail; + bool ok; + g_autofree hwaddr *sgs = g_new(hwaddr, MAX(elem->out_num, elem->in_num)); + + *head = svq->free_head; + + /* We need some descriptors here */ + if (unlikely(!elem->out_num && !elem->in_num)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Guest provided element with no descriptors"); + return false; + } + + ok = vhost_svq_translate_addr(svq, sgs, elem->out_sg, elem->out_num); + if (unlikely(!ok)) { + return false; + } + vhost_vring_write_descs(svq, sgs, elem->out_sg, elem->out_num, + elem->in_num > 0, false); + + + ok = vhost_svq_translate_addr(svq, sgs, elem->in_sg, elem->in_num); + if (unlikely(!ok)) { + return false; + } + + vhost_vring_write_descs(svq, sgs, elem->in_sg, elem->in_num, false, true); + + /* + * Put the entry in the available array (but don't update avail->idx until + * they do sync). + */ + avail_idx = svq->shadow_avail_idx & (svq->vring.num - 1); + avail->ring[avail_idx] = cpu_to_le16(*head); + svq->shadow_avail_idx++; + + /* Update the avail index after write the descriptor */ + smp_wmb(); + avail->idx = cpu_to_le16(svq->shadow_avail_idx); + + return true; +} + +static bool vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem) +{ + unsigned qemu_head; + bool ok = vhost_svq_add_split(svq, elem, &qemu_head); + if (unlikely(!ok)) { + return false; + } + + svq->ring_id_maps[qemu_head] = elem; + return true; +} + +static void vhost_svq_kick(VhostShadowVirtqueue *svq) +{ + /* + * We need to expose the available array entries before checking the used + * flags + */ + smp_mb(); + if (svq->vring.used->flags & VRING_USED_F_NO_NOTIFY) { + return; + } + + event_notifier_set(&svq->hdev_kick); +} + +/** + * Forward available buffers. + * + * @svq: Shadow VirtQueue + * + * Note that this function does not guarantee that all guest's available + * buffers are available to the device in SVQ avail ring. The guest may have + * exposed a GPA / GIOVA contiguous buffer, but it may not be contiguous in + * qemu vaddr. + * + * If that happens, guest's kick notifications will be disabled until the + * device uses some buffers. + */ +static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq) +{ + /* Clear event notifier */ + event_notifier_test_and_clear(&svq->svq_kick); + + /* Forward to the device as many available buffers as possible */ + do { + virtio_queue_set_notification(svq->vq, false); + + while (true) { + VirtQueueElement *elem; + bool ok; + + if (svq->next_guest_avail_elem) { + elem = g_steal_pointer(&svq->next_guest_avail_elem); + } else { + elem = virtqueue_pop(svq->vq, sizeof(*elem)); + } + + if (!elem) { + break; + } + + if (elem->out_num + elem->in_num > vhost_svq_available_slots(svq)) { + /* + * This condition is possible since a contiguous buffer in GPA + * does not imply a contiguous buffer in qemu's VA + * scatter-gather segments. If that happens, the buffer exposed + * to the device needs to be a chain of descriptors at this + * moment. + * + * SVQ cannot hold more available buffers if we are here: + * queue the current guest descriptor and ignore further kicks + * until some elements are used. + */ + svq->next_guest_avail_elem = elem; + return; + } + + ok = vhost_svq_add(svq, elem); + if (unlikely(!ok)) { + /* VQ is broken, just return and ignore any other kicks */ + return; + } + vhost_svq_kick(svq); + } + + virtio_queue_set_notification(svq->vq, true); + } while (!virtio_queue_empty(svq->vq)); +} + +/** + * Handle guest's kick. + * + * @n: guest kick event notifier, the one that guest set to notify svq. + */ +static void vhost_handle_guest_kick_notifier(EventNotifier *n) +{ + VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, svq_kick); + event_notifier_test_and_clear(n); + vhost_handle_guest_kick(svq); +} + +static bool vhost_svq_more_used(VhostShadowVirtqueue *svq) +{ + if (svq->last_used_idx != svq->shadow_used_idx) { + return true; + } + + svq->shadow_used_idx = cpu_to_le16(svq->vring.used->idx); + + return svq->last_used_idx != svq->shadow_used_idx; +} + +/** + * Enable vhost device calls after disable them. + * + * @svq: The svq + * + * It returns false if there are pending used buffers from the vhost device, + * avoiding the possible races between SVQ checking for more work and enabling + * callbacks. True if SVQ used vring has no more pending buffers. + */ +static bool vhost_svq_enable_notification(VhostShadowVirtqueue *svq) +{ + svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); + /* Make sure the flag is written before the read of used_idx */ + smp_mb(); + return !vhost_svq_more_used(svq); +} + +static void vhost_svq_disable_notification(VhostShadowVirtqueue *svq) +{ + svq->vring.avail->flags |= cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); +} + +static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq, + uint32_t *len) +{ + vring_desc_t *descs = svq->vring.desc; + const vring_used_t *used = svq->vring.used; + vring_used_elem_t used_elem; + uint16_t last_used; + + if (!vhost_svq_more_used(svq)) { + return NULL; + } + + /* Only get used array entries after they have been exposed by dev */ + smp_rmb(); + last_used = svq->last_used_idx & (svq->vring.num - 1); + used_elem.id = le32_to_cpu(used->ring[last_used].id); + used_elem.len = le32_to_cpu(used->ring[last_used].len); + + svq->last_used_idx++; + if (unlikely(used_elem.id >= svq->vring.num)) { + qemu_log_mask(LOG_GUEST_ERROR, "Device %s says index %u is used", + svq->vdev->name, used_elem.id); + return NULL; + } + + if (unlikely(!svq->ring_id_maps[used_elem.id])) { + qemu_log_mask(LOG_GUEST_ERROR, + "Device %s says index %u is used, but it was not available", + svq->vdev->name, used_elem.id); + return NULL; + } + + descs[used_elem.id].next = svq->free_head; + svq->free_head = used_elem.id; + + *len = used_elem.len; + return g_steal_pointer(&svq->ring_id_maps[used_elem.id]); +} + +static void vhost_svq_flush(VhostShadowVirtqueue *svq, + bool check_for_avail_queue) +{ + VirtQueue *vq = svq->vq; + + /* Forward as many used buffers as possible. */ + do { + unsigned i = 0; + + vhost_svq_disable_notification(svq); + while (true) { + uint32_t len; + g_autofree VirtQueueElement *elem = vhost_svq_get_buf(svq, &len); + if (!elem) { + break; + } + + if (unlikely(i >= svq->vring.num)) { + qemu_log_mask(LOG_GUEST_ERROR, + "More than %u used buffers obtained in a %u size SVQ", + i, svq->vring.num); + virtqueue_fill(vq, elem, len, i); + virtqueue_flush(vq, i); + return; + } + virtqueue_fill(vq, elem, len, i++); + } + + virtqueue_flush(vq, i); + event_notifier_set(&svq->svq_call); + + if (check_for_avail_queue && svq->next_guest_avail_elem) { + /* + * Avail ring was full when vhost_svq_flush was called, so it's a + * good moment to make more descriptors available if possible. + */ + vhost_handle_guest_kick(svq); + } + } while (!vhost_svq_enable_notification(svq)); +} + +/** + * Forward used buffers. + * + * @n: hdev call event notifier, the one that device set to notify svq. + * + * Note that we are not making any buffers available in the loop, there is no + * way that it runs more than virtqueue size times. + */ +static void vhost_svq_handle_call(EventNotifier *n) +{ + VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, + hdev_call); + event_notifier_test_and_clear(n); + vhost_svq_flush(svq, true); +} + +/** + * Set the call notifier for the SVQ to call the guest + * + * @svq: Shadow virtqueue + * @call_fd: call notifier + * + * Called on BQL context. + */ +void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd) +{ + if (call_fd == VHOST_FILE_UNBIND) { + /* + * Fail event_notifier_set if called handling device call. + * + * SVQ still needs device notifications, since it needs to keep + * forwarding used buffers even with the unbind. + */ + memset(&svq->svq_call, 0, sizeof(svq->svq_call)); + } else { + event_notifier_init_fd(&svq->svq_call, call_fd); + } +} + +/** + * Get the shadow vq vring address. + * @svq: Shadow virtqueue + * @addr: Destination to store address + */ +void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq, + struct vhost_vring_addr *addr) +{ + addr->desc_user_addr = (uint64_t)(uintptr_t)svq->vring.desc; + addr->avail_user_addr = (uint64_t)(uintptr_t)svq->vring.avail; + addr->used_user_addr = (uint64_t)(uintptr_t)svq->vring.used; +} + +size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq) +{ + size_t desc_size = sizeof(vring_desc_t) * svq->vring.num; + size_t avail_size = offsetof(vring_avail_t, ring) + + sizeof(uint16_t) * svq->vring.num; + + return ROUND_UP(desc_size + avail_size, qemu_real_host_page_size()); +} + +size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq) +{ + size_t used_size = offsetof(vring_used_t, ring) + + sizeof(vring_used_elem_t) * svq->vring.num; + return ROUND_UP(used_size, qemu_real_host_page_size()); +} + +/** + * Set a new file descriptor for the guest to kick the SVQ and notify for avail + * + * @svq: The svq + * @svq_kick_fd: The svq kick fd + * + * Note that the SVQ will never close the old file descriptor. + */ +void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd) +{ + EventNotifier *svq_kick = &svq->svq_kick; + bool poll_stop = VHOST_FILE_UNBIND != event_notifier_get_fd(svq_kick); + bool poll_start = svq_kick_fd != VHOST_FILE_UNBIND; + + if (poll_stop) { + event_notifier_set_handler(svq_kick, NULL); + } + + /* + * event_notifier_set_handler already checks for guest's notifications if + * they arrive at the new file descriptor in the switch, so there is no + * need to explicitly check for them. + */ + if (poll_start) { + event_notifier_init_fd(svq_kick, svq_kick_fd); + event_notifier_set(svq_kick); + event_notifier_set_handler(svq_kick, vhost_handle_guest_kick_notifier); + } +} + +/** + * Start the shadow virtqueue operation. + * + * @svq: Shadow Virtqueue + * @vdev: VirtIO device + * @vq: Virtqueue to shadow + */ +void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, + VirtQueue *vq) +{ + size_t desc_size, driver_size, device_size; + + svq->next_guest_avail_elem = NULL; + svq->shadow_avail_idx = 0; + svq->shadow_used_idx = 0; + svq->last_used_idx = 0; + svq->vdev = vdev; + svq->vq = vq; + + svq->vring.num = virtio_queue_get_num(vdev, virtio_get_queue_index(vq)); + driver_size = vhost_svq_driver_area_size(svq); + device_size = vhost_svq_device_area_size(svq); + svq->vring.desc = qemu_memalign(qemu_real_host_page_size(), driver_size); + desc_size = sizeof(vring_desc_t) * svq->vring.num; + svq->vring.avail = (void *)((char *)svq->vring.desc + desc_size); + memset(svq->vring.desc, 0, driver_size); + svq->vring.used = qemu_memalign(qemu_real_host_page_size(), device_size); + memset(svq->vring.used, 0, device_size); + svq->ring_id_maps = g_new0(VirtQueueElement *, svq->vring.num); + for (unsigned i = 0; i < svq->vring.num - 1; i++) { + svq->vring.desc[i].next = cpu_to_le16(i + 1); + } +} + +/** + * Stop the shadow virtqueue operation. + * @svq: Shadow Virtqueue + */ +void vhost_svq_stop(VhostShadowVirtqueue *svq) +{ + event_notifier_set_handler(&svq->svq_kick, NULL); + g_autofree VirtQueueElement *next_avail_elem = NULL; + + if (!svq->vq) { + return; + } + + /* Send all pending used descriptors to guest */ + vhost_svq_flush(svq, false); + + for (unsigned i = 0; i < svq->vring.num; ++i) { + g_autofree VirtQueueElement *elem = NULL; + elem = g_steal_pointer(&svq->ring_id_maps[i]); + if (elem) { + virtqueue_detach_element(svq->vq, elem, 0); + } + } + + next_avail_elem = g_steal_pointer(&svq->next_guest_avail_elem); + if (next_avail_elem) { + virtqueue_detach_element(svq->vq, next_avail_elem, 0); + } + svq->vq = NULL; + g_free(svq->ring_id_maps); + qemu_vfree(svq->vring.desc); + qemu_vfree(svq->vring.used); +} + +/** + * Creates vhost shadow virtqueue, and instructs the vhost device to use the + * shadow methods and file descriptors. + * + * @iova_tree: Tree to perform descriptors translations + * + * Returns the new virtqueue or NULL. + * + * In case of error, reason is reported through error_report. + */ +VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree) +{ + g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1); + int r; + + r = event_notifier_init(&svq->hdev_kick, 0); + if (r != 0) { + error_report("Couldn't create kick event notifier: %s (%d)", + g_strerror(errno), errno); + goto err_init_hdev_kick; + } + + r = event_notifier_init(&svq->hdev_call, 0); + if (r != 0) { + error_report("Couldn't create call event notifier: %s (%d)", + g_strerror(errno), errno); + goto err_init_hdev_call; + } + + event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND); + event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call); + svq->iova_tree = iova_tree; + return g_steal_pointer(&svq); + +err_init_hdev_call: + event_notifier_cleanup(&svq->hdev_kick); + +err_init_hdev_kick: + return NULL; +} + +/** + * Free the resources of the shadow virtqueue. + * + * @pvq: gpointer to SVQ so it can be used by autofree functions. + */ +void vhost_svq_free(gpointer pvq) +{ + VhostShadowVirtqueue *vq = pvq; + vhost_svq_stop(vq); + event_notifier_cleanup(&vq->hdev_kick); + event_notifier_set_handler(&vq->hdev_call, NULL); + event_notifier_cleanup(&vq->hdev_call); + g_free(vq); +} diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h new file mode 100644 index 0000000000..e5e24c536d --- /dev/null +++ b/hw/virtio/vhost-shadow-virtqueue.h @@ -0,0 +1,87 @@ +/* + * vhost shadow virtqueue + * + * SPDX-FileCopyrightText: Red Hat, Inc. 2021 + * SPDX-FileContributor: Author: Eugenio Pérez + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef VHOST_SHADOW_VIRTQUEUE_H +#define VHOST_SHADOW_VIRTQUEUE_H + +#include "qemu/event_notifier.h" +#include "hw/virtio/virtio.h" +#include "standard-headers/linux/vhost_types.h" +#include "hw/virtio/vhost-iova-tree.h" + +/* Shadow virtqueue to relay notifications */ +typedef struct VhostShadowVirtqueue { + /* Shadow vring */ + struct vring vring; + + /* Shadow kick notifier, sent to vhost */ + EventNotifier hdev_kick; + /* Shadow call notifier, sent to vhost */ + EventNotifier hdev_call; + + /* + * Borrowed virtqueue's guest to host notifier. To borrow it in this event + * notifier allows to recover the VhostShadowVirtqueue from the event loop + * easily. If we use the VirtQueue's one, we don't have an easy way to + * retrieve VhostShadowVirtqueue. + * + * So shadow virtqueue must not clean it, or we would lose VirtQueue one. + */ + EventNotifier svq_kick; + + /* Guest's call notifier, where the SVQ calls guest. */ + EventNotifier svq_call; + + /* Virtio queue shadowing */ + VirtQueue *vq; + + /* Virtio device */ + VirtIODevice *vdev; + + /* IOVA mapping */ + VhostIOVATree *iova_tree; + + /* Map for use the guest's descriptors */ + VirtQueueElement **ring_id_maps; + + /* Next VirtQueue element that guest made available */ + VirtQueueElement *next_guest_avail_elem; + + /* Next head to expose to the device */ + uint16_t shadow_avail_idx; + + /* Next free descriptor */ + uint16_t free_head; + + /* Last seen used idx */ + uint16_t shadow_used_idx; + + /* Next head to consume from the device */ + uint16_t last_used_idx; +} VhostShadowVirtqueue; + +bool vhost_svq_valid_features(uint64_t features, Error **errp); + +void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd); +void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd); +void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq, + struct vhost_vring_addr *addr); +size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq); +size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq); + +void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, + VirtQueue *vq); +void vhost_svq_stop(VhostShadowVirtqueue *svq); + +VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree); + +void vhost_svq_free(gpointer vq); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free); + +#endif diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c index d172632bb0..42c7f6d9e5 100644 --- a/hw/virtio/vhost-user-i2c.c +++ b/hw/virtio/vhost-user-i2c.c @@ -19,6 +19,11 @@ #define VIRTIO_ID_I2C_ADAPTER 34 #endif +static const int feature_bits[] = { + VIRTIO_I2C_F_ZERO_LENGTH_REQUEST, + VHOST_INVALID_FEATURE_BIT +}; + static void vu_i2c_start(VirtIODevice *vdev) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -113,8 +118,10 @@ static void vu_i2c_set_status(VirtIODevice *vdev, uint8_t status) static uint64_t vu_i2c_get_features(VirtIODevice *vdev, uint64_t requested_features, Error **errp) { - /* No feature bits used yet */ - return requested_features; + VHostUserI2C *i2c = VHOST_USER_I2C(vdev); + + virtio_add_feature(&requested_features, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST); + return vhost_get_features(&i2c->vhost_dev, feature_bits, requested_features); } static void vu_i2c_handle_output(VirtIODevice *vdev, VirtQueue *vq) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 662853513e..a80315ecfc 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -25,6 +25,7 @@ #include "migration/migration.h" #include "migration/postcopy-ram.h" #include "trace.h" +#include "exec/ramblock.h" #include #include @@ -1162,37 +1163,32 @@ static int vhost_user_set_vring_num(struct vhost_dev *dev, return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); } -static void vhost_user_host_notifier_restore(struct vhost_dev *dev, - int queue_idx) +static void vhost_user_host_notifier_free(VhostUserHostNotifier *n) { - struct vhost_user *u = dev->opaque; - VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; - VirtIODevice *vdev = dev->vdev; - - if (n->addr && !n->set) { - virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true); - n->set = true; - } + assert(n && n->unmap_addr); + munmap(n->unmap_addr, qemu_real_host_page_size()); + n->unmap_addr = NULL; } -static void vhost_user_host_notifier_remove(struct vhost_dev *dev, - int queue_idx) +static void vhost_user_host_notifier_remove(VhostUserState *user, + VirtIODevice *vdev, int queue_idx) { - struct vhost_user *u = dev->opaque; - VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; - VirtIODevice *vdev = dev->vdev; + VhostUserHostNotifier *n = &user->notifier[queue_idx]; - if (n->addr && n->set) { - virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); - n->set = false; + if (n->addr) { + if (vdev) { + virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); + } + assert(!n->unmap_addr); + n->unmap_addr = n->addr; + n->addr = NULL; + call_rcu(n, vhost_user_host_notifier_free, rcu); } } static int vhost_user_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { - vhost_user_host_notifier_restore(dev, ring->index); - return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); } @@ -1235,8 +1231,9 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, .payload.state = *ring, .hdr.size = sizeof(msg.payload.state), }; + struct vhost_user *u = dev->opaque; - vhost_user_host_notifier_remove(dev, ring->index); + vhost_user_host_notifier_remove(u->user, dev->vdev, ring->index); ret = vhost_user_write(dev, &msg, NULL, 0); if (ret < 0) { @@ -1506,7 +1503,7 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, int fd) { int queue_idx = area->u64 & VHOST_USER_VRING_IDX_MASK; - size_t page_size = qemu_real_host_page_size; + size_t page_size = qemu_real_host_page_size(); struct vhost_user *u = dev->opaque; VhostUserState *user = u->user; VirtIODevice *vdev = dev->vdev; @@ -1522,12 +1519,7 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, n = &user->notifier[queue_idx]; - if (n->addr) { - virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); - object_unparent(OBJECT(&n->mr)); - munmap(n->addr, page_size); - n->addr = NULL; - } + vhost_user_host_notifier_remove(user, vdev, queue_idx); if (area->u64 & VHOST_USER_VRING_NOFD_MASK) { return 0; @@ -1546,9 +1538,12 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]", user, queue_idx); - if (!n->mr.ram) /* Don't init again after suspend. */ + if (!n->mr.ram) { /* Don't init again after suspend. */ memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, page_size, addr); + } else { + n->mr.ram_block->host = addr; + } g_free(name); if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) { @@ -1558,7 +1553,6 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, } n->addr = addr; - n->set = true; return 0; } @@ -1832,7 +1826,7 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) error_setg(errp, "%s: Failed to get ufd", __func__); return -EIO; } - qemu_set_nonblock(ufd); + qemu_socket_set_nonblock(ufd); /* register ufd with userfault thread */ u->postcopy_fd.fd = ufd; @@ -2522,17 +2516,16 @@ bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp) void vhost_user_cleanup(VhostUserState *user) { int i; + VhostUserHostNotifier *n; if (!user->chr) { return; } memory_region_transaction_begin(); for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (user->notifier[i].addr) { - object_unparent(OBJECT(&user->notifier[i].mr)); - munmap(user->notifier[i].addr, qemu_real_host_page_size); - user->notifier[i].addr = NULL; - } + n = &user->notifier[i]; + vhost_user_host_notifier_remove(user, NULL, i); + object_unparent(OBJECT(&n->mr)); } memory_region_transaction_commit(); user->chr = NULL; diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 04ea43704f..a30510ed17 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -17,12 +17,14 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio-net.h" +#include "hw/virtio/vhost-shadow-virtqueue.h" #include "hw/virtio/vhost-vdpa.h" #include "exec/address-spaces.h" +#include "qemu/cutils.h" #include "qemu/main-loop.h" #include "cpu.h" #include "trace.h" -#include "qemu-common.h" +#include "qapi/error.h" /* * Return one past the end of the end of section. Be careful with uint64_t @@ -127,6 +129,7 @@ static void vhost_vdpa_listener_begin_batch(struct vhost_vdpa *v) .iotlb.type = VHOST_IOTLB_BATCH_BEGIN, }; + trace_vhost_vdpa_listener_begin_batch(v, fd, msg.type, msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); @@ -161,6 +164,7 @@ static void vhost_vdpa_listener_commit(MemoryListener *listener) msg.type = v->msg_type; msg.iotlb.type = VHOST_IOTLB_BATCH_END; + trace_vhost_vdpa_listener_commit(v, fd, msg.type, msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); @@ -207,6 +211,21 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, vaddr, section->readonly); llsize = int128_sub(llend, int128_make64(iova)); + if (v->shadow_vqs_enabled) { + DMAMap mem_region = { + .translated_addr = (hwaddr)(uintptr_t)vaddr, + .size = int128_get64(llsize) - 1, + .perm = IOMMU_ACCESS_FLAG(true, section->readonly), + }; + + int r = vhost_iova_tree_map_alloc(v->iova_tree, &mem_region); + if (unlikely(r != IOVA_OK)) { + error_report("Can't allocate a mapping (%d)", r); + goto fail; + } + + iova = mem_region.iova; + } vhost_vdpa_iotlb_batch_begin_once(v); ret = vhost_vdpa_dma_map(v, iova, int128_get64(llsize), @@ -259,6 +278,20 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); + if (v->shadow_vqs_enabled) { + const DMAMap *result; + const void *vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + DMAMap mem_region = { + .translated_addr = (hwaddr)(uintptr_t)vaddr, + .size = int128_get64(llsize) - 1, + }; + + result = vhost_iova_tree_find_iova(v->iova_tree, &mem_region); + iova = result->iova; + vhost_iova_tree_remove(v->iova_tree, &mem_region); + } vhost_vdpa_iotlb_batch_begin_once(v); ret = vhost_vdpa_dma_unmap(v, iova, int128_get64(llsize)); if (ret) { @@ -268,7 +301,7 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, memory_region_unref(section->mr); } /* - * IOTLB API is used by vhost-vpda which requires incremental updating + * IOTLB API is used by vhost-vdpa which requires incremental updating * of the mapping. So we can not use generic vhost memory listener which * depends on the addnop(). */ @@ -342,6 +375,55 @@ static bool vhost_vdpa_one_time_request(struct vhost_dev *dev) return v->index != 0; } +static int vhost_vdpa_get_dev_features(struct vhost_dev *dev, + uint64_t *features) +{ + int ret; + + ret = vhost_vdpa_call(dev, VHOST_GET_FEATURES, features); + trace_vhost_vdpa_get_features(dev, *features); + return ret; +} + +static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v, + Error **errp) +{ + g_autoptr(GPtrArray) shadow_vqs = NULL; + uint64_t dev_features, svq_features; + int r; + bool ok; + + if (!v->shadow_vqs_enabled) { + return 0; + } + + r = vhost_vdpa_get_dev_features(hdev, &dev_features); + if (r != 0) { + error_setg_errno(errp, -r, "Can't get vdpa device features"); + return r; + } + + svq_features = dev_features; + ok = vhost_svq_valid_features(svq_features, errp); + if (unlikely(!ok)) { + return -1; + } + + shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free); + for (unsigned n = 0; n < hdev->nvqs; ++n) { + g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new(v->iova_tree); + + if (unlikely(!svq)) { + error_setg(errp, "Cannot create svq %u", n); + return -1; + } + g_ptr_array_add(shadow_vqs, g_steal_pointer(&svq)); + } + + v->shadow_vqs = g_steal_pointer(&shadow_vqs); + return 0; +} + static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) { struct vhost_vdpa *v; @@ -364,6 +446,10 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) dev->opaque = opaque ; v->listener = vhost_vdpa_memory_listener; v->msg_type = VHOST_IOTLB_MSG_V2; + ret = vhost_vdpa_init_svq(dev, v, errp); + if (ret) { + goto err; + } vhost_vdpa_get_iova_range(v); @@ -375,12 +461,16 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) VIRTIO_CONFIG_S_DRIVER); return 0; + +err: + ram_block_discard_disable(false); + return ret; } static void vhost_vdpa_host_notifier_uninit(struct vhost_dev *dev, int queue_index) { - size_t page_size = qemu_real_host_page_size; + size_t page_size = qemu_real_host_page_size(); struct vhost_vdpa *v = dev->opaque; VirtIODevice *vdev = dev->vdev; VhostVDPAHostNotifier *n; @@ -395,18 +485,9 @@ static void vhost_vdpa_host_notifier_uninit(struct vhost_dev *dev, } } -static void vhost_vdpa_host_notifiers_uninit(struct vhost_dev *dev, int n) -{ - int i; - - for (i = 0; i < n; i++) { - vhost_vdpa_host_notifier_uninit(dev, i); - } -} - static int vhost_vdpa_host_notifier_init(struct vhost_dev *dev, int queue_index) { - size_t page_size = qemu_real_host_page_size; + size_t page_size = qemu_real_host_page_size(); struct vhost_vdpa *v = dev->opaque; VirtIODevice *vdev = dev->vdev; VhostVDPAHostNotifier *n; @@ -431,6 +512,7 @@ static int vhost_vdpa_host_notifier_init(struct vhost_dev *dev, int queue_index) g_free(name); if (virtio_queue_set_host_notifier_mr(vdev, queue_index, &n->mr, true)) { + object_unparent(OBJECT(&n->mr)); munmap(addr, page_size); goto err; } @@ -442,10 +524,25 @@ err: return -1; } -static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev) +static void vhost_vdpa_host_notifiers_uninit(struct vhost_dev *dev, int n) { int i; + for (i = dev->vq_index; i < dev->vq_index + n; i++) { + vhost_vdpa_host_notifier_uninit(dev, i); + } +} + +static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + int i; + + if (v->shadow_vqs_enabled) { + /* FIXME SVQ is not compatible with host notifiers mr */ + return; + } + for (i = dev->vq_index; i < dev->vq_index + dev->nvqs; i++) { if (vhost_vdpa_host_notifier_init(dev, i)) { goto err; @@ -455,10 +552,25 @@ static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev) return; err: - vhost_vdpa_host_notifiers_uninit(dev, i); + vhost_vdpa_host_notifiers_uninit(dev, i - dev->vq_index); return; } +static void vhost_vdpa_svq_cleanup(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + size_t idx; + + if (!v->shadow_vqs) { + return; + } + + for (idx = 0; idx < v->shadow_vqs->len; ++idx) { + vhost_svq_stop(g_ptr_array_index(v->shadow_vqs, idx)); + } + g_ptr_array_free(v->shadow_vqs, true); +} + static int vhost_vdpa_cleanup(struct vhost_dev *dev) { struct vhost_vdpa *v; @@ -467,6 +579,7 @@ static int vhost_vdpa_cleanup(struct vhost_dev *dev) trace_vhost_vdpa_cleanup(dev, v); vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); memory_listener_unregister(&v->listener); + vhost_vdpa_svq_cleanup(dev); dev->opaque = NULL; ram_block_discard_disable(false); @@ -509,12 +622,29 @@ static int vhost_vdpa_set_mem_table(struct vhost_dev *dev, static int vhost_vdpa_set_features(struct vhost_dev *dev, uint64_t features) { + struct vhost_vdpa *v = dev->opaque; int ret; if (vhost_vdpa_one_time_request(dev)) { return 0; } + if (v->shadow_vqs_enabled) { + if ((v->acked_features ^ features) == BIT_ULL(VHOST_F_LOG_ALL)) { + /* + * QEMU is just trying to enable or disable logging. SVQ handles + * this sepparately, so no need to forward this. + */ + v->acked_features = features; + return 0; + } + + v->acked_features = features; + + /* We must not ack _F_LOG if SVQ is enabled */ + features &= ~BIT_ULL(VHOST_F_LOG_ALL); + } + trace_vhost_vdpa_set_features(dev, features); ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features); if (ret) { @@ -558,11 +688,26 @@ static int vhost_vdpa_get_device_id(struct vhost_dev *dev, return ret; } +static void vhost_vdpa_reset_svq(struct vhost_vdpa *v) +{ + if (!v->shadow_vqs_enabled) { + return; + } + + for (unsigned i = 0; i < v->shadow_vqs->len; ++i) { + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i); + vhost_svq_stop(svq); + } +} + static int vhost_vdpa_reset_device(struct vhost_dev *dev) { + struct vhost_vdpa *v = dev->opaque; int ret; uint8_t status = 0; + vhost_vdpa_reset_svq(v); + ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); trace_vhost_vdpa_reset_device(dev, status); return ret; @@ -646,15 +791,311 @@ static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config, return ret; } +static int vhost_vdpa_set_dev_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + trace_vhost_vdpa_set_vring_base(dev, ring->index, ring->num); + return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring); +} + +static int vhost_vdpa_set_vring_dev_kick(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + trace_vhost_vdpa_set_vring_kick(dev, file->index, file->fd); + return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file); +} + +static int vhost_vdpa_set_vring_dev_call(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + trace_vhost_vdpa_set_vring_call(dev, file->index, file->fd); + return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file); +} + +static int vhost_vdpa_set_vring_dev_addr(struct vhost_dev *dev, + struct vhost_vring_addr *addr) +{ + trace_vhost_vdpa_set_vring_addr(dev, addr->index, addr->flags, + addr->desc_user_addr, addr->used_user_addr, + addr->avail_user_addr, + addr->log_guest_addr); + + return vhost_vdpa_call(dev, VHOST_SET_VRING_ADDR, addr); + +} + +/** + * Set the shadow virtqueue descriptors to the device + * + * @dev: The vhost device model + * @svq: The shadow virtqueue + * @idx: The index of the virtqueue in the vhost device + * @errp: Error + * + * Note that this function does not rewind kick file descriptor if cannot set + * call one. + */ +static int vhost_vdpa_svq_set_fds(struct vhost_dev *dev, + VhostShadowVirtqueue *svq, unsigned idx, + Error **errp) +{ + struct vhost_vring_file file = { + .index = dev->vq_index + idx, + }; + const EventNotifier *event_notifier = &svq->hdev_kick; + int r; + + file.fd = event_notifier_get_fd(event_notifier); + r = vhost_vdpa_set_vring_dev_kick(dev, &file); + if (unlikely(r != 0)) { + error_setg_errno(errp, -r, "Can't set device kick fd"); + return r; + } + + event_notifier = &svq->hdev_call; + file.fd = event_notifier_get_fd(event_notifier); + r = vhost_vdpa_set_vring_dev_call(dev, &file); + if (unlikely(r != 0)) { + error_setg_errno(errp, -r, "Can't set device call fd"); + } + + return r; +} + +/** + * Unmap a SVQ area in the device + */ +static bool vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, + const DMAMap *needle) +{ + const DMAMap *result = vhost_iova_tree_find_iova(v->iova_tree, needle); + hwaddr size; + int r; + + if (unlikely(!result)) { + error_report("Unable to find SVQ address to unmap"); + return false; + } + + size = ROUND_UP(result->size, qemu_real_host_page_size()); + r = vhost_vdpa_dma_unmap(v, result->iova, size); + return r == 0; +} + +static bool vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev, + const VhostShadowVirtqueue *svq) +{ + DMAMap needle = {}; + struct vhost_vdpa *v = dev->opaque; + struct vhost_vring_addr svq_addr; + bool ok; + + vhost_svq_get_vring_addr(svq, &svq_addr); + + needle.translated_addr = svq_addr.desc_user_addr; + ok = vhost_vdpa_svq_unmap_ring(v, &needle); + if (unlikely(!ok)) { + return false; + } + + needle.translated_addr = svq_addr.used_user_addr; + return vhost_vdpa_svq_unmap_ring(v, &needle); +} + +/** + * Map the SVQ area in the device + * + * @v: Vhost-vdpa device + * @needle: The area to search iova + * @errorp: Error pointer + */ +static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle, + Error **errp) +{ + int r; + + r = vhost_iova_tree_map_alloc(v->iova_tree, needle); + if (unlikely(r != IOVA_OK)) { + error_setg(errp, "Cannot allocate iova (%d)", r); + return false; + } + + r = vhost_vdpa_dma_map(v, needle->iova, needle->size + 1, + (void *)(uintptr_t)needle->translated_addr, + needle->perm == IOMMU_RO); + if (unlikely(r != 0)) { + error_setg_errno(errp, -r, "Cannot map region to device"); + vhost_iova_tree_remove(v->iova_tree, needle); + } + + return r == 0; +} + +/** + * Map the shadow virtqueue rings in the device + * + * @dev: The vhost device + * @svq: The shadow virtqueue + * @addr: Assigned IOVA addresses + * @errp: Error pointer + */ +static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, + const VhostShadowVirtqueue *svq, + struct vhost_vring_addr *addr, + Error **errp) +{ + DMAMap device_region, driver_region; + struct vhost_vring_addr svq_addr; + struct vhost_vdpa *v = dev->opaque; + size_t device_size = vhost_svq_device_area_size(svq); + size_t driver_size = vhost_svq_driver_area_size(svq); + size_t avail_offset; + bool ok; + + ERRP_GUARD(); + vhost_svq_get_vring_addr(svq, &svq_addr); + + driver_region = (DMAMap) { + .translated_addr = svq_addr.desc_user_addr, + .size = driver_size - 1, + .perm = IOMMU_RO, + }; + ok = vhost_vdpa_svq_map_ring(v, &driver_region, errp); + if (unlikely(!ok)) { + error_prepend(errp, "Cannot create vq driver region: "); + return false; + } + addr->desc_user_addr = driver_region.iova; + avail_offset = svq_addr.avail_user_addr - svq_addr.desc_user_addr; + addr->avail_user_addr = driver_region.iova + avail_offset; + + device_region = (DMAMap) { + .translated_addr = svq_addr.used_user_addr, + .size = device_size - 1, + .perm = IOMMU_RW, + }; + ok = vhost_vdpa_svq_map_ring(v, &device_region, errp); + if (unlikely(!ok)) { + error_prepend(errp, "Cannot create vq device region: "); + vhost_vdpa_svq_unmap_ring(v, &driver_region); + } + addr->used_user_addr = device_region.iova; + + return ok; +} + +static bool vhost_vdpa_svq_setup(struct vhost_dev *dev, + VhostShadowVirtqueue *svq, unsigned idx, + Error **errp) +{ + uint16_t vq_index = dev->vq_index + idx; + struct vhost_vring_state s = { + .index = vq_index, + }; + int r; + + r = vhost_vdpa_set_dev_vring_base(dev, &s); + if (unlikely(r)) { + error_setg_errno(errp, -r, "Cannot set vring base"); + return false; + } + + r = vhost_vdpa_svq_set_fds(dev, svq, idx, errp); + return r == 0; +} + +static bool vhost_vdpa_svqs_start(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + Error *err = NULL; + unsigned i; + + if (!v->shadow_vqs) { + return true; + } + + for (i = 0; i < v->shadow_vqs->len; ++i) { + VirtQueue *vq = virtio_get_queue(dev->vdev, dev->vq_index + i); + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i); + struct vhost_vring_addr addr = { + .index = i, + }; + int r; + bool ok = vhost_vdpa_svq_setup(dev, svq, i, &err); + if (unlikely(!ok)) { + goto err; + } + + vhost_svq_start(svq, dev->vdev, vq); + ok = vhost_vdpa_svq_map_rings(dev, svq, &addr, &err); + if (unlikely(!ok)) { + goto err_map; + } + + /* Override vring GPA set by vhost subsystem */ + r = vhost_vdpa_set_vring_dev_addr(dev, &addr); + if (unlikely(r != 0)) { + error_setg_errno(&err, -r, "Cannot set device address"); + goto err_set_addr; + } + } + + return true; + +err_set_addr: + vhost_vdpa_svq_unmap_rings(dev, g_ptr_array_index(v->shadow_vqs, i)); + +err_map: + vhost_svq_stop(g_ptr_array_index(v->shadow_vqs, i)); + +err: + error_reportf_err(err, "Cannot setup SVQ %u: ", i); + for (unsigned j = 0; j < i; ++j) { + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, j); + vhost_vdpa_svq_unmap_rings(dev, svq); + vhost_svq_stop(svq); + } + + return false; +} + +static bool vhost_vdpa_svqs_stop(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + + if (!v->shadow_vqs) { + return true; + } + + for (unsigned i = 0; i < v->shadow_vqs->len; ++i) { + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i); + bool ok = vhost_vdpa_svq_unmap_rings(dev, svq); + if (unlikely(!ok)) { + return false; + } + } + + return true; +} + static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) { struct vhost_vdpa *v = dev->opaque; + bool ok; trace_vhost_vdpa_dev_start(dev, started); if (started) { vhost_vdpa_host_notifiers_init(dev); + ok = vhost_vdpa_svqs_start(dev); + if (unlikely(!ok)) { + return -1; + } vhost_vdpa_set_vring_ready(dev); } else { + ok = vhost_vdpa_svqs_stop(dev); + if (unlikely(!ok)) { + return -1; + } vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); } @@ -678,7 +1119,8 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, struct vhost_log *log) { - if (vhost_vdpa_one_time_request(dev)) { + struct vhost_vdpa *v = dev->opaque; + if (v->shadow_vqs_enabled || vhost_vdpa_one_time_request(dev)) { return 0; } @@ -690,11 +1132,17 @@ static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, static int vhost_vdpa_set_vring_addr(struct vhost_dev *dev, struct vhost_vring_addr *addr) { - trace_vhost_vdpa_set_vring_addr(dev, addr->index, addr->flags, - addr->desc_user_addr, addr->used_user_addr, - addr->avail_user_addr, - addr->log_guest_addr); - return vhost_vdpa_call(dev, VHOST_SET_VRING_ADDR, addr); + struct vhost_vdpa *v = dev->opaque; + + if (v->shadow_vqs_enabled) { + /* + * Device vring addr was set at device start. SVQ base is handled by + * VirtQueue code. + */ + return 0; + } + + return vhost_vdpa_set_vring_dev_addr(dev, addr); } static int vhost_vdpa_set_vring_num(struct vhost_dev *dev, @@ -707,15 +1155,41 @@ static int vhost_vdpa_set_vring_num(struct vhost_dev *dev, static int vhost_vdpa_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { - trace_vhost_vdpa_set_vring_base(dev, ring->index, ring->num); - return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring); + struct vhost_vdpa *v = dev->opaque; + + if (v->shadow_vqs_enabled) { + /* + * Device vring base was set at device start. SVQ base is handled by + * VirtQueue code. + */ + return 0; + } + + return vhost_vdpa_set_dev_vring_base(dev, ring); } static int vhost_vdpa_get_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { + struct vhost_vdpa *v = dev->opaque; int ret; + if (v->shadow_vqs_enabled) { + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, + ring->index); + + /* + * Setting base as last used idx, so destination will see as available + * all the entries that the device did not use, including the in-flight + * processing ones. + * + * TODO: This is ok for networking, but other kinds of devices might + * have problems with these retransmissions. + */ + ring->num = svq->last_used_idx; + return 0; + } + ret = vhost_vdpa_call(dev, VHOST_GET_VRING_BASE, ring); trace_vhost_vdpa_get_vring_base(dev, ring->index, ring->num); return ret; @@ -724,24 +1198,45 @@ static int vhost_vdpa_get_vring_base(struct vhost_dev *dev, static int vhost_vdpa_set_vring_kick(struct vhost_dev *dev, struct vhost_vring_file *file) { - trace_vhost_vdpa_set_vring_kick(dev, file->index, file->fd); - return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file); + struct vhost_vdpa *v = dev->opaque; + int vdpa_idx = file->index - dev->vq_index; + + if (v->shadow_vqs_enabled) { + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, vdpa_idx); + vhost_svq_set_svq_kick_fd(svq, file->fd); + return 0; + } else { + return vhost_vdpa_set_vring_dev_kick(dev, file); + } } static int vhost_vdpa_set_vring_call(struct vhost_dev *dev, struct vhost_vring_file *file) { - trace_vhost_vdpa_set_vring_call(dev, file->index, file->fd); - return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file); + struct vhost_vdpa *v = dev->opaque; + + if (v->shadow_vqs_enabled) { + int vdpa_idx = file->index - dev->vq_index; + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, vdpa_idx); + + vhost_svq_set_svq_call_fd(svq, file->fd); + return 0; + } else { + return vhost_vdpa_set_vring_dev_call(dev, file); + } } static int vhost_vdpa_get_features(struct vhost_dev *dev, uint64_t *features) { - int ret; + struct vhost_vdpa *v = dev->opaque; + int ret = vhost_vdpa_get_dev_features(dev, features); + + if (ret == 0 && v->shadow_vqs_enabled) { + /* Add SVQ logging capabilities */ + *features |= BIT_ULL(VHOST_F_LOG_ALL); + } - ret = vhost_vdpa_call(dev, VHOST_GET_FEATURES, features); - trace_vhost_vdpa_get_features(dev, *features); return ret; } diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index 3f3771274e..ed706681ac 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -153,19 +153,23 @@ static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc) if (elem->out_num) { error_report("invalid vhost-vsock event virtqueue element with " "out buffers"); - goto out; + goto err; } if (iov_from_buf(elem->in_sg, elem->in_num, 0, &event, sizeof(event)) != sizeof(event)) { error_report("vhost-vsock event virtqueue element is too short"); - goto out; + goto err; } virtqueue_push(vq, elem, sizeof(event)); virtio_notify(VIRTIO_DEVICE(vvc), vq); -out: + g_free(elem); + return; + +err: + virtqueue_detach_element(vq, elem, 0); g_free(elem); } diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index 433d42d897..714046210b 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -149,9 +149,8 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) return; } - ret = qemu_try_set_nonblock(vhostfd); - if (ret < 0) { - error_setg_errno(errp, -ret, + if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { + error_setg_errno(errp, errno, "vhost-vsock: unable to set non-blocking mode"); return; } @@ -163,7 +162,11 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) return; } - qemu_set_nonblock(vhostfd); + if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { + error_setg_errno(errp, errno, + "Failed to set FD nonblocking"); + return; + } } vhost_vsock_common_realize(vdev, "vhost-vsock"); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 7b03efccec..2bc72c27c5 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -26,7 +26,6 @@ #include "migration/blocker.h" #include "migration/qemu-file-types.h" #include "sysemu/dma.h" -#include "sysemu/tcg.h" #include "trace.h" /* enabled until disconnected backend stabilizes */ @@ -989,7 +988,7 @@ static inline bool vhost_needs_vring_endian(VirtIODevice *vdev) if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { return false; } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_LITTLE; #else return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; @@ -1287,7 +1286,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, return r; } - file.fd = event_notifier_get_fd(&vq->masked_notifier); + file.fd = event_notifier_get_wfd(&vq->masked_notifier); r = dev->vhost_ops->vhost_set_vring_call(dev, &file); if (r) { VHOST_OPS_DEBUG(r, "vhost_set_vring_call failed"); @@ -1542,9 +1541,9 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, if (mask) { assert(vdev->use_guest_notifier_mask); - file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier); + file.fd = event_notifier_get_wfd(&hdev->vqs[index].masked_notifier); } else { - file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq)); + file.fd = event_notifier_get_wfd(virtio_queue_get_guest_notifier(vvq)); } file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index e6c1b0aa46..8f1b38ef5c 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -242,7 +242,7 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { Error *err = NULL; - VirtIOBalloon *s = opaque; + VirtIOBalloon *s = VIRTIO_BALLOON(obj); int i; if (!visit_start_struct(v, name, NULL, 0, &err)) { @@ -277,7 +277,7 @@ static void balloon_stats_get_poll_interval(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - VirtIOBalloon *s = opaque; + VirtIOBalloon *s = VIRTIO_BALLOON(obj); visit_type_int(v, name, &s->stats_poll_interval, errp); } @@ -285,7 +285,7 @@ static void balloon_stats_set_poll_interval(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - VirtIOBalloon *s = opaque; + VirtIOBalloon *s = VIRTIO_BALLOON(obj); int64_t value; if (!visit_type_int(v, name, &value, errp)) { @@ -452,7 +452,6 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) VirtQueueElement *elem; VirtIOBalloonStat stat; size_t offset = 0; - qemu_timeval tv; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { @@ -484,13 +483,7 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) s->stats[tag] = val; } s->stats_vq_offset = offset; - - if (qemu_gettimeofday(&tv) < 0) { - warn_report("%s: failed to get time of day", __func__); - goto out; - } - - s->stats_last_update = tv.tv_sec; + s->stats_last_update = g_get_real_time() / G_USEC_PER_SEC; out: if (balloon_stats_enabled(s)) { @@ -1015,12 +1008,12 @@ static void virtio_balloon_instance_init(Object *obj) s->free_page_hint_notify.notify = virtio_balloon_free_page_hint_notify; object_property_add(obj, "guest-stats", "guest statistics", - balloon_stats_get_all, NULL, NULL, s); + balloon_stats_get_all, NULL, NULL, NULL); object_property_add(obj, "guest-stats-polling-interval", "int", balloon_stats_get_poll_interval, balloon_stats_set_poll_interval, - NULL, s); + NULL, NULL); } static const VMStateDescription vmstate_virtio_balloon = { diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d23db98c56..0f69d1c742 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -48,6 +48,7 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); + bool vdev_has_iommu; Error *local_err = NULL; DPRINTF("%s: plug device.\n", qbus->name); @@ -69,11 +70,6 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) return; } - if (has_iommu && !virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { - error_setg(errp, "iommu_platform=true is not supported by the device"); - return; - } - if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent, &local_err); } @@ -82,9 +78,15 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) return; } + vdev_has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); if (klass->get_dma_as != NULL && has_iommu) { virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM); vdev->dma_as = klass->get_dma_as(qbus->parent); + if (!vdev_has_iommu && vdev->dma_as != &address_space_memory) { + error_setg(errp, + "iommu_platform=true is not supported by the device"); + return; + } } else { vdev->dma_as = &address_space_memory; } diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 54f9bbb789..dcd80b904d 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -812,7 +812,7 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, "virtio-crypto", VIRTIO_ID_CRYPTO, vcrypto->config_size); vcrypto->curr_queues = 1; - vcrypto->vqs = g_malloc0(sizeof(VirtIOCryptoQueue) * vcrypto->max_queues); + vcrypto->vqs = g_new0(VirtIOCryptoQueue, vcrypto->max_queues); for (i = 0; i < vcrypto->max_queues; i++) { vcrypto->vqs[i].dataq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_dataq_bh); diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index aa9c16a17b..57c09d98a9 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -20,10 +20,10 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/iov.h" -#include "qemu-common.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio.h" #include "sysemu/kvm.h" +#include "sysemu/reset.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "trace.h" @@ -42,6 +42,7 @@ typedef struct VirtIOIOMMUDomain { uint32_t id; + bool bypass; GTree *mappings; QLIST_HEAD(, VirtIOIOMMUEndpoint) endpoint_list; } VirtIOIOMMUDomain; @@ -257,12 +258,16 @@ static void virtio_iommu_put_endpoint(gpointer data) } static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s, - uint32_t domain_id) + uint32_t domain_id, + bool bypass) { VirtIOIOMMUDomain *domain; domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id)); if (domain) { + if (domain->bypass != bypass) { + return NULL; + } return domain; } domain = g_malloc0(sizeof(*domain)); @@ -270,6 +275,7 @@ static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s, domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL, (GDestroyNotify)g_free, (GDestroyNotify)g_free); + domain->bypass = bypass; g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain); QLIST_INIT(&domain->endpoint_list); trace_virtio_iommu_get_domain(domain_id); @@ -309,7 +315,7 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, char *name = g_strdup_printf("%s-%d-%d", TYPE_VIRTIO_IOMMU_MEMORY_REGION, mr_index++, devfn); - sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice)); + sdev = sbus->pbdev[devfn] = g_new0(IOMMUDevice, 1); sdev->viommu = s; sdev->bus = bus; @@ -333,11 +339,16 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, { uint32_t domain_id = le32_to_cpu(req->domain); uint32_t ep_id = le32_to_cpu(req->endpoint); + uint32_t flags = le32_to_cpu(req->flags); VirtIOIOMMUDomain *domain; VirtIOIOMMUEndpoint *ep; trace_virtio_iommu_attach(domain_id, ep_id); + if (flags & ~VIRTIO_IOMMU_ATTACH_F_BYPASS) { + return VIRTIO_IOMMU_S_INVAL; + } + ep = virtio_iommu_get_endpoint(s, ep_id); if (!ep) { return VIRTIO_IOMMU_S_NOENT; @@ -355,7 +366,12 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, } } - domain = virtio_iommu_get_domain(s, domain_id); + domain = virtio_iommu_get_domain(s, domain_id, + flags & VIRTIO_IOMMU_ATTACH_F_BYPASS); + if (!domain) { + /* Incompatible bypass flag */ + return VIRTIO_IOMMU_S_INVAL; + } QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next); ep->domain = domain; @@ -418,6 +434,10 @@ static int virtio_iommu_map(VirtIOIOMMU *s, return VIRTIO_IOMMU_S_NOENT; } + if (domain->bypass) { + return VIRTIO_IOMMU_S_INVAL; + } + interval = g_malloc0(sizeof(*interval)); interval->low = virt_start; @@ -463,6 +483,11 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s, if (!domain) { return VIRTIO_IOMMU_S_NOENT; } + + if (domain->bypass) { + return VIRTIO_IOMMU_S_INVAL; + } + interval.low = virt_start; interval.high = virt_end; @@ -657,6 +682,7 @@ out: virtio_notify(vdev, vq); g_free(elem); g_free(buf); + buf = NULL; } } @@ -728,8 +754,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, .perm = IOMMU_NONE, }; - bypass_allowed = virtio_vdev_has_feature(&s->parent_obj, - VIRTIO_IOMMU_F_BYPASS); + bypass_allowed = s->config.bypass; sid = virtio_iommu_get_bdf(sdev); @@ -780,6 +805,9 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, entry.perm = flag; } goto unlock; + } else if (ep->domain->bypass) { + entry.perm = flag; + goto unlock; } found = g_tree_lookup_extended(ep->domain->mappings, (gpointer)(&interval), @@ -831,13 +859,37 @@ static void virtio_iommu_get_config(VirtIODevice *vdev, uint8_t *config_data) out_config->domain_range.start = cpu_to_le32(dev_config->domain_range.start); out_config->domain_range.end = cpu_to_le32(dev_config->domain_range.end); out_config->probe_size = cpu_to_le32(dev_config->probe_size); + out_config->bypass = dev_config->bypass; trace_virtio_iommu_get_config(dev_config->page_size_mask, dev_config->input_range.start, dev_config->input_range.end, dev_config->domain_range.start, dev_config->domain_range.end, - dev_config->probe_size); + dev_config->probe_size, + dev_config->bypass); +} + +static void virtio_iommu_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev); + struct virtio_iommu_config *dev_config = &dev->config; + const struct virtio_iommu_config *in_config = (void *)config_data; + + if (in_config->bypass != dev_config->bypass) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_IOMMU_F_BYPASS_CONFIG)) { + virtio_error(vdev, "cannot set config.bypass"); + return; + } else if (in_config->bypass != 0 && in_config->bypass != 1) { + virtio_error(vdev, "invalid config.bypass value '%u'", + in_config->bypass); + return; + } + dev_config->bypass = in_config->bypass; + } + + trace_virtio_iommu_set_config(in_config->bypass); } static uint64_t virtio_iommu_get_features(VirtIODevice *vdev, uint64_t f, @@ -963,6 +1015,19 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, return 0; } +static void virtio_iommu_system_reset(void *opaque) +{ + VirtIOIOMMU *s = opaque; + + trace_virtio_iommu_system_reset(); + + /* + * config.bypass is sticky across device reset, but should be restored on + * system reset + */ + s->config.bypass = s->boot_bypass; +} + static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -988,9 +1053,9 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_add_feature(&s->features, VIRTIO_IOMMU_F_INPUT_RANGE); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_DOMAIN_RANGE); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP); - virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_PROBE); + virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS_CONFIG); qemu_mutex_init(&s->mutex); @@ -1001,6 +1066,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) } else { error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!"); } + + qemu_register_reset(virtio_iommu_system_reset, s); } static void virtio_iommu_device_unrealize(DeviceState *dev) @@ -1008,6 +1075,8 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOIOMMU *s = VIRTIO_IOMMU(dev); + qemu_unregister_reset(virtio_iommu_system_reset, s); + g_hash_table_destroy(s->as_by_busptr); if (s->domains) { g_tree_destroy(s->domains); @@ -1098,8 +1167,8 @@ static const VMStateDescription vmstate_endpoint = { static const VMStateDescription vmstate_domain = { .name = "domain", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .pre_load = domain_preload, .fields = (VMStateField[]) { VMSTATE_UINT32(id, VirtIOIOMMUDomain), @@ -1108,6 +1177,7 @@ static const VMStateDescription vmstate_domain = { VirtIOIOMMUInterval, VirtIOIOMMUMapping), VMSTATE_QLIST_V(endpoint_list, VirtIOIOMMUDomain, 1, vmstate_endpoint, VirtIOIOMMUEndpoint, next), + VMSTATE_BOOL_V(bypass, VirtIOIOMMUDomain, 2), VMSTATE_END_OF_LIST() } }; @@ -1141,21 +1211,22 @@ static int iommu_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_virtio_iommu_device = { .name = "virtio-iommu-device", - .minimum_version_id = 1, - .version_id = 1, + .minimum_version_id = 2, + .version_id = 2, .post_load = iommu_post_load, .fields = (VMStateField[]) { - VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 1, + VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 2, &vmstate_domain, VirtIOIOMMUDomain), + VMSTATE_UINT8_V(config.bypass, VirtIOIOMMU, 2), VMSTATE_END_OF_LIST() }, }; static const VMStateDescription vmstate_virtio_iommu = { .name = "virtio-iommu", - .minimum_version_id = 1, + .minimum_version_id = 2, .priority = MIG_PRI_IOMMU, - .version_id = 1, + .version_id = 2, .fields = (VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() @@ -1164,6 +1235,7 @@ static const VMStateDescription vmstate_virtio_iommu = { static Property virtio_iommu_properties[] = { DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, "PCI", PCIBus *), + DEFINE_PROP_BOOL("boot-bypass", VirtIOIOMMU, boot_bypass, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1180,6 +1252,7 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data) vdc->unrealize = virtio_iommu_device_unrealize; vdc->reset = virtio_iommu_device_reset; vdc->get_config = virtio_iommu_get_config; + vdc->set_config = virtio_iommu_set_config; vdc->get_features = virtio_iommu_get_features; vdc->set_status = virtio_iommu_set_status; vdc->vmsd = &vmstate_virtio_iommu_device; diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index f55dcf61f2..5aca408726 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/iov.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -53,11 +52,11 @@ static uint32_t virtio_mem_default_thp_size(void) #if defined(__x86_64__) || defined(__arm__) || defined(__powerpc64__) default_thp_size = 2 * MiB; #elif defined(__aarch64__) - if (qemu_real_host_page_size == 4 * KiB) { + if (qemu_real_host_page_size() == 4 * KiB) { default_thp_size = 2 * MiB; - } else if (qemu_real_host_page_size == 16 * KiB) { + } else if (qemu_real_host_page_size() == 16 * KiB) { default_thp_size = 32 * MiB; - } else if (qemu_real_host_page_size == 64 * KiB) { + } else if (qemu_real_host_page_size() == 64 * KiB) { default_thp_size = 512 * MiB; } #endif @@ -120,7 +119,7 @@ static uint64_t virtio_mem_default_block_size(RAMBlock *rb) const uint64_t page_size = qemu_ram_pagesize(rb); /* We can have hugetlbfs with a page size smaller than the THP size. */ - if (page_size == qemu_real_host_page_size) { + if (page_size == qemu_real_host_page_size()) { return MAX(page_size, virtio_mem_thp_size()); } return MAX(page_size, VIRTIO_MEM_MIN_BLOCK_SIZE); @@ -135,7 +134,7 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb) * fresh page, consuming actual memory. */ return !qemu_ram_is_shared(rb) && rb->fd < 0 && - qemu_ram_pagesize(rb) == qemu_real_host_page_size; + qemu_ram_pagesize(rb) == qemu_real_host_page_size(); } #endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */ diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index f9cf9592fd..7cf1231c1c 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -683,10 +683,12 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, int ret; if (irqfd->users == 0) { - ret = kvm_irqchip_add_msi_route(kvm_state, vector, &proxy->pci_dev); + KVMRouteChange c = kvm_irqchip_begin_route_changes(kvm_state); + ret = kvm_irqchip_add_msi_route(&c, vector, &proxy->pci_dev); if (ret < 0) { return ret; } + kvm_irqchip_commit_route_changes(&c); irqfd->virq = ret; } irqfd->users++; diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index 5419dca75e..5dd21c2c44 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "hw/virtio/virtio-pmem.h" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 9e8f51dfb0..9d637e043e 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2380,8 +2380,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, vdev->vq[i].vring.num_default = queue_size; vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; vdev->vq[i].handle_output = handle_output; - vdev->vq[i].used_elems = g_malloc0(sizeof(VirtQueueElement) * - queue_size); + vdev->vq[i].used_elems = g_new0(VirtQueueElement, queue_size); return &vdev->vq[i]; } @@ -3228,7 +3227,7 @@ void virtio_init(VirtIODevice *vdev, const char *name, qatomic_set(&vdev->isr, 0); vdev->queue_sel = 0; vdev->config_vector = VIRTIO_NO_VECTOR; - vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX); + vdev->vq = g_new0(VirtQueue, VIRTIO_QUEUE_MAX); vdev->vm_running = runstate_is_running(); vdev->broken = false; for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { @@ -3580,7 +3579,7 @@ void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) vdev->bus_name = g_strdup(bus_name); } -void GCC_FMT_ATTR(2, 3) virtio_error(VirtIODevice *vdev, const char *fmt, ...) +void G_GNUC_PRINTF(2, 3) virtio_error(VirtIODevice *vdev, const char *fmt, ...) { va_list ap; diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 6aa6f90b66..31855afdf4 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -232,8 +232,8 @@ static void aspeed_wdt_reset(DeviceState *dev) AspeedWDTState *s = ASPEED_WDT(dev); AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(s); - s->regs[WDT_STATUS] = 0x3EF1480; - s->regs[WDT_RELOAD_VALUE] = 0x03EF1480; + s->regs[WDT_STATUS] = awc->default_status; + s->regs[WDT_RELOAD_VALUE] = awc->default_reload_value; s->regs[WDT_RESTART] = 0; s->regs[WDT_CTRL] = awc->sanitize_ctrl(0); s->regs[WDT_RESET_WIDTH] = 0xFF; @@ -319,6 +319,8 @@ static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data) awc->reset_ctrl_reg = SCU_RESET_CONTROL1; awc->wdt_reload = aspeed_wdt_reload; awc->sanitize_ctrl = aspeed_2400_sanitize_ctrl; + awc->default_status = 0x03EF1480; + awc->default_reload_value = 0x03EF1480; } static const TypeInfo aspeed_2400_wdt_info = { @@ -355,6 +357,8 @@ static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data) awc->reset_pulse = aspeed_2500_wdt_reset_pulse; awc->wdt_reload = aspeed_wdt_reload_1mhz; awc->sanitize_ctrl = aspeed_2500_sanitize_ctrl; + awc->default_status = 0x014FB180; + awc->default_reload_value = 0x014FB180; } static const TypeInfo aspeed_2500_wdt_info = { @@ -376,6 +380,8 @@ static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data) awc->reset_pulse = aspeed_2500_wdt_reset_pulse; awc->wdt_reload = aspeed_wdt_reload_1mhz; awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; + awc->default_status = 0x014FB180; + awc->default_reload_value = 0x014FB180; } static const TypeInfo aspeed_2600_wdt_info = { @@ -385,6 +391,29 @@ static const TypeInfo aspeed_2600_wdt_info = { .class_init = aspeed_2600_wdt_class_init, }; +static void aspeed_1030_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); + + dc->desc = "ASPEED 1030 Watchdog Controller"; + awc->offset = 0x80; + awc->ext_pulse_width_mask = 0xfffff; /* TODO */ + awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; + awc->reset_pulse = aspeed_2500_wdt_reset_pulse; + awc->wdt_reload = aspeed_wdt_reload_1mhz; + awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; + awc->default_status = 0x014FB180; + awc->default_reload_value = 0x014FB180; +} + +static const TypeInfo aspeed_1030_wdt_info = { + .name = TYPE_ASPEED_1030_WDT, + .parent = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_1030_wdt_class_init, +}; + static void wdt_aspeed_register_types(void) { watchdog_add_model(&model); @@ -392,6 +421,7 @@ static void wdt_aspeed_register_types(void) type_register_static(&aspeed_2400_wdt_info); type_register_static(&aspeed_2500_wdt_info); type_register_static(&aspeed_2600_wdt_info); + type_register_static(&aspeed_1030_wdt_info); } type_init(wdt_aspeed_register_types) diff --git a/hw/xen/meson.build b/hw/xen/meson.build index 076954b89c..08dc1f6857 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -8,13 +8,17 @@ softmmu_ss.add(when: ['CONFIG_XEN', xen], if_true: files( )) xen_specific_ss = ss.source_set() -xen_specific_ss.add(when: 'CONFIG_XEN_PCI_PASSTHROUGH', if_true: files( - 'xen-host-pci-device.c', - 'xen_pt.c', - 'xen_pt_config_init.c', - 'xen_pt_graphics.c', - 'xen_pt_load_rom.c', - 'xen_pt_msi.c', -), if_false: files('xen_pt_stub.c')) +if have_xen_pci_passthrough + xen_specific_ss.add(files( + 'xen-host-pci-device.c', + 'xen_pt.c', + 'xen_pt_config_init.c', + 'xen_pt_graphics.c', + 'xen_pt_load_rom.c', + 'xen_pt_msi.c', + )) +else + xen_specific_ss.add('xen_pt_stub.c') +endif specific_ss.add_all(when: ['CONFIG_XEN', xen], if_true: xen_specific_ss) diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index c74c4678f2..6b8e13cdee 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -9,7 +9,7 @@ bool xen_igd_gfx_pt_enabled(void); void xen_igd_gfx_pt_set(bool value, Error **errp); -void xen_pt_log(const PCIDevice *d, const char *f, ...) GCC_FMT_ATTR(2, 3); +void xen_pt_log(const PCIDevice *d, const char *f, ...) G_GNUC_PRINTF(2, 3); #define XEN_PT_ERR(d, _f, _a...) xen_pt_log(d, "%s: Error: "_f, __func__, ##_a) diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index 8ab458922a..037152f063 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -196,37 +196,40 @@ const char *xenbus_strstate(enum xenbus_state state) * 2 == noisy debug messages (logfile only). * 3 == will flood your log (logfile only). */ +static void xen_pv_output_msg(struct XenLegacyDevice *xendev, + FILE *f, const char *fmt, va_list args) +{ + if (xendev) { + fprintf(f, "xen be: %s: ", xendev->name); + } else { + fprintf(f, "xen be core: "); + } + vfprintf(f, fmt, args); +} + void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level, const char *fmt, ...) { + FILE *logfile; va_list args; - if (xendev) { - if (msg_level > xendev->debug) { - return; - } - qemu_log("xen be: %s: ", xendev->name); - if (msg_level == 0) { - fprintf(stderr, "xen be: %s: ", xendev->name); - } - } else { - if (msg_level > debug) { - return; - } - qemu_log("xen be core: "); - if (msg_level == 0) { - fprintf(stderr, "xen be core: "); - } + if (msg_level > (xendev ? xendev->debug : debug)) { + return; } - va_start(args, fmt); - qemu_log_vprintf(fmt, args); - va_end(args); + + logfile = qemu_log_trylock(); + if (logfile) { + va_start(args, fmt); + xen_pv_output_msg(xendev, logfile, fmt, args); + va_end(args); + qemu_log_unlock(logfile); + } + if (msg_level == 0) { va_start(args, fmt); - vfprintf(stderr, fmt, args); + xen_pv_output_msg(xendev, stderr, fmt, args); va_end(args); } - qemu_log_flush(); } void xen_pv_evtchn_event(void *opaque) diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 2028fe793d..946c71cb5b 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -96,7 +96,7 @@ XtensaCPU *xtensa_sim_common_init(MachineState *machine) void xtensa_sim_load_kernel(XtensaCPU *cpu, MachineState *machine) { const char *kernel_filename = machine->kernel_filename; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN int big_endian = true; #else int big_endian = false; diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 17f087b395..2a5556a35f 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -126,7 +126,7 @@ static const MemoryRegionOps xtfpga_fpga_ops = { static XtfpgaFpgaState *xtfpga_fpga_init(MemoryRegion *address_space, hwaddr base, uint32_t freq) { - XtfpgaFpgaState *s = g_malloc(sizeof(XtfpgaFpgaState)); + XtfpgaFpgaState *s = g_new(XtfpgaFpgaState, 1); memory_region_init_io(&s->iomem, NULL, &xtfpga_fpga_ops, s, "xtfpga.fpga", 0x10000); @@ -219,7 +219,7 @@ static const MemoryRegionOps xtfpga_io_ops = { static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN int be = 1; #else int be = 0; @@ -430,7 +430,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) } if (entry_point != env->pc) { uint8_t boot[] = { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN 0x60, 0x00, 0x08, /* j 1f */ 0x00, /* .literal_position */ 0x00, 0x00, 0x00, 0x00, /* .literal entry_pc */ diff --git a/include/block/block-common.h b/include/block/block-common.h new file mode 100644 index 0000000000..fdb7306e78 --- /dev/null +++ b/include/block/block-common.h @@ -0,0 +1,419 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BLOCK_COMMON_H +#define BLOCK_COMMON_H + +#include "block/aio.h" +#include "block/aio-wait.h" +#include "qemu/iov.h" +#include "qemu/coroutine.h" +#include "block/accounting.h" +#include "block/dirty-bitmap.h" +#include "block/blockjob.h" +#include "qemu/hbitmap.h" +#include "qemu/transactions.h" + +/* + * generated_co_wrapper + * + * Function specifier, which does nothing but mark functions to be + * generated by scripts/block-coroutine-wrapper.py + * + * Read more in docs/devel/block-coroutine-wrapper.rst + */ +#define generated_co_wrapper + +/* block.c */ +typedef struct BlockDriver BlockDriver; +typedef struct BdrvChild BdrvChild; +typedef struct BdrvChildClass BdrvChildClass; + +typedef struct BlockDriverInfo { + /* in bytes, 0 if irrelevant */ + int cluster_size; + /* offset at which the VM state can be saved (0 if not possible) */ + int64_t vm_state_offset; + bool is_dirty; + /* + * True if this block driver only supports compressed writes + */ + bool needs_compressed_writes; +} BlockDriverInfo; + +typedef struct BlockFragInfo { + uint64_t allocated_clusters; + uint64_t total_clusters; + uint64_t fragmented_clusters; + uint64_t compressed_clusters; +} BlockFragInfo; + +typedef enum { + BDRV_REQ_COPY_ON_READ = 0x1, + BDRV_REQ_ZERO_WRITE = 0x2, + + /* + * The BDRV_REQ_MAY_UNMAP flag is used in write_zeroes requests to indicate + * that the block driver should unmap (discard) blocks if it is guaranteed + * that the result will read back as zeroes. The flag is only passed to the + * driver if the block device is opened with BDRV_O_UNMAP. + */ + BDRV_REQ_MAY_UNMAP = 0x4, + + BDRV_REQ_FUA = 0x10, + BDRV_REQ_WRITE_COMPRESSED = 0x20, + + /* + * Signifies that this write request will not change the visible disk + * content. + */ + BDRV_REQ_WRITE_UNCHANGED = 0x40, + + /* + * Forces request serialisation. Use only with write requests. + */ + BDRV_REQ_SERIALISING = 0x80, + + /* + * Execute the request only if the operation can be offloaded or otherwise + * be executed efficiently, but return an error instead of using a slow + * fallback. + */ + BDRV_REQ_NO_FALLBACK = 0x100, + + /* + * BDRV_REQ_PREFETCH makes sense only in the context of copy-on-read + * (i.e., together with the BDRV_REQ_COPY_ON_READ flag or when a COR + * filter is involved), in which case it signals that the COR operation + * need not read the data into memory (qiov) but only ensure they are + * copied to the top layer (i.e., that COR operation is done). + */ + BDRV_REQ_PREFETCH = 0x200, + + /* + * If we need to wait for other requests, just fail immediately. Used + * only together with BDRV_REQ_SERIALISING. Used only with requests aligned + * to request_alignment (corresponding assertions are in block/io.c). + */ + BDRV_REQ_NO_WAIT = 0x400, + + /* Mask of valid flags */ + BDRV_REQ_MASK = 0x7ff, +} BdrvRequestFlags; + +#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */ +#define BDRV_O_RDWR 0x0002 +#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */ +#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save + writes in a snapshot */ +#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ +#define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */ +#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the + thread pool */ +#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */ +#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ +#define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */ +#define BDRV_O_INACTIVE 0x0800 /* consistency hint for migration handoff */ +#define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ +#define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */ +#define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */ +#define BDRV_O_PROTOCOL 0x8000 /* if no block driver is explicitly given: + select an appropriate protocol driver, + ignoring the format layer */ +#define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */ +#define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening + read-write fails */ +#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */ + +#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) + + +/* Option names of options parsed by the block layer */ + +#define BDRV_OPT_CACHE_WB "cache.writeback" +#define BDRV_OPT_CACHE_DIRECT "cache.direct" +#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush" +#define BDRV_OPT_READ_ONLY "read-only" +#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only" +#define BDRV_OPT_DISCARD "discard" +#define BDRV_OPT_FORCE_SHARE "force-share" + + +#define BDRV_SECTOR_BITS 9 +#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) + +#define BDRV_REQUEST_MAX_SECTORS MIN_CONST(SIZE_MAX >> BDRV_SECTOR_BITS, \ + INT_MAX >> BDRV_SECTOR_BITS) +#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) + +/* + * We want allow aligning requests and disk length up to any 32bit alignment + * and don't afraid of overflow. + * To achieve it, and in the same time use some pretty number as maximum disk + * size, let's define maximum "length" (a limit for any offset/bytes request and + * for disk size) to be the greatest power of 2 less than INT64_MAX. + */ +#define BDRV_MAX_ALIGNMENT (1L << 30) +#define BDRV_MAX_LENGTH (QEMU_ALIGN_DOWN(INT64_MAX, BDRV_MAX_ALIGNMENT)) + +/* + * Allocation status flags for bdrv_block_status() and friends. + * + * Public flags: + * BDRV_BLOCK_DATA: allocation for data at offset is tied to this layer + * BDRV_BLOCK_ZERO: offset reads as zero + * BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data + * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this + * layer rather than any backing, set by block layer + * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this + * layer, set by block layer + * + * Internal flags: + * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request + * that the block layer recompute the answer from the returned + * BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID. + * BDRV_BLOCK_RECURSE: request that the block layer will recursively search for + * zeroes in file child of current block node inside + * returned region. Only valid together with both + * BDRV_BLOCK_DATA and BDRV_BLOCK_OFFSET_VALID. Should not + * appear with BDRV_BLOCK_ZERO. + * + * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the + * host offset within the returned BDS that is allocated for the + * corresponding raw guest data. However, whether that offset + * actually contains data also depends on BDRV_BLOCK_DATA, as follows: + * + * DATA ZERO OFFSET_VALID + * t t t sectors read as zero, returned file is zero at offset + * t f t sectors read as valid from file at offset + * f t t sectors preallocated, read as zero, returned file not + * necessarily zero at offset + * f f t sectors preallocated but read from backing_hd, + * returned file contains garbage at offset + * t t f sectors preallocated, read as zero, unknown offset + * t f f sectors read from unknown file or offset + * f t f not allocated or unknown offset, read as zero + * f f f not allocated or unknown offset, read from backing_hd + */ +#define BDRV_BLOCK_DATA 0x01 +#define BDRV_BLOCK_ZERO 0x02 +#define BDRV_BLOCK_OFFSET_VALID 0x04 +#define BDRV_BLOCK_RAW 0x08 +#define BDRV_BLOCK_ALLOCATED 0x10 +#define BDRV_BLOCK_EOF 0x20 +#define BDRV_BLOCK_RECURSE 0x40 + +typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; + +typedef struct BDRVReopenState { + BlockDriverState *bs; + int flags; + BlockdevDetectZeroesOptions detect_zeroes; + bool backing_missing; + BlockDriverState *old_backing_bs; /* keep pointer for permissions update */ + BlockDriverState *old_file_bs; /* keep pointer for permissions update */ + QDict *options; + QDict *explicit_options; + void *opaque; +} BDRVReopenState; + +/* + * Block operation types + */ +typedef enum BlockOpType { + BLOCK_OP_TYPE_BACKUP_SOURCE, + BLOCK_OP_TYPE_BACKUP_TARGET, + BLOCK_OP_TYPE_CHANGE, + BLOCK_OP_TYPE_COMMIT_SOURCE, + BLOCK_OP_TYPE_COMMIT_TARGET, + BLOCK_OP_TYPE_DATAPLANE, + BLOCK_OP_TYPE_DRIVE_DEL, + BLOCK_OP_TYPE_EJECT, + BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, + BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, + BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, + BLOCK_OP_TYPE_MIRROR_SOURCE, + BLOCK_OP_TYPE_MIRROR_TARGET, + BLOCK_OP_TYPE_RESIZE, + BLOCK_OP_TYPE_STREAM, + BLOCK_OP_TYPE_REPLACE, + BLOCK_OP_TYPE_MAX, +} BlockOpType; + +/* Block node permission constants */ +enum { + /** + * A user that has the "permission" of consistent reads is guaranteed that + * their view of the contents of the block device is complete and + * self-consistent, representing the contents of a disk at a specific + * point. + * + * For most block devices (including their backing files) this is true, but + * the property cannot be maintained in a few situations like for + * intermediate nodes of a commit block job. + */ + BLK_PERM_CONSISTENT_READ = 0x01, + + /** This permission is required to change the visible disk contents. */ + BLK_PERM_WRITE = 0x02, + + /** + * This permission (which is weaker than BLK_PERM_WRITE) is both enough and + * required for writes to the block node when the caller promises that + * the visible disk content doesn't change. + * + * As the BLK_PERM_WRITE permission is strictly stronger, either is + * sufficient to perform an unchanging write. + */ + BLK_PERM_WRITE_UNCHANGED = 0x04, + + /** This permission is required to change the size of a block node. */ + BLK_PERM_RESIZE = 0x08, + + /** + * There was a now-removed bit BLK_PERM_GRAPH_MOD, with value of 0x10. QEMU + * 6.1 and earlier may still lock the corresponding byte in block/file-posix + * locking. So, implementing some new permission should be very careful to + * not interfere with this old unused thing. + */ + + BLK_PERM_ALL = 0x0f, + + DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ + | BLK_PERM_WRITE + | BLK_PERM_WRITE_UNCHANGED + | BLK_PERM_RESIZE, + + DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH, +}; + +/* + * Flags that parent nodes assign to child nodes to specify what kind of + * role(s) they take. + * + * At least one of DATA, METADATA, FILTERED, or COW must be set for + * every child. + */ +enum BdrvChildRoleBits { + /* + * This child stores data. + * Any node may have an arbitrary number of such children. + */ + BDRV_CHILD_DATA = (1 << 0), + + /* + * This child stores metadata. + * Any node may have an arbitrary number of metadata-storing + * children. + */ + BDRV_CHILD_METADATA = (1 << 1), + + /* + * A child that always presents exactly the same visible data as + * the parent, e.g. by virtue of the parent forwarding all reads + * and writes. + * This flag is mutually exclusive with DATA, METADATA, and COW. + * Any node may have at most one filtered child at a time. + */ + BDRV_CHILD_FILTERED = (1 << 2), + + /* + * Child from which to read all data that isn't allocated in the + * parent (i.e., the backing child); such data is copied to the + * parent through COW (and optionally COR). + * This field is mutually exclusive with DATA, METADATA, and + * FILTERED. + * Any node may have at most one such backing child at a time. + */ + BDRV_CHILD_COW = (1 << 3), + + /* + * The primary child. For most drivers, this is the child whose + * filename applies best to the parent node. + * Any node may have at most one primary child at a time. + */ + BDRV_CHILD_PRIMARY = (1 << 4), + + /* Useful combination of flags */ + BDRV_CHILD_IMAGE = BDRV_CHILD_DATA + | BDRV_CHILD_METADATA + | BDRV_CHILD_PRIMARY, +}; + +/* Mask of BdrvChildRoleBits values */ +typedef unsigned int BdrvChildRole; + +typedef struct BdrvCheckResult { + int corruptions; + int leaks; + int check_errors; + int corruptions_fixed; + int leaks_fixed; + int64_t image_end_offset; + BlockFragInfo bfi; +} BdrvCheckResult; + +typedef enum { + BDRV_FIX_LEAKS = 1, + BDRV_FIX_ERRORS = 2, +} BdrvCheckMode; + +typedef struct BlockSizes { + uint32_t phys; + uint32_t log; +} BlockSizes; + +typedef struct HDGeometry { + uint32_t heads; + uint32_t sectors; + uint32_t cylinders; +} HDGeometry; + +/* + * Common functions that are neither I/O nor Global State. + * + * These functions must never call any function from other categories + * (I/O, "I/O or GS", Global State) except this one, but can be invoked by + * all of them. + */ + +char *bdrv_perm_names(uint64_t perm); +uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); + +void bdrv_init_with_whitelist(void); +bool bdrv_uses_whitelist(void); +int bdrv_is_whitelisted(BlockDriver *drv, bool read_only); + +int bdrv_parse_aio(const char *mode, int *flags); +int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough); +int bdrv_parse_discard_flags(const char *mode, int *flags); + +int path_has_protocol(const char *path); +int path_is_absolute(const char *path); +char *path_combine(const char *base_path, const char *filename); + +char *bdrv_get_full_backing_filename_from_filename(const char *backed, + const char *backing, + Error **errp); + +#endif /* BLOCK_COMMON_H */ diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 99370fa38b..68bbd344b2 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -25,6 +25,7 @@ typedef struct BlockCopyState BlockCopyState; typedef struct BlockCopyCallState BlockCopyCallState; BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + const BdrvDirtyBitmap *bitmap, Error **errp); /* Function should be called prior any actual copy request */ @@ -34,6 +35,7 @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm); void block_copy_state_free(BlockCopyState *s); +void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes); int64_t block_copy_reset_unallocated(BlockCopyState *s, int64_t offset, int64_t *count); diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h new file mode 100644 index 0000000000..25bb69bbef --- /dev/null +++ b/include/block/block-global-state.h @@ -0,0 +1,253 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BLOCK_GLOBAL_STATE_H +#define BLOCK_GLOBAL_STATE_H + +#include "block-common.h" + +/* + * Global state (GS) API. These functions run under the BQL. + * + * If a function modifies the graph, it also uses drain and/or + * aio_context_acquire/release to be sure it has unique access. + * aio_context locking is needed together with BQL because of + * the thread-safe I/O API that concurrently runs and accesses + * the graph without the BQL. + * + * It is important to note that not all of these functions are + * necessarily limited to running under the BQL, but they would + * require additional auditing and many small thread-safety changes + * to move them into the I/O API. Often it's not worth doing that + * work since the APIs are only used with the BQL held at the + * moment, so they have been placed in the GS API (for now). + * + * These functions can call any function from this and other categories + * (I/O, "I/O or GS", Common), but must be invoked only by other GS APIs. + * + * All functions in this header must use the macro + * GLOBAL_STATE_CODE(); + * to catch when they are accidentally called without the BQL. + */ + +void bdrv_init(void); +BlockDriver *bdrv_find_protocol(const char *filename, + bool allow_protocol_prefix, + Error **errp); +BlockDriver *bdrv_find_format(const char *format_name); +int bdrv_create(BlockDriver *drv, const char* filename, + QemuOpts *opts, Error **errp); +int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); + +BlockDriverState *bdrv_new(void); +int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, + Error **errp); +int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + Error **errp); +int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, + Error **errp); +BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, + int flags, Error **errp); +int bdrv_drop_filter(BlockDriverState *bs, Error **errp); + +BdrvChild *bdrv_open_child(const char *filename, + QDict *options, const char *bdref_key, + BlockDriverState *parent, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + bool allow_none, Error **errp); +BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); +int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, + Error **errp); +int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, + const char *bdref_key, Error **errp); +BlockDriverState *bdrv_open(const char *filename, const char *reference, + QDict *options, int flags, Error **errp); +BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, + const char *node_name, + QDict *options, int flags, + Error **errp); +BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, + int flags, Error **errp); +BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, + BlockDriverState *bs, QDict *options, + bool keep_old_opts); +void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue); +int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); +int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, + Error **errp); +int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, + Error **errp); +BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, + const char *backing_file); +void bdrv_refresh_filename(BlockDriverState *bs); +void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); +int bdrv_commit(BlockDriverState *bs); +int bdrv_make_empty(BdrvChild *c, Error **errp); +int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt, bool warn); +void bdrv_register(BlockDriver *bdrv); +int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, + const char *backing_file_str); +BlockDriverState *bdrv_find_overlay(BlockDriverState *active, + BlockDriverState *bs); +BlockDriverState *bdrv_find_base(BlockDriverState *bs); +bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, + Error **errp); +int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, + Error **errp); +void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); + +/* + * The units of offset and total_work_size may be chosen arbitrarily by the + * block driver; total_work_size may change during the course of the amendment + * operation + */ +typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, + int64_t total_work_size, void *opaque); +int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, + Error **errp); + +/* check if a named node can be replaced when doing drive-mirror */ +BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, + const char *node_name, Error **errp); + +int bdrv_activate(BlockDriverState *bs, Error **errp); +void bdrv_activate_all(Error **errp); +int bdrv_inactivate_all(void); + +int bdrv_flush_all(void); +void bdrv_close_all(void); +void bdrv_drain_all_begin(void); +void bdrv_drain_all_end(void); +void bdrv_drain_all(void); + +int bdrv_has_zero_init_1(BlockDriverState *bs); +int bdrv_has_zero_init(BlockDriverState *bs); +BlockDriverState *bdrv_find_node(const char *node_name); +BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); +XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); +BlockDriverState *bdrv_lookup_bs(const char *device, + const char *node_name, + Error **errp); +bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); +BlockDriverState *bdrv_next_node(BlockDriverState *bs); +BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); + +typedef struct BdrvNextIterator { + enum { + BDRV_NEXT_BACKEND_ROOTS, + BDRV_NEXT_MONITOR_OWNED, + } phase; + BlockBackend *blk; + BlockDriverState *bs; +} BdrvNextIterator; + +BlockDriverState *bdrv_first(BdrvNextIterator *it); +BlockDriverState *bdrv_next(BdrvNextIterator *it); +void bdrv_next_cleanup(BdrvNextIterator *it); + +BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); +void bdrv_iterate_format(void (*it)(void *opaque, const char *name), + void *opaque, bool read_only); +int bdrv_get_flags(BlockDriverState *bs); +char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); +char *bdrv_dirname(BlockDriverState *bs, Error **errp); + +void bdrv_img_create(const char *filename, const char *fmt, + const char *base_filename, const char *base_fmt, + char *options, uint64_t img_size, int flags, + bool quiet, Error **errp); + +void bdrv_ref(BlockDriverState *bs); +void bdrv_unref(BlockDriverState *bs); +void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); +BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + const char *child_name, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + Error **errp); + +bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); +void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason); +void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason); +void bdrv_op_block_all(BlockDriverState *bs, Error *reason); +void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason); +bool bdrv_op_blocker_is_empty(BlockDriverState *bs); + +int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, + const char *tag); +int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag); +int bdrv_debug_resume(BlockDriverState *bs, const char *tag); +bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag); + +/** + * Locks the AioContext of @bs if it's not the current AioContext. This avoids + * double locking which could lead to deadlocks: This is a coroutine_fn, so we + * know we already own the lock of the current AioContext. + * + * May only be called in the main thread. + */ +void coroutine_fn bdrv_co_lock(BlockDriverState *bs); + +/** + * Unlocks the AioContext of @bs if it's not the current AioContext. + */ +void coroutine_fn bdrv_co_unlock(BlockDriverState *bs); + +void bdrv_set_aio_context_ignore(BlockDriverState *bs, + AioContext *new_context, GSList **ignore); +int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, + Error **errp); +int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp); +bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, + GSList **ignore, Error **errp); +bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, + GSList **ignore, Error **errp); +AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); + +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); + +void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, + Error **errp); +void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); + +/** + * + * bdrv_register_buf/bdrv_unregister_buf: + * + * Register/unregister a buffer for I/O. For example, VFIO drivers are + * interested to know the memory areas that would later be used for I/O, so + * that they can prepare IOMMU mapping etc., to get better performance. + */ +void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size); +void bdrv_unregister_buf(BlockDriverState *bs, void *host); + +void bdrv_cancel_in_flight(BlockDriverState *bs); + +#endif /* BLOCK_GLOBAL_STATE_H */ diff --git a/include/block/block-io.h b/include/block/block-io.h new file mode 100644 index 0000000000..5e3f346806 --- /dev/null +++ b/include/block/block-io.h @@ -0,0 +1,368 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BLOCK_IO_H +#define BLOCK_IO_H + +#include "block-common.h" + +/* + * I/O API functions. These functions are thread-safe, and therefore + * can run in any thread as long as the thread has called + * aio_context_acquire/release(). + * + * These functions can only call functions from I/O and Common categories, + * but can be invoked by GS, "I/O or GS" and I/O APIs. + * + * All functions in this category must use the macro + * IO_CODE(); + * to catch when they are accidentally called by the wrong API. + */ + +int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, + int64_t bytes, BdrvRequestFlags flags); +int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); +int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes); +int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, + int64_t bytes); +int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, + const void *buf, int64_t bytes); +/* + * Efficiently zero a region of the disk image. Note that this is a regular + * I/O request like read or write and should have a reasonable size. This + * function is not suitable for zeroing the entire image in a single request + * because it may allocate memory for the entire region. + */ +int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, + int64_t bytes, BdrvRequestFlags flags); + +int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp); + +int64_t bdrv_nb_sectors(BlockDriverState *bs); +int64_t bdrv_getlength(BlockDriverState *bs); +int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); +BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, + BlockDriverState *in_bs, Error **errp); +void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); +int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp); +void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs); + + +/* async block I/O */ +void bdrv_aio_cancel(BlockAIOCB *acb); +void bdrv_aio_cancel_async(BlockAIOCB *acb); + +/* sg packet commands */ +int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); + +/* Ensure contents are flushed to disk. */ +int coroutine_fn bdrv_co_flush(BlockDriverState *bs); + +int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); +bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); +int bdrv_block_status(BlockDriverState *bs, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file); +int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file); +int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, + int64_t *pnum); +int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, + bool include_base, int64_t offset, int64_t bytes, + int64_t *pnum); +int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, + int64_t bytes); + +int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, + bool ignore_allow_rdw, Error **errp); +int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, + Error **errp); +bool bdrv_is_read_only(BlockDriverState *bs); +bool bdrv_is_writable(BlockDriverState *bs); +bool bdrv_is_sg(BlockDriverState *bs); +bool bdrv_is_inserted(BlockDriverState *bs); +void bdrv_lock_medium(BlockDriverState *bs, bool locked); +void bdrv_eject(BlockDriverState *bs, bool eject_flag); +const char *bdrv_get_format_name(BlockDriverState *bs); + +bool bdrv_supports_compressed_writes(BlockDriverState *bs); +const char *bdrv_get_node_name(const BlockDriverState *bs); +const char *bdrv_get_device_name(const BlockDriverState *bs); +const char *bdrv_get_device_or_node_name(const BlockDriverState *bs); +int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); +ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, + Error **errp); +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); +void bdrv_round_to_clusters(BlockDriverState *bs, + int64_t offset, int64_t bytes, + int64_t *cluster_offset, + int64_t *cluster_bytes); + +void bdrv_get_backing_filename(BlockDriverState *bs, + char *filename, int filename_size); + +int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, + int64_t pos, int size); + +int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, + int64_t pos, int size); + +/* + * Returns the alignment in bytes that is required so that no bounce buffer + * is required throughout the stack + */ +size_t bdrv_min_mem_align(BlockDriverState *bs); +/* Returns optimal alignment in bytes for bounce buffer */ +size_t bdrv_opt_mem_align(BlockDriverState *bs); +void *qemu_blockalign(BlockDriverState *bs, size_t size); +void *qemu_blockalign0(BlockDriverState *bs, size_t size); +void *qemu_try_blockalign(BlockDriverState *bs, size_t size); +void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); +bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov); + +void bdrv_enable_copy_on_read(BlockDriverState *bs); +void bdrv_disable_copy_on_read(BlockDriverState *bs); + +void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); + +#define BLKDBG_EVENT(child, evt) \ + do { \ + if (child) { \ + bdrv_debug_event(child->bs, evt); \ + } \ + } while (0) + +/** + * bdrv_get_aio_context: + * + * Returns: the currently bound #AioContext + */ +AioContext *bdrv_get_aio_context(BlockDriverState *bs); + +/** + * Move the current coroutine to the AioContext of @bs and return the old + * AioContext of the coroutine. Increase bs->in_flight so that draining @bs + * will wait for the operation to proceed until the corresponding + * bdrv_co_leave(). + * + * Consequently, you can't call drain inside a bdrv_co_enter/leave() section as + * this will deadlock. + */ +AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs); + +/** + * Ends a section started by bdrv_co_enter(). Move the current coroutine back + * to old_ctx and decrease bs->in_flight again. + */ +void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx); + +/** + * Transfer control to @co in the aio context of @bs + */ +void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co); + +AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c); + +void bdrv_io_plug(BlockDriverState *bs); +void bdrv_io_unplug(BlockDriverState *bs); + +bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, + uint32_t granularity, Error **errp); + +/** + * + * bdrv_co_copy_range: + * + * Do offloaded copy between two children. If the operation is not implemented + * by the driver, or if the backend storage doesn't support it, a negative + * error code will be returned. + * + * Note: block layer doesn't emulate or fallback to a bounce buffer approach + * because usually the caller shouldn't attempt offloaded copy any more (e.g. + * calling copy_file_range(2)) after the first error, thus it should fall back + * to a read+write path in the caller level. + * + * @src: Source child to copy data from + * @src_offset: offset in @src image to read data + * @dst: Destination child to copy data to + * @dst_offset: offset in @dst image to write data + * @bytes: number of bytes to copy + * @flags: request flags. Supported flags: + * BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero + * write on @dst as if bdrv_co_pwrite_zeroes is + * called. Used to simplify caller code, or + * during BlockDriver.bdrv_co_copy_range_from() + * recursion. + * BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping + * requests currently in flight. + * + * Returns: 0 if succeeded; negative error code if failed. + **/ +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + +/** + * bdrv_drained_end_no_poll: + * + * Same as bdrv_drained_end(), but do not poll for the subgraph to + * actually become unquiesced. Therefore, no graph changes will occur + * with this function. + * + * *drained_end_counter is incremented for every background operation + * that is scheduled, and will be decremented for every operation once + * it settles. The caller must poll until it reaches 0. The counter + * should be accessed using atomic operations only. + */ +void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); + + +/* + * "I/O or GS" API functions. These functions can run without + * the BQL, but only in one specific iothread/main loop. + * + * More specifically, these functions use BDRV_POLL_WHILE(bs), which + * requires the caller to be either in the main thread and hold + * the BlockdriverState (bs) AioContext lock, or directly in the + * home thread that runs the bs AioContext. Calling them from + * another thread in another AioContext would cause deadlocks. + * + * Therefore, these functions are not proper I/O, because they + * can't run in *any* iothreads, but only in a specific one. + * + * These functions can call any function from I/O, Common and this + * categories, but must be invoked only by other "I/O or GS" and GS APIs. + * + * All functions in this category must use the macro + * IO_OR_GS_CODE(); + * to catch when they are accidentally called by the wrong API. + */ + +#define BDRV_POLL_WHILE(bs, cond) ({ \ + BlockDriverState *bs_ = (bs); \ + IO_OR_GS_CODE(); \ + AIO_WAIT_WHILE(bdrv_get_aio_context(bs_), \ + cond); }) + +void bdrv_drain(BlockDriverState *bs); +void coroutine_fn bdrv_co_drain(BlockDriverState *bs); + +int generated_co_wrapper +bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); + +int generated_co_wrapper bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix); + +/* Invalidate any cached metadata used by image formats */ +int generated_co_wrapper bdrv_invalidate_cache(BlockDriverState *bs, + Error **errp); +int generated_co_wrapper bdrv_flush(BlockDriverState *bs); +int generated_co_wrapper bdrv_pdiscard(BdrvChild *child, int64_t offset, + int64_t bytes); +int generated_co_wrapper +bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); +int generated_co_wrapper +bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); + +/** + * bdrv_parent_drained_begin_single: + * + * Begin a quiesced section for the parent of @c. If @poll is true, wait for + * any pending activity to cease. + */ +void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll); + +/** + * bdrv_parent_drained_end_single: + * + * End a quiesced section for the parent of @c. + * + * This polls @bs's AioContext until all scheduled sub-drained_ends + * have settled, which may result in graph changes. + */ +void bdrv_parent_drained_end_single(BdrvChild *c); + +/** + * bdrv_drain_poll: + * + * Poll for pending requests in @bs, its parents (except for @ignore_parent), + * and if @recursive is true its children as well (used for subtree drain). + * + * If @ignore_bds_parents is true, parents that are BlockDriverStates must + * ignore the drain request because they will be drained separately (used for + * drain_all). + * + * This is part of bdrv_drained_begin. + */ +bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent, bool ignore_bds_parents); + +/** + * bdrv_drained_begin: + * + * Begin a quiesced section for exclusive access to the BDS, by disabling + * external request sources including NBD server, block jobs, and device model. + * + * This function can be recursive. + */ +void bdrv_drained_begin(BlockDriverState *bs); + +/** + * bdrv_do_drained_begin_quiesce: + * + * Quiesces a BDS like bdrv_drained_begin(), but does not wait for already + * running requests to complete. + */ +void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, + BdrvChild *parent, bool ignore_bds_parents); + +/** + * Like bdrv_drained_begin, but recursively begins a quiesced section for + * exclusive access to all child nodes as well. + */ +void bdrv_subtree_drained_begin(BlockDriverState *bs); + +/** + * bdrv_drained_end: + * + * End a quiescent section started by bdrv_drained_begin(). + * + * This polls @bs's AioContext until all scheduled sub-drained_ends + * have settled. On one hand, that may result in graph changes. On + * the other, this requires that the caller either runs in the main + * loop; or that all involved nodes (@bs and all of its parents) are + * in the caller's AioContext. + */ +void bdrv_drained_end(BlockDriverState *bs); + +/** + * End a quiescent section started by bdrv_subtree_drained_begin(). + */ +void bdrv_subtree_drained_end(BlockDriverState *bs); + +#endif /* BLOCK_IO_H */ diff --git a/include/block/block.h b/include/block/block.h index e1713ee306..1e6b8fef1e 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -1,864 +1,32 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #ifndef BLOCK_H #define BLOCK_H -#include "block/aio.h" -#include "block/aio-wait.h" -#include "qemu/iov.h" -#include "qemu/coroutine.h" -#include "block/accounting.h" -#include "block/dirty-bitmap.h" -#include "block/blockjob.h" -#include "qemu/hbitmap.h" -#include "qemu/transactions.h" +#include "block-global-state.h" +#include "block-io.h" -/* - * generated_co_wrapper - * - * Function specifier, which does nothing but mark functions to be - * generated by scripts/block-coroutine-wrapper.py - * - * Read more in docs/devel/block-coroutine-wrapper.rst - */ -#define generated_co_wrapper +/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ -/* block.c */ -typedef struct BlockDriver BlockDriver; -typedef struct BdrvChild BdrvChild; -typedef struct BdrvChildClass BdrvChildClass; - -typedef struct BlockDriverInfo { - /* in bytes, 0 if irrelevant */ - int cluster_size; - /* offset at which the VM state can be saved (0 if not possible) */ - int64_t vm_state_offset; - bool is_dirty; - /* - * True if this block driver only supports compressed writes - */ - bool needs_compressed_writes; -} BlockDriverInfo; - -typedef struct BlockFragInfo { - uint64_t allocated_clusters; - uint64_t total_clusters; - uint64_t fragmented_clusters; - uint64_t compressed_clusters; -} BlockFragInfo; - -typedef enum { - BDRV_REQ_COPY_ON_READ = 0x1, - BDRV_REQ_ZERO_WRITE = 0x2, - - /* - * The BDRV_REQ_MAY_UNMAP flag is used in write_zeroes requests to indicate - * that the block driver should unmap (discard) blocks if it is guaranteed - * that the result will read back as zeroes. The flag is only passed to the - * driver if the block device is opened with BDRV_O_UNMAP. - */ - BDRV_REQ_MAY_UNMAP = 0x4, - - BDRV_REQ_FUA = 0x10, - BDRV_REQ_WRITE_COMPRESSED = 0x20, - - /* Signifies that this write request will not change the visible disk - * content. */ - BDRV_REQ_WRITE_UNCHANGED = 0x40, - - /* Forces request serialisation. Use only with write requests. */ - BDRV_REQ_SERIALISING = 0x80, - - /* Execute the request only if the operation can be offloaded or otherwise - * be executed efficiently, but return an error instead of using a slow - * fallback. */ - BDRV_REQ_NO_FALLBACK = 0x100, - - /* - * BDRV_REQ_PREFETCH makes sense only in the context of copy-on-read - * (i.e., together with the BDRV_REQ_COPY_ON_READ flag or when a COR - * filter is involved), in which case it signals that the COR operation - * need not read the data into memory (qiov) but only ensure they are - * copied to the top layer (i.e., that COR operation is done). - */ - BDRV_REQ_PREFETCH = 0x200, - - /* - * If we need to wait for other requests, just fail immediately. Used - * only together with BDRV_REQ_SERIALISING. - */ - BDRV_REQ_NO_WAIT = 0x400, - - /* Mask of valid flags */ - BDRV_REQ_MASK = 0x7ff, -} BdrvRequestFlags; - -typedef struct BlockSizes { - uint32_t phys; - uint32_t log; -} BlockSizes; - -typedef struct HDGeometry { - uint32_t heads; - uint32_t sectors; - uint32_t cylinders; -} HDGeometry; - -#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */ -#define BDRV_O_RDWR 0x0002 -#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */ -#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ -#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ -#define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */ -#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the thread pool */ -#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */ -#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ -#define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */ -#define BDRV_O_INACTIVE 0x0800 /* consistency hint for migration handoff */ -#define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ -#define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */ -#define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */ -#define BDRV_O_PROTOCOL 0x8000 /* if no block driver is explicitly given: - select an appropriate protocol driver, - ignoring the format layer */ -#define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */ -#define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening read-write fails */ -#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */ - -#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) - - -/* Option names of options parsed by the block layer */ - -#define BDRV_OPT_CACHE_WB "cache.writeback" -#define BDRV_OPT_CACHE_DIRECT "cache.direct" -#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush" -#define BDRV_OPT_READ_ONLY "read-only" -#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only" -#define BDRV_OPT_DISCARD "discard" -#define BDRV_OPT_FORCE_SHARE "force-share" - - -#define BDRV_SECTOR_BITS 9 -#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) - -#define BDRV_REQUEST_MAX_SECTORS MIN_CONST(SIZE_MAX >> BDRV_SECTOR_BITS, \ - INT_MAX >> BDRV_SECTOR_BITS) -#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) - -/* - * We want allow aligning requests and disk length up to any 32bit alignment - * and don't afraid of overflow. - * To achieve it, and in the same time use some pretty number as maximum disk - * size, let's define maximum "length" (a limit for any offset/bytes request and - * for disk size) to be the greatest power of 2 less than INT64_MAX. - */ -#define BDRV_MAX_ALIGNMENT (1L << 30) -#define BDRV_MAX_LENGTH (QEMU_ALIGN_DOWN(INT64_MAX, BDRV_MAX_ALIGNMENT)) - -/* - * Allocation status flags for bdrv_block_status() and friends. - * - * Public flags: - * BDRV_BLOCK_DATA: allocation for data at offset is tied to this layer - * BDRV_BLOCK_ZERO: offset reads as zero - * BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data - * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this - * layer rather than any backing, set by block layer - * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this - * layer, set by block layer - * - * Internal flags: - * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request - * that the block layer recompute the answer from the returned - * BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID. - * BDRV_BLOCK_RECURSE: request that the block layer will recursively search for - * zeroes in file child of current block node inside - * returned region. Only valid together with both - * BDRV_BLOCK_DATA and BDRV_BLOCK_OFFSET_VALID. Should not - * appear with BDRV_BLOCK_ZERO. - * - * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the - * host offset within the returned BDS that is allocated for the - * corresponding raw guest data. However, whether that offset - * actually contains data also depends on BDRV_BLOCK_DATA, as follows: - * - * DATA ZERO OFFSET_VALID - * t t t sectors read as zero, returned file is zero at offset - * t f t sectors read as valid from file at offset - * f t t sectors preallocated, read as zero, returned file not - * necessarily zero at offset - * f f t sectors preallocated but read from backing_hd, - * returned file contains garbage at offset - * t t f sectors preallocated, read as zero, unknown offset - * t f f sectors read from unknown file or offset - * f t f not allocated or unknown offset, read as zero - * f f f not allocated or unknown offset, read from backing_hd - */ -#define BDRV_BLOCK_DATA 0x01 -#define BDRV_BLOCK_ZERO 0x02 -#define BDRV_BLOCK_OFFSET_VALID 0x04 -#define BDRV_BLOCK_RAW 0x08 -#define BDRV_BLOCK_ALLOCATED 0x10 -#define BDRV_BLOCK_EOF 0x20 -#define BDRV_BLOCK_RECURSE 0x40 - -typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; - -typedef struct BDRVReopenState { - BlockDriverState *bs; - int flags; - BlockdevDetectZeroesOptions detect_zeroes; - bool backing_missing; - BlockDriverState *old_backing_bs; /* keep pointer for permissions update */ - BlockDriverState *old_file_bs; /* keep pointer for permissions update */ - QDict *options; - QDict *explicit_options; - void *opaque; -} BDRVReopenState; - -/* - * Block operation types - */ -typedef enum BlockOpType { - BLOCK_OP_TYPE_BACKUP_SOURCE, - BLOCK_OP_TYPE_BACKUP_TARGET, - BLOCK_OP_TYPE_CHANGE, - BLOCK_OP_TYPE_COMMIT_SOURCE, - BLOCK_OP_TYPE_COMMIT_TARGET, - BLOCK_OP_TYPE_DATAPLANE, - BLOCK_OP_TYPE_DRIVE_DEL, - BLOCK_OP_TYPE_EJECT, - BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, - BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, - BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, - BLOCK_OP_TYPE_MIRROR_SOURCE, - BLOCK_OP_TYPE_MIRROR_TARGET, - BLOCK_OP_TYPE_RESIZE, - BLOCK_OP_TYPE_STREAM, - BLOCK_OP_TYPE_REPLACE, - BLOCK_OP_TYPE_MAX, -} BlockOpType; - -/* Block node permission constants */ -enum { - /** - * A user that has the "permission" of consistent reads is guaranteed that - * their view of the contents of the block device is complete and - * self-consistent, representing the contents of a disk at a specific - * point. - * - * For most block devices (including their backing files) this is true, but - * the property cannot be maintained in a few situations like for - * intermediate nodes of a commit block job. - */ - BLK_PERM_CONSISTENT_READ = 0x01, - - /** This permission is required to change the visible disk contents. */ - BLK_PERM_WRITE = 0x02, - - /** - * This permission (which is weaker than BLK_PERM_WRITE) is both enough and - * required for writes to the block node when the caller promises that - * the visible disk content doesn't change. - * - * As the BLK_PERM_WRITE permission is strictly stronger, either is - * sufficient to perform an unchanging write. - */ - BLK_PERM_WRITE_UNCHANGED = 0x04, - - /** This permission is required to change the size of a block node. */ - BLK_PERM_RESIZE = 0x08, - - /** - * There was a now-removed bit BLK_PERM_GRAPH_MOD, with value of 0x10. QEMU - * 6.1 and earlier may still lock the corresponding byte in block/file-posix - * locking. So, implementing some new permission should be very careful to - * not interfere with this old unused thing. - */ - - BLK_PERM_ALL = 0x0f, - - DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ - | BLK_PERM_WRITE - | BLK_PERM_WRITE_UNCHANGED - | BLK_PERM_RESIZE, - - DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH, -}; - -/* - * Flags that parent nodes assign to child nodes to specify what kind of - * role(s) they take. - * - * At least one of DATA, METADATA, FILTERED, or COW must be set for - * every child. - */ -enum BdrvChildRoleBits { - /* - * This child stores data. - * Any node may have an arbitrary number of such children. - */ - BDRV_CHILD_DATA = (1 << 0), - - /* - * This child stores metadata. - * Any node may have an arbitrary number of metadata-storing - * children. - */ - BDRV_CHILD_METADATA = (1 << 1), - - /* - * A child that always presents exactly the same visible data as - * the parent, e.g. by virtue of the parent forwarding all reads - * and writes. - * This flag is mutually exclusive with DATA, METADATA, and COW. - * Any node may have at most one filtered child at a time. - */ - BDRV_CHILD_FILTERED = (1 << 2), - - /* - * Child from which to read all data that isn't allocated in the - * parent (i.e., the backing child); such data is copied to the - * parent through COW (and optionally COR). - * This field is mutually exclusive with DATA, METADATA, and - * FILTERED. - * Any node may have at most one such backing child at a time. - */ - BDRV_CHILD_COW = (1 << 3), - - /* - * The primary child. For most drivers, this is the child whose - * filename applies best to the parent node. - * Any node may have at most one primary child at a time. - */ - BDRV_CHILD_PRIMARY = (1 << 4), - - /* Useful combination of flags */ - BDRV_CHILD_IMAGE = BDRV_CHILD_DATA - | BDRV_CHILD_METADATA - | BDRV_CHILD_PRIMARY, -}; - -/* Mask of BdrvChildRoleBits values */ -typedef unsigned int BdrvChildRole; - -char *bdrv_perm_names(uint64_t perm); -uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); - -void bdrv_init(void); -void bdrv_init_with_whitelist(void); -bool bdrv_uses_whitelist(void); -int bdrv_is_whitelisted(BlockDriver *drv, bool read_only); -BlockDriver *bdrv_find_protocol(const char *filename, - bool allow_protocol_prefix, - Error **errp); -BlockDriver *bdrv_find_format(const char *format_name); -int bdrv_create(BlockDriver *drv, const char* filename, - QemuOpts *opts, Error **errp); -int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); - -BlockDriverState *bdrv_new(void); -int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, - Error **errp); -int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, - Error **errp); -int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, - Error **errp); -BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, - int flags, Error **errp); -int bdrv_drop_filter(BlockDriverState *bs, Error **errp); - -int bdrv_parse_aio(const char *mode, int *flags); -int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough); -int bdrv_parse_discard_flags(const char *mode, int *flags); -BdrvChild *bdrv_open_child(const char *filename, - QDict *options, const char *bdref_key, - BlockDriverState* parent, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - bool allow_none, Error **errp); -BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); -int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, - Error **errp); -int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, - const char *bdref_key, Error **errp); -BlockDriverState *bdrv_open(const char *filename, const char *reference, - QDict *options, int flags, Error **errp); -BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, - const char *node_name, - QDict *options, int flags, - Error **errp); -BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp); -BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, - BlockDriverState *bs, QDict *options, - bool keep_old_opts); -void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue); -int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); -int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, - Error **errp); -int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, - Error **errp); -int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes); -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, - int64_t bytes); -int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int64_t bytes); -/* - * Efficiently zero a region of the disk image. Note that this is a regular - * I/O request like read or write and should have a reasonable size. This - * function is not suitable for zeroing the entire image in a single request - * because it may allocate memory for the entire region. - */ -int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, - const char *backing_file); -void bdrv_refresh_filename(BlockDriverState *bs); - -int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, - Error **errp); -int generated_co_wrapper -bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); - -int64_t bdrv_nb_sectors(BlockDriverState *bs); -int64_t bdrv_getlength(BlockDriverState *bs); -int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); -BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, - BlockDriverState *in_bs, Error **errp); -void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); -void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); -int bdrv_commit(BlockDriverState *bs); -int bdrv_make_empty(BdrvChild *c, Error **errp); -int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, - const char *backing_fmt, bool warn); -void bdrv_register(BlockDriver *bdrv); -int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, - const char *backing_file_str); -BlockDriverState *bdrv_find_overlay(BlockDriverState *active, - BlockDriverState *bs); -BlockDriverState *bdrv_find_base(BlockDriverState *bs); -bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, - Error **errp); -int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, - Error **errp); -void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); -int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp); -void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs); - - -typedef struct BdrvCheckResult { - int corruptions; - int leaks; - int check_errors; - int corruptions_fixed; - int leaks_fixed; - int64_t image_end_offset; - BlockFragInfo bfi; -} BdrvCheckResult; - -typedef enum { - BDRV_FIX_LEAKS = 1, - BDRV_FIX_ERRORS = 2, -} BdrvCheckMode; - -int generated_co_wrapper bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix); - -/* The units of offset and total_work_size may be chosen arbitrarily by the - * block driver; total_work_size may change during the course of the amendment - * operation */ -typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, - int64_t total_work_size, void *opaque); -int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque, - bool force, - Error **errp); - -/* check if a named node can be replaced when doing drive-mirror */ -BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, - const char *node_name, Error **errp); - -/* async block I/O */ -void bdrv_aio_cancel(BlockAIOCB *acb); -void bdrv_aio_cancel_async(BlockAIOCB *acb); - -/* sg packet commands */ -int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); - -/* Invalidate any cached metadata used by image formats */ -int generated_co_wrapper bdrv_invalidate_cache(BlockDriverState *bs, - Error **errp); -void bdrv_invalidate_cache_all(Error **errp); -int bdrv_inactivate_all(void); - -/* Ensure contents are flushed to disk. */ -int generated_co_wrapper bdrv_flush(BlockDriverState *bs); -int coroutine_fn bdrv_co_flush(BlockDriverState *bs); -int bdrv_flush_all(void); -void bdrv_close_all(void); -void bdrv_drain(BlockDriverState *bs); -void coroutine_fn bdrv_co_drain(BlockDriverState *bs); -void bdrv_drain_all_begin(void); -void bdrv_drain_all_end(void); -void bdrv_drain_all(void); - -#define BDRV_POLL_WHILE(bs, cond) ({ \ - BlockDriverState *bs_ = (bs); \ - AIO_WAIT_WHILE(bdrv_get_aio_context(bs_), \ - cond); }) - -int generated_co_wrapper bdrv_pdiscard(BdrvChild *child, int64_t offset, - int64_t bytes); -int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); -int bdrv_has_zero_init_1(BlockDriverState *bs); -int bdrv_has_zero_init(BlockDriverState *bs); -bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); -int bdrv_block_status(BlockDriverState *bs, int64_t offset, - int64_t bytes, int64_t *pnum, int64_t *map, - BlockDriverState **file); -int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, - int64_t offset, int64_t bytes, int64_t *pnum, - int64_t *map, BlockDriverState **file); -int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, - int64_t *pnum); -int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, - bool include_base, int64_t offset, int64_t bytes, - int64_t *pnum); -int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, - int64_t bytes); - -bool bdrv_is_read_only(BlockDriverState *bs); -int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, - bool ignore_allow_rdw, Error **errp); -int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, - Error **errp); -bool bdrv_is_writable(BlockDriverState *bs); -bool bdrv_is_sg(BlockDriverState *bs); -bool bdrv_is_inserted(BlockDriverState *bs); -void bdrv_lock_medium(BlockDriverState *bs, bool locked); -void bdrv_eject(BlockDriverState *bs, bool eject_flag); -const char *bdrv_get_format_name(BlockDriverState *bs); -BlockDriverState *bdrv_find_node(const char *node_name); -BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); -XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); -BlockDriverState *bdrv_lookup_bs(const char *device, - const char *node_name, - Error **errp); -bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); -BlockDriverState *bdrv_next_node(BlockDriverState *bs); -BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); - -typedef struct BdrvNextIterator { - enum { - BDRV_NEXT_BACKEND_ROOTS, - BDRV_NEXT_MONITOR_OWNED, - } phase; - BlockBackend *blk; - BlockDriverState *bs; -} BdrvNextIterator; - -BlockDriverState *bdrv_first(BdrvNextIterator *it); -BlockDriverState *bdrv_next(BdrvNextIterator *it); -void bdrv_next_cleanup(BdrvNextIterator *it); - -BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); -bool bdrv_supports_compressed_writes(BlockDriverState *bs); -void bdrv_iterate_format(void (*it)(void *opaque, const char *name), - void *opaque, bool read_only); -const char *bdrv_get_node_name(const BlockDriverState *bs); -const char *bdrv_get_device_name(const BlockDriverState *bs); -const char *bdrv_get_device_or_node_name(const BlockDriverState *bs); -int bdrv_get_flags(BlockDriverState *bs); -int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); -ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, - Error **errp); -BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); -void bdrv_round_to_clusters(BlockDriverState *bs, - int64_t offset, int64_t bytes, - int64_t *cluster_offset, - int64_t *cluster_bytes); - -void bdrv_get_backing_filename(BlockDriverState *bs, - char *filename, int filename_size); -char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); -char *bdrv_get_full_backing_filename_from_filename(const char *backed, - const char *backing, - Error **errp); -char *bdrv_dirname(BlockDriverState *bs, Error **errp); - -int path_has_protocol(const char *path); -int path_is_absolute(const char *path); -char *path_combine(const char *base_path, const char *filename); - -int generated_co_wrapper -bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); -int generated_co_wrapper -bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); -int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, - int64_t pos, int size); - -int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, - int64_t pos, int size); - -void bdrv_img_create(const char *filename, const char *fmt, - const char *base_filename, const char *base_fmt, - char *options, uint64_t img_size, int flags, - bool quiet, Error **errp); - -/* Returns the alignment in bytes that is required so that no bounce buffer - * is required throughout the stack */ -size_t bdrv_min_mem_align(BlockDriverState *bs); -/* Returns optimal alignment in bytes for bounce buffer */ -size_t bdrv_opt_mem_align(BlockDriverState *bs); -void *qemu_blockalign(BlockDriverState *bs, size_t size); -void *qemu_blockalign0(BlockDriverState *bs, size_t size); -void *qemu_try_blockalign(BlockDriverState *bs, size_t size); -void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); -bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov); - -void bdrv_enable_copy_on_read(BlockDriverState *bs); -void bdrv_disable_copy_on_read(BlockDriverState *bs); - -void bdrv_ref(BlockDriverState *bs); -void bdrv_unref(BlockDriverState *bs); -void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); -BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, - BlockDriverState *child_bs, - const char *child_name, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - Error **errp); - -bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); -void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason); -void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason); -void bdrv_op_block_all(BlockDriverState *bs, Error *reason); -void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason); -bool bdrv_op_blocker_is_empty(BlockDriverState *bs); - -#define BLKDBG_EVENT(child, evt) \ - do { \ - if (child) { \ - bdrv_debug_event(child->bs, evt); \ - } \ - } while (0) - -void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); - -int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, - const char *tag); -int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag); -int bdrv_debug_resume(BlockDriverState *bs, const char *tag); -bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag); - -/** - * bdrv_get_aio_context: - * - * Returns: the currently bound #AioContext - */ -AioContext *bdrv_get_aio_context(BlockDriverState *bs); - -/** - * Move the current coroutine to the AioContext of @bs and return the old - * AioContext of the coroutine. Increase bs->in_flight so that draining @bs - * will wait for the operation to proceed until the corresponding - * bdrv_co_leave(). - * - * Consequently, you can't call drain inside a bdrv_co_enter/leave() section as - * this will deadlock. - */ -AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs); - -/** - * Ends a section started by bdrv_co_enter(). Move the current coroutine back - * to old_ctx and decrease bs->in_flight again. - */ -void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx); - -/** - * Locks the AioContext of @bs if it's not the current AioContext. This avoids - * double locking which could lead to deadlocks: This is a coroutine_fn, so we - * know we already own the lock of the current AioContext. - * - * May only be called in the main thread. - */ -void coroutine_fn bdrv_co_lock(BlockDriverState *bs); - -/** - * Unlocks the AioContext of @bs if it's not the current AioContext. - */ -void coroutine_fn bdrv_co_unlock(BlockDriverState *bs); - -/** - * Transfer control to @co in the aio context of @bs - */ -void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co); - -void bdrv_set_aio_context_ignore(BlockDriverState *bs, - AioContext *new_context, GSList **ignore); -int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - Error **errp); -int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp); -bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp); -bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, - GSList **ignore, Error **errp); -AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); -AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c); - -int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); -int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); - -void bdrv_io_plug(BlockDriverState *bs); -void bdrv_io_unplug(BlockDriverState *bs); - -/** - * bdrv_parent_drained_begin_single: - * - * Begin a quiesced section for the parent of @c. If @poll is true, wait for - * any pending activity to cease. - */ -void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll); - -/** - * bdrv_parent_drained_end_single: - * - * End a quiesced section for the parent of @c. - * - * This polls @bs's AioContext until all scheduled sub-drained_ends - * have settled, which may result in graph changes. - */ -void bdrv_parent_drained_end_single(BdrvChild *c); - -/** - * bdrv_drain_poll: - * - * Poll for pending requests in @bs, its parents (except for @ignore_parent), - * and if @recursive is true its children as well (used for subtree drain). - * - * If @ignore_bds_parents is true, parents that are BlockDriverStates must - * ignore the drain request because they will be drained separately (used for - * drain_all). - * - * This is part of bdrv_drained_begin. - */ -bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, - BdrvChild *ignore_parent, bool ignore_bds_parents); - -/** - * bdrv_drained_begin: - * - * Begin a quiesced section for exclusive access to the BDS, by disabling - * external request sources including NBD server, block jobs, and device model. - * - * This function can be recursive. - */ -void bdrv_drained_begin(BlockDriverState *bs); - -/** - * bdrv_do_drained_begin_quiesce: - * - * Quiesces a BDS like bdrv_drained_begin(), but does not wait for already - * running requests to complete. - */ -void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, - BdrvChild *parent, bool ignore_bds_parents); - -/** - * Like bdrv_drained_begin, but recursively begins a quiesced section for - * exclusive access to all child nodes as well. - */ -void bdrv_subtree_drained_begin(BlockDriverState *bs); - -/** - * bdrv_drained_end: - * - * End a quiescent section started by bdrv_drained_begin(). - * - * This polls @bs's AioContext until all scheduled sub-drained_ends - * have settled. On one hand, that may result in graph changes. On - * the other, this requires that the caller either runs in the main - * loop; or that all involved nodes (@bs and all of its parents) are - * in the caller's AioContext. - */ -void bdrv_drained_end(BlockDriverState *bs); - -/** - * bdrv_drained_end_no_poll: - * - * Same as bdrv_drained_end(), but do not poll for the subgraph to - * actually become unquiesced. Therefore, no graph changes will occur - * with this function. - * - * *drained_end_counter is incremented for every background operation - * that is scheduled, and will be decremented for every operation once - * it settles. The caller must poll until it reaches 0. The counter - * should be accessed using atomic operations only. - */ -void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); - -/** - * End a quiescent section started by bdrv_subtree_drained_begin(). - */ -void bdrv_subtree_drained_end(BlockDriverState *bs); - -void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, - Error **errp); -void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); - -bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, - uint32_t granularity, Error **errp); -/** - * - * bdrv_register_buf/bdrv_unregister_buf: - * - * Register/unregister a buffer for I/O. For example, VFIO drivers are - * interested to know the memory areas that would later be used for I/O, so - * that they can prepare IOMMU mapping etc., to get better performance. - */ -void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size); -void bdrv_unregister_buf(BlockDriverState *bs, void *host); - -/** - * - * bdrv_co_copy_range: - * - * Do offloaded copy between two children. If the operation is not implemented - * by the driver, or if the backend storage doesn't support it, a negative - * error code will be returned. - * - * Note: block layer doesn't emulate or fallback to a bounce buffer approach - * because usually the caller shouldn't attempt offloaded copy any more (e.g. - * calling copy_file_range(2)) after the first error, thus it should fall back - * to a read+write path in the caller level. - * - * @src: Source child to copy data from - * @src_offset: offset in @src image to read data - * @dst: Destination child to copy data to - * @dst_offset: offset in @dst image to write data - * @bytes: number of bytes to copy - * @flags: request flags. Supported flags: - * BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero - * write on @dst as if bdrv_co_pwrite_zeroes is - * called. Used to simplify caller code, or - * during BlockDriver.bdrv_co_copy_range_from() - * recursion. - * BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping - * requests currently in flight. - * - * Returns: 0 if succeeded; negative error code if failed. - **/ -int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, - BdrvChild *dst, int64_t dst_offset, - int64_t bytes, BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); - -void bdrv_cancel_in_flight(BlockDriverState *bs); - -#endif +#endif /* BLOCK_H */ diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h new file mode 100644 index 0000000000..8947abab76 --- /dev/null +++ b/include/block/block_int-common.h @@ -0,0 +1,1246 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BLOCK_INT_COMMON_H +#define BLOCK_INT_COMMON_H + +#include "block/accounting.h" +#include "block/block.h" +#include "block/aio-wait.h" +#include "qemu/queue.h" +#include "qemu/coroutine.h" +#include "qemu/stats64.h" +#include "qemu/timer.h" +#include "qemu/hbitmap.h" +#include "block/snapshot.h" +#include "qemu/throttle.h" +#include "qemu/rcu.h" + +#define BLOCK_FLAG_LAZY_REFCOUNTS 8 + +#define BLOCK_OPT_SIZE "size" +#define BLOCK_OPT_ENCRYPT "encryption" +#define BLOCK_OPT_ENCRYPT_FORMAT "encrypt.format" +#define BLOCK_OPT_COMPAT6 "compat6" +#define BLOCK_OPT_HWVERSION "hwversion" +#define BLOCK_OPT_BACKING_FILE "backing_file" +#define BLOCK_OPT_BACKING_FMT "backing_fmt" +#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" +#define BLOCK_OPT_TABLE_SIZE "table_size" +#define BLOCK_OPT_PREALLOC "preallocation" +#define BLOCK_OPT_SUBFMT "subformat" +#define BLOCK_OPT_COMPAT_LEVEL "compat" +#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts" +#define BLOCK_OPT_ADAPTER_TYPE "adapter_type" +#define BLOCK_OPT_REDUNDANCY "redundancy" +#define BLOCK_OPT_NOCOW "nocow" +#define BLOCK_OPT_EXTENT_SIZE_HINT "extent_size_hint" +#define BLOCK_OPT_OBJECT_SIZE "object_size" +#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" +#define BLOCK_OPT_DATA_FILE "data_file" +#define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" +#define BLOCK_OPT_COMPRESSION_TYPE "compression_type" +#define BLOCK_OPT_EXTL2 "extended_l2" + +#define BLOCK_PROBE_BUF_SIZE 512 + +enum BdrvTrackedRequestType { + BDRV_TRACKED_READ, + BDRV_TRACKED_WRITE, + BDRV_TRACKED_DISCARD, + BDRV_TRACKED_TRUNCATE, +}; + +/* + * That is not quite good that BdrvTrackedRequest structure is public, + * as block/io.c is very careful about incoming offset/bytes being + * correct. Be sure to assert bdrv_check_request() succeeded after any + * modification of BdrvTrackedRequest object out of block/io.c + */ +typedef struct BdrvTrackedRequest { + BlockDriverState *bs; + int64_t offset; + int64_t bytes; + enum BdrvTrackedRequestType type; + + bool serialising; + int64_t overlap_offset; + int64_t overlap_bytes; + + QLIST_ENTRY(BdrvTrackedRequest) list; + Coroutine *co; /* owner, used for deadlock detection */ + CoQueue wait_queue; /* coroutines blocked on this request */ + + struct BdrvTrackedRequest *waiting_for; +} BdrvTrackedRequest; + + +struct BlockDriver { + /* + * These fields are initialized when this object is created, + * and are never changed afterwards. + */ + + const char *format_name; + int instance_size; + + /* + * Set to true if the BlockDriver is a block filter. Block filters pass + * certain callbacks that refer to data (see block.c) to their bs->file + * or bs->backing (whichever one exists) if the driver doesn't implement + * them. Drivers that do not wish to forward must implement them and return + * -ENOTSUP. + * Note that filters are not allowed to modify data. + * + * Filters generally cannot have more than a single filtered child, + * because the data they present must at all times be the same as + * that on their filtered child. That would be impossible to + * achieve for multiple filtered children. + * (And this filtered child must then be bs->file or bs->backing.) + */ + bool is_filter; + /* + * Set to true if the BlockDriver is a format driver. Format nodes + * generally do not expect their children to be other format nodes + * (except for backing files), and so format probing is disabled + * on those children. + */ + bool is_format; + + /* + * Drivers not implementing bdrv_parse_filename nor bdrv_open should have + * this field set to true, except ones that are defined only by their + * child's bs. + * An example of the last type will be the quorum block driver. + */ + bool bdrv_needs_filename; + + /* + * Set if a driver can support backing files. This also implies the + * following semantics: + * + * - Return status 0 of .bdrv_co_block_status means that corresponding + * blocks are not allocated in this layer of backing-chain + * - For such (unallocated) blocks, read will: + * - fill buffer with zeros if there is no backing file + * - read from the backing file otherwise, where the block layer + * takes care of reading zeros beyond EOF if backing file is short + */ + bool supports_backing; + + bool has_variable_length; + + /* + * Drivers setting this field must be able to work with just a plain + * filename with ':' as a prefix, and no other options. + * Options may be extracted from the filename by implementing + * bdrv_parse_filename. + */ + const char *protocol_name; + + /* List of options for creating images, terminated by name == NULL */ + QemuOptsList *create_opts; + + /* List of options for image amend */ + QemuOptsList *amend_opts; + + /* + * If this driver supports reopening images this contains a + * NULL-terminated list of the runtime options that can be + * modified. If an option in this list is unspecified during + * reopen then it _must_ be reset to its default value or return + * an error. + */ + const char *const *mutable_opts; + + /* + * Pointer to a NULL-terminated array of names of strong options + * that can be specified for bdrv_open(). A strong option is one + * that changes the data of a BDS. + * If this pointer is NULL, the array is considered empty. + * "filename" and "driver" are always considered strong. + */ + const char *const *strong_runtime_opts; + + + /* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + + /* + * This function is invoked under BQL before .bdrv_co_amend() + * (which in contrast does not necessarily run under the BQL) + * to allow driver-specific initialization code that requires + * the BQL, like setting up specific permission flags. + */ + int (*bdrv_amend_pre_run)(BlockDriverState *bs, Error **errp); + /* + * This function is invoked under BQL after .bdrv_co_amend() + * to allow cleaning up what was done in .bdrv_amend_pre_run(). + */ + void (*bdrv_amend_clean)(BlockDriverState *bs); + + /* + * Return true if @to_replace can be replaced by a BDS with the + * same data as @bs without it affecting @bs's behavior (that is, + * without it being visible to @bs's parents). + */ + bool (*bdrv_recurse_can_replace)(BlockDriverState *bs, + BlockDriverState *to_replace); + + int (*bdrv_probe_device)(const char *filename); + + /* + * Any driver implementing this callback is expected to be able to handle + * NULL file names in its .bdrv_open() implementation. + */ + void (*bdrv_parse_filename)(const char *filename, QDict *options, + Error **errp); + + /* For handling image reopen for split or non-split files. */ + int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state, + BlockReopenQueue *queue, Error **errp); + void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); + void (*bdrv_reopen_commit_post)(BDRVReopenState *reopen_state); + void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); + void (*bdrv_join_options)(QDict *options, QDict *old_options); + + int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags, + Error **errp); + + /* Protocol drivers should implement this instead of bdrv_open */ + int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, + Error **errp); + void (*bdrv_close)(BlockDriverState *bs); + + int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts, + Error **errp); + int coroutine_fn (*bdrv_co_create_opts)(BlockDriver *drv, + const char *filename, + QemuOpts *opts, + Error **errp); + + int (*bdrv_amend_options)(BlockDriverState *bs, + QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, + bool force, + Error **errp); + + int (*bdrv_make_empty)(BlockDriverState *bs); + + /* + * Refreshes the bs->exact_filename field. If that is impossible, + * bs->exact_filename has to be left empty. + */ + void (*bdrv_refresh_filename)(BlockDriverState *bs); + + /* + * Gathers the open options for all children into @target. + * A simple format driver (without backing file support) might + * implement this function like this: + * + * QINCREF(bs->file->bs->full_open_options); + * qdict_put(target, "file", bs->file->bs->full_open_options); + * + * If not specified, the generic implementation will simply put + * all children's options under their respective name. + * + * @backing_overridden is true when bs->backing seems not to be + * the child that would result from opening bs->backing_file. + * Therefore, if it is true, the backing child's options should be + * gathered; otherwise, there is no need since the backing child + * is the one implied by the image header. + * + * Note that ideally this function would not be needed. Every + * block driver which implements it is probably doing something + * shady regarding its runtime option structure. + */ + void (*bdrv_gather_child_options)(BlockDriverState *bs, QDict *target, + bool backing_overridden); + + /* + * Returns an allocated string which is the directory name of this BDS: It + * will be used to make relative filenames absolute by prepending this + * function's return value to them. + */ + char *(*bdrv_dirname)(BlockDriverState *bs, Error **errp); + + /* + * This informs the driver that we are no longer interested in the result + * of in-flight requests, so don't waste the time if possible. + * + * One example usage is to avoid waiting for an nbd target node reconnect + * timeout during job-cancel with force=true. + */ + void (*bdrv_cancel_in_flight)(BlockDriverState *bs); + + int (*bdrv_inactivate)(BlockDriverState *bs); + + int (*bdrv_snapshot_create)(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info); + int (*bdrv_snapshot_goto)(BlockDriverState *bs, + const char *snapshot_id); + int (*bdrv_snapshot_delete)(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); + int (*bdrv_snapshot_list)(BlockDriverState *bs, + QEMUSnapshotInfo **psn_info); + int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); + + int (*bdrv_change_backing_file)(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt); + + /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ + int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event, + const char *tag); + int (*bdrv_debug_remove_breakpoint)(BlockDriverState *bs, + const char *tag); + int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag); + bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag); + + void (*bdrv_refresh_limits)(BlockDriverState *bs, Error **errp); + + /* + * Returns 1 if newly created images are guaranteed to contain only + * zeros, 0 otherwise. + */ + int (*bdrv_has_zero_init)(BlockDriverState *bs); + + /* + * Remove fd handlers, timers, and other event loop callbacks so the event + * loop is no longer in use. Called with no in-flight requests and in + * depth-first traversal order with parents before child nodes. + */ + void (*bdrv_detach_aio_context)(BlockDriverState *bs); + + /* + * Add fd handlers, timers, and other event loop callbacks so I/O requests + * can be processed again. Called with no in-flight requests and in + * depth-first traversal order with child nodes before parent nodes. + */ + void (*bdrv_attach_aio_context)(BlockDriverState *bs, + AioContext *new_context); + + /** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ + int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz); + /** + * Try to get @bs's geometry (cyls, heads, sectors) + * On success, store them in @geo and return 0. + * On failure return -errno. + * Only drivers that want to override guest geometry implement this + * callback; see hd_geometry_guess(). + */ + int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); + + void (*bdrv_add_child)(BlockDriverState *parent, BlockDriverState *child, + Error **errp); + void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child, + Error **errp); + + /** + * Informs the block driver that a permission change is intended. The + * driver checks whether the change is permissible and may take other + * preparations for the change (e.g. get file system locks). This operation + * is always followed either by a call to either .bdrv_set_perm or + * .bdrv_abort_perm_update. + * + * Checks whether the requested set of cumulative permissions in @perm + * can be granted for accessing @bs and whether no other users are using + * permissions other than those given in @shared (both arguments take + * BLK_PERM_* bitmasks). + * + * If both conditions are met, 0 is returned. Otherwise, -errno is returned + * and errp is set to an error describing the conflict. + */ + int (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, + uint64_t shared, Error **errp); + + /** + * Called to inform the driver that the set of cumulative set of used + * permissions for @bs has changed to @perm, and the set of sharable + * permission to @shared. The driver can use this to propagate changes to + * its children (i.e. request permissions only if a parent actually needs + * them). + * + * This function is only invoked after bdrv_check_perm(), so block drivers + * may rely on preparations made in their .bdrv_check_perm implementation. + */ + void (*bdrv_set_perm)(BlockDriverState *bs, uint64_t perm, uint64_t shared); + + /* + * Called to inform the driver that after a previous bdrv_check_perm() + * call, the permission update is not performed and any preparations made + * for it (e.g. taken file locks) need to be undone. + * + * This function can be called even for nodes that never saw a + * bdrv_check_perm() call. It is a no-op then. + */ + void (*bdrv_abort_perm_update)(BlockDriverState *bs); + + /** + * Returns in @nperm and @nshared the permissions that the driver for @bs + * needs on its child @c, based on the cumulative permissions requested by + * the parents in @parent_perm and @parent_shared. + * + * If @c is NULL, return the permissions for attaching a new child for the + * given @child_class and @role. + * + * If @reopen_queue is non-NULL, don't return the currently needed + * permissions, but those that will be needed after applying the + * @reopen_queue. + */ + void (*bdrv_child_perm)(BlockDriverState *bs, BdrvChild *c, + BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t parent_perm, uint64_t parent_shared, + uint64_t *nperm, uint64_t *nshared); + + /** + * Register/unregister a buffer for I/O. For example, when the driver is + * interested to know the memory areas that will later be used in iovs, so + * that it can do IOMMU mapping with VFIO etc., in order to get better + * performance. In the case of VFIO drivers, this callback is used to do + * DMA mapping for hot buffers. + */ + void (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size); + void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host); + + /* + * This field is modified only under the BQL, and is part of + * the global state. + */ + QLIST_ENTRY(BlockDriver) list; + + /* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + + int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); + + int coroutine_fn (*bdrv_co_amend)(BlockDriverState *bs, + BlockdevAmendOptions *opts, + bool force, + Error **errp); + + /* aio */ + BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); + BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); + BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, + BlockCompletionFunc *cb, void *opaque); + BlockAIOCB *(*bdrv_aio_pdiscard)(BlockDriverState *bs, + int64_t offset, int bytes, + BlockCompletionFunc *cb, void *opaque); + + int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); + + /** + * @offset: position in bytes to read at + * @bytes: number of bytes to read + * @qiov: the buffers to fill with read data + * @flags: currently unused, always 0 + * + * @offset and @bytes will be a multiple of 'request_alignment', + * but the length of individual @qiov elements does not have to + * be a multiple. + * + * @bytes will always equal the total size of @qiov, and will be + * no larger than 'max_transfer'. + * + * The buffer in @qiov may point directly to guest memory. + */ + int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); + + int coroutine_fn (*bdrv_co_preadv_part)(BlockDriverState *bs, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); + + int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, + int flags); + /** + * @offset: position in bytes to write at + * @bytes: number of bytes to write + * @qiov: the buffers containing data to write + * @flags: zero or more bits allowed by 'supported_write_flags' + * + * @offset and @bytes will be a multiple of 'request_alignment', + * but the length of individual @qiov elements does not have to + * be a multiple. + * + * @bytes will always equal the total size of @qiov, and will be + * no larger than 'max_transfer'. + * + * The buffer in @qiov may point directly to guest memory. + */ + int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); + int coroutine_fn (*bdrv_co_pwritev_part)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); + + /* + * Efficiently zero a region of the disk image. Typically an image format + * would use a compact metadata representation to implement this. This + * function pointer may be NULL or return -ENOSUP and .bdrv_co_writev() + * will be called instead. + */ + int coroutine_fn (*bdrv_co_pwrite_zeroes)(BlockDriverState *bs, + int64_t offset, int64_t bytes, BdrvRequestFlags flags); + int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, + int64_t offset, int64_t bytes); + + /* + * Map [offset, offset + nbytes) range onto a child of @bs to copy from, + * and invoke bdrv_co_copy_range_from(child, ...), or invoke + * bdrv_co_copy_range_to() if @bs is the leaf child to copy data from. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. + */ + int coroutine_fn (*bdrv_co_copy_range_from)(BlockDriverState *bs, + BdrvChild *src, + int64_t offset, + BdrvChild *dst, + int64_t dst_offset, + int64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + + /* + * Map [offset, offset + nbytes) range onto a child of bs to copy data to, + * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy + * operation if @bs is the leaf and @src has the same BlockDriver. Return + * -ENOTSUP if @bs is the leaf but @src has a different BlockDriver. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. + */ + int coroutine_fn (*bdrv_co_copy_range_to)(BlockDriverState *bs, + BdrvChild *src, + int64_t src_offset, + BdrvChild *dst, + int64_t dst_offset, + int64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + + /* + * Building block for bdrv_block_status[_above] and + * bdrv_is_allocated[_above]. The driver should answer only + * according to the current layer, and should only need to set + * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID, + * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing + * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). See + * block.h for the overall meaning of the bits. As a hint, the + * flag want_zero is true if the caller cares more about precise + * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for + * overall allocation (favor larger *pnum, perhaps by reporting + * _DATA instead of _ZERO). The block layer guarantees input + * clamped to bdrv_getlength() and aligned to request_alignment, + * as well as non-NULL pnum, map, and file; in turn, the driver + * must return an error or set pnum to an aligned non-zero value. + * + * Note that @bytes is just a hint on how big of a region the + * caller wants to inspect. It is not a limit on *pnum. + * Implementations are free to return larger values of *pnum if + * doing so does not incur a performance penalty. + * + * block/io.c's bdrv_co_block_status() will utilize an unclamped + * *pnum value for the block-status cache on protocol nodes, prior + * to clamping *pnum for return to its caller. + */ + int coroutine_fn (*bdrv_co_block_status)(BlockDriverState *bs, + bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file); + + /* + * Snapshot-access API. + * + * Block-driver may provide snapshot-access API: special functions to access + * some internal "snapshot". The functions are similar with normal + * read/block_status/discard handler, but don't have any specific handling + * in generic block-layer: no serializing, no alignment, no tracked + * requests. So, block-driver that realizes these APIs is fully responsible + * for synchronization between snapshot-access API and normal IO requests. + * + * TODO: To be able to support qcow2's internal snapshots, this API will + * need to be extended to: + * - be able to select a specific snapshot + * - receive the snapshot's actual length (which may differ from bs's + * length) + */ + int coroutine_fn (*bdrv_co_preadv_snapshot)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); + int coroutine_fn (*bdrv_co_snapshot_block_status)(BlockDriverState *bs, + bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file); + int coroutine_fn (*bdrv_co_pdiscard_snapshot)(BlockDriverState *bs, + int64_t offset, int64_t bytes); + + /* + * Invalidate any cached meta-data. + */ + void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs, + Error **errp); + + /* + * Flushes all data for all layers by calling bdrv_co_flush for underlying + * layers, if needed. This function is needed for deterministic + * synchronization of the flush finishing callback. + */ + int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs); + + /* Delete a created file. */ + int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs, + Error **errp); + + /* + * Flushes all data that was already written to the OS all the way down to + * the disk (for example file-posix.c calls fsync()). + */ + int coroutine_fn (*bdrv_co_flush_to_disk)(BlockDriverState *bs); + + /* + * Flushes all internal caches to the OS. The data may still sit in a + * writeback cache of the host OS, but it will survive a crash of the qemu + * process. + */ + int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs); + + /* + * Truncate @bs to @offset bytes using the given @prealloc mode + * when growing. Modes other than PREALLOC_MODE_OFF should be + * rejected when shrinking @bs. + * + * If @exact is true, @bs must be resized to exactly @offset. + * Otherwise, it is sufficient for @bs (if it is a host block + * device and thus there is no way to resize it) to be at least + * @offset bytes in length. + * + * If @exact is true and this function fails but would succeed + * with @exact = false, it should return -ENOTSUP. + */ + int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset, + bool exact, PreallocMode prealloc, + BdrvRequestFlags flags, Error **errp); + int64_t (*bdrv_getlength)(BlockDriverState *bs); + int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs); + BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs, + Error **errp); + + int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov); + int coroutine_fn (*bdrv_co_pwritev_compressed_part)(BlockDriverState *bs, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset); + + int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); + + ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs, + Error **errp); + BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs); + + int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs, + QEMUIOVector *qiov, + int64_t pos); + int coroutine_fn (*bdrv_load_vmstate)(BlockDriverState *bs, + QEMUIOVector *qiov, + int64_t pos); + + /* removable device specific */ + bool (*bdrv_is_inserted)(BlockDriverState *bs); + void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag); + void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked); + + /* to control generic scsi devices */ + BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockCompletionFunc *cb, void *opaque); + int coroutine_fn (*bdrv_co_ioctl)(BlockDriverState *bs, + unsigned long int req, void *buf); + + /* + * Returns 0 for completed check, -errno for internal errors. + * The check results are stored in result. + */ + int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs, + BdrvCheckResult *result, + BdrvCheckMode fix); + + void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); + + /* io queue for linux-aio */ + void (*bdrv_io_plug)(BlockDriverState *bs); + void (*bdrv_io_unplug)(BlockDriverState *bs); + + /** + * bdrv_co_drain_begin is called if implemented in the beginning of a + * drain operation to drain and stop any internal sources of requests in + * the driver. + * bdrv_co_drain_end is called if implemented at the end of the drain. + * + * They should be used by the driver to e.g. manage scheduled I/O + * requests, or toggle an internal state. After the end of the drain new + * requests will continue normally. + */ + void coroutine_fn (*bdrv_co_drain_begin)(BlockDriverState *bs); + void coroutine_fn (*bdrv_co_drain_end)(BlockDriverState *bs); + + bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs); + bool (*bdrv_co_can_store_new_dirty_bitmap)(BlockDriverState *bs, + const char *name, + uint32_t granularity, + Error **errp); + int (*bdrv_co_remove_persistent_dirty_bitmap)(BlockDriverState *bs, + const char *name, + Error **errp); +}; + +static inline bool block_driver_can_compress(BlockDriver *drv) +{ + return drv->bdrv_co_pwritev_compressed || + drv->bdrv_co_pwritev_compressed_part; +} + +typedef struct BlockLimits { + /* + * Alignment requirement, in bytes, for offset/length of I/O + * requests. Must be a power of 2 less than INT_MAX; defaults to + * 1 for drivers with modern byte interfaces, and to 512 + * otherwise. + */ + uint32_t request_alignment; + + /* + * Maximum number of bytes that can be discarded at once. Must be multiple + * of pdiscard_alignment, but need not be power of 2. May be 0 if no + * inherent 64-bit limit. + */ + int64_t max_pdiscard; + + /* + * Optimal alignment for discard requests in bytes. A power of 2 + * is best but not mandatory. Must be a multiple of + * bl.request_alignment, and must be less than max_pdiscard if + * that is set. May be 0 if bl.request_alignment is good enough + */ + uint32_t pdiscard_alignment; + + /* + * Maximum number of bytes that can zeroized at once. Must be multiple of + * pwrite_zeroes_alignment. 0 means no limit. + */ + int64_t max_pwrite_zeroes; + + /* + * Optimal alignment for write zeroes requests in bytes. A power + * of 2 is best but not mandatory. Must be a multiple of + * bl.request_alignment, and must be less than max_pwrite_zeroes + * if that is set. May be 0 if bl.request_alignment is good + * enough + */ + uint32_t pwrite_zeroes_alignment; + + /* + * Optimal transfer length in bytes. A power of 2 is best but not + * mandatory. Must be a multiple of bl.request_alignment, or 0 if + * no preferred size + */ + uint32_t opt_transfer; + + /* + * Maximal transfer length in bytes. Need not be power of 2, but + * must be multiple of opt_transfer and bl.request_alignment, or 0 + * for no 32-bit limit. For now, anything larger than INT_MAX is + * clamped down. + */ + uint32_t max_transfer; + + /* + * Maximal hardware transfer length in bytes. Applies whenever + * transfers to the device bypass the kernel I/O scheduler, for + * example with SG_IO. If larger than max_transfer or if zero, + * blk_get_max_hw_transfer will fall back to max_transfer. + */ + uint64_t max_hw_transfer; + + /* + * Maximal number of scatter/gather elements allowed by the hardware. + * Applies whenever transfers to the device bypass the kernel I/O + * scheduler, for example with SG_IO. If larger than max_iov + * or if zero, blk_get_max_hw_iov will fall back to max_iov. + */ + int max_hw_iov; + + + /* memory alignment, in bytes so that no bounce buffer is needed */ + size_t min_mem_alignment; + + /* memory alignment, in bytes, for bounce buffer */ + size_t opt_mem_alignment; + + /* maximum number of iovec elements */ + int max_iov; +} BlockLimits; + +typedef struct BdrvOpBlocker BdrvOpBlocker; + +typedef struct BdrvAioNotifier { + void (*attached_aio_context)(AioContext *new_context, void *opaque); + void (*detach_aio_context)(void *opaque); + + void *opaque; + bool deleted; + + QLIST_ENTRY(BdrvAioNotifier) list; +} BdrvAioNotifier; + +struct BdrvChildClass { + /* + * If true, bdrv_replace_node() doesn't change the node this BdrvChild + * points to. + */ + bool stay_at_node; + + /* + * If true, the parent is a BlockDriverState and bdrv_next_all_states() + * will return it. This information is used for drain_all, where every node + * will be drained separately, so the drain only needs to be propagated to + * non-BDS parents. + */ + bool parent_is_bds; + + /* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + void (*inherit_options)(BdrvChildRole role, bool parent_is_format, + int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options); + void (*change_media)(BdrvChild *child, bool load); + + /* + * Returns a malloced string that describes the parent of the child for a + * human reader. This could be a node-name, BlockBackend name, qdev ID or + * QOM path of the device owning the BlockBackend, job type and ID etc. The + * caller is responsible for freeing the memory. + */ + char *(*get_parent_desc)(BdrvChild *child); + + /* + * Notifies the parent that the child has been activated/inactivated (e.g. + * when migration is completing) and it can start/stop requesting + * permissions and doing I/O on it. + */ + void (*activate)(BdrvChild *child, Error **errp); + int (*inactivate)(BdrvChild *child); + + void (*attach)(BdrvChild *child); + void (*detach)(BdrvChild *child); + + /* + * Notifies the parent that the filename of its child has changed (e.g. + * because the direct child was removed from the backing chain), so that it + * can update its reference. + */ + int (*update_filename)(BdrvChild *child, BlockDriverState *new_base, + const char *filename, Error **errp); + + bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx, + GSList **ignore, Error **errp); + void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore); + + AioContext *(*get_parent_aio_context)(BdrvChild *child); + + /* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + + void (*resize)(BdrvChild *child); + + /* + * Returns a name that is supposedly more useful for human users than the + * node name for identifying the node in question (in particular, a BB + * name), or NULL if the parent can't provide a better name. + */ + const char *(*get_name)(BdrvChild *child); + + /* + * If this pair of functions is implemented, the parent doesn't issue new + * requests after returning from .drained_begin() until .drained_end() is + * called. + * + * These functions must not change the graph (and therefore also must not + * call aio_poll(), which could change the graph indirectly). + * + * If drained_end() schedules background operations, it must atomically + * increment *drained_end_counter for each such operation and atomically + * decrement it once the operation has settled. + * + * Note that this can be nested. If drained_begin() was called twice, new + * I/O is allowed only after drained_end() was called twice, too. + */ + void (*drained_begin)(BdrvChild *child); + void (*drained_end)(BdrvChild *child, int *drained_end_counter); + + /* + * Returns whether the parent has pending requests for the child. This + * callback is polled after .drained_begin() has been called until all + * activity on the child has stopped. + */ + bool (*drained_poll)(BdrvChild *child); +}; + +extern const BdrvChildClass child_of_bds; + +struct BdrvChild { + BlockDriverState *bs; + char *name; + const BdrvChildClass *klass; + BdrvChildRole role; + void *opaque; + + /** + * Granted permissions for operating on this BdrvChild (BLK_PERM_* bitmask) + */ + uint64_t perm; + + /** + * Permissions that can still be granted to other users of @bs while this + * BdrvChild is still attached to it. (BLK_PERM_* bitmask) + */ + uint64_t shared_perm; + + /* + * This link is frozen: the child can neither be replaced nor + * detached from the parent. + */ + bool frozen; + + /* + * How many times the parent of this child has been drained + * (through klass->drained_*). + * Usually, this is equal to bs->quiesce_counter (potentially + * reduced by bdrv_drain_all_count). It may differ while the + * child is entering or leaving a drained section. + */ + int parent_quiesce_counter; + + QLIST_ENTRY(BdrvChild) next; + QLIST_ENTRY(BdrvChild) next_parent; +}; + +/* + * Allows bdrv_co_block_status() to cache one data region for a + * protocol node. + * + * @valid: Whether the cache is valid (should be accessed with atomic + * functions so this can be reset by RCU readers) + * @data_start: Offset where we know (or strongly assume) is data + * @data_end: Offset where the data region ends (which is not necessarily + * the start of a zeroed region) + */ +typedef struct BdrvBlockStatusCache { + struct rcu_head rcu; + + bool valid; + int64_t data_start; + int64_t data_end; +} BdrvBlockStatusCache; + +struct BlockDriverState { + /* + * Protected by big QEMU lock or read-only after opening. No special + * locking needed during I/O... + */ + int open_flags; /* flags used to open the file, re-used for re-open */ + bool encrypted; /* if true, the media is encrypted */ + bool sg; /* if true, the device is a /dev/sg* */ + bool probed; /* if true, format was probed rather than specified */ + bool force_share; /* if true, always allow all shared permissions */ + bool implicit; /* if true, this filter node was automatically inserted */ + + BlockDriver *drv; /* NULL means no media */ + void *opaque; + + AioContext *aio_context; /* event loop used for fd handlers, timers, etc */ + /* + * long-running tasks intended to always use the same AioContext as this + * BDS may register themselves in this list to be notified of changes + * regarding this BDS's context + */ + QLIST_HEAD(, BdrvAioNotifier) aio_notifiers; + bool walking_aio_notifiers; /* to make removal during iteration safe */ + + char filename[PATH_MAX]; + /* + * If not empty, this image is a diff in relation to backing_file. + * Note that this is the name given in the image header and + * therefore may or may not be equal to .backing->bs->filename. + * If this field contains a relative path, it is to be resolved + * relatively to the overlay's location. + */ + char backing_file[PATH_MAX]; + /* + * The backing filename indicated by the image header. Contrary + * to backing_file, if we ever open this file, auto_backing_file + * is replaced by the resulting BDS's filename (i.e. after a + * bdrv_refresh_filename() run). + */ + char auto_backing_file[PATH_MAX]; + char backing_format[16]; /* if non-zero and backing_file exists */ + + QDict *full_open_options; + char exact_filename[PATH_MAX]; + + BdrvChild *backing; + BdrvChild *file; + + /* I/O Limits */ + BlockLimits bl; + + /* + * Flags honored during pread + */ + unsigned int supported_read_flags; + /* + * Flags honored during pwrite (so far: BDRV_REQ_FUA, + * BDRV_REQ_WRITE_UNCHANGED). + * If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those + * writes will be issued as normal writes without the flag set. + * This is important to note for drivers that do not explicitly + * request a WRITE permission for their children and instead take + * the same permissions as their parent did (this is commonly what + * block filters do). Such drivers have to be aware that the + * parent may have taken a WRITE_UNCHANGED permission only and is + * issuing such requests. Drivers either must make sure that + * these requests do not result in plain WRITE accesses (usually + * by supporting BDRV_REQ_WRITE_UNCHANGED, and then forwarding + * every incoming write request as-is, including potentially that + * flag), or they have to explicitly take the WRITE permission for + * their children. + */ + unsigned int supported_write_flags; + /* + * Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, + * BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) + */ + unsigned int supported_zero_flags; + /* + * Flags honoured during truncate (so far: BDRV_REQ_ZERO_WRITE). + * + * If BDRV_REQ_ZERO_WRITE is given, the truncate operation must make sure + * that any added space reads as all zeros. If this can't be guaranteed, + * the operation must fail. + */ + unsigned int supported_truncate_flags; + + /* the following member gives a name to every node on the bs graph. */ + char node_name[32]; + /* element of the list of named nodes building the graph */ + QTAILQ_ENTRY(BlockDriverState) node_list; + /* element of the list of all BlockDriverStates (all_bdrv_states) */ + QTAILQ_ENTRY(BlockDriverState) bs_list; + /* element of the list of monitor-owned BDS */ + QTAILQ_ENTRY(BlockDriverState) monitor_list; + int refcnt; + + /* operation blockers. Protected by BQL. */ + QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MAX]; + + /* + * The node that this node inherited default options from (and a reopen on + * which can affect this node by changing these defaults). This is always a + * parent node of this node. + */ + BlockDriverState *inherits_from; + QLIST_HEAD(, BdrvChild) children; + QLIST_HEAD(, BdrvChild) parents; + + QDict *options; + QDict *explicit_options; + BlockdevDetectZeroesOptions detect_zeroes; + + /* The error object in use for blocking operations on backing_hd */ + Error *backing_blocker; + + /* Protected by AioContext lock */ + + /* + * If we are reading a disk image, give its size in sectors. + * Generally read-only; it is written to by load_snapshot and + * save_snaphost, but the block layer is quiescent during those. + */ + int64_t total_sectors; + + /* threshold limit for writes, in bytes. "High water mark". */ + uint64_t write_threshold_offset; + + /* + * Writing to the list requires the BQL _and_ the dirty_bitmap_mutex. + * Reading from the list can be done with either the BQL or the + * dirty_bitmap_mutex. Modifying a bitmap only requires + * dirty_bitmap_mutex. + */ + QemuMutex dirty_bitmap_mutex; + QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps; + + /* Offset after the highest byte written to */ + Stat64 wr_highest_offset; + + /* + * If true, copy read backing sectors into image. Can be >1 if more + * than one client has requested copy-on-read. Accessed with atomic + * ops. + */ + int copy_on_read; + + /* + * number of in-flight requests; overall and serialising. + * Accessed with atomic ops. + */ + unsigned int in_flight; + unsigned int serialising_in_flight; + + /* + * counter for nested bdrv_io_plug. + * Accessed with atomic ops. + */ + unsigned io_plugged; + + /* do we need to tell the quest if we have a volatile write cache? */ + int enable_write_cache; + + /* Accessed with atomic ops. */ + int quiesce_counter; + int recursive_quiesce_counter; + + unsigned int write_gen; /* Current data generation */ + + /* Protected by reqs_lock. */ + CoMutex reqs_lock; + QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; + CoQueue flush_queue; /* Serializing flush queue */ + bool active_flush_req; /* Flush request in flight? */ + + /* Only read/written by whoever has set active_flush_req to true. */ + unsigned int flushed_gen; /* Flushed write generation */ + + /* BdrvChild links to this node may never be frozen */ + bool never_freeze; + + /* Lock for block-status cache RCU writers */ + CoMutex bsc_modify_lock; + /* Always non-NULL, but must only be dereferenced under an RCU read guard */ + BdrvBlockStatusCache *block_status_cache; +}; + +struct BlockBackendRootState { + int open_flags; + BlockdevDetectZeroesOptions detect_zeroes; +}; + +typedef enum BlockMirrorBackingMode { + /* + * Reuse the existing backing chain from the source for the target. + * - sync=full: Set backing BDS to NULL. + * - sync=top: Use source's backing BDS. + * - sync=none: Use source as the backing BDS. + */ + MIRROR_SOURCE_BACKING_CHAIN, + + /* Open the target's backing chain completely anew */ + MIRROR_OPEN_BACKING_CHAIN, + + /* Do not change the target's backing BDS after job completion */ + MIRROR_LEAVE_BACKING_CHAIN, +} BlockMirrorBackingMode; + + +/* + * Essential block drivers which must always be statically linked into qemu, and + * which therefore can be accessed without using bdrv_find_format() + */ +extern BlockDriver bdrv_file; +extern BlockDriver bdrv_raw; +extern BlockDriver bdrv_qcow2; + +extern unsigned int bdrv_drain_all_count; +extern QemuOptsList bdrv_create_opts_simple; + +/* + * Common functions that are neither I/O nor Global State. + * + * See include/block/block-commmon.h for more information about + * the Common API. + */ + +static inline BlockDriverState *child_bs(BdrvChild *child) +{ + return child ? child->bs : NULL; +} + +int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp); +int get_tmp_filename(char *filename, int size); +void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, + QDict *options); + + +int bdrv_check_qiov_request(int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + Error **errp); + +#ifdef _WIN32 +int is_windows_drive(const char *filename); +#endif + +#endif /* BLOCK_INT_COMMON_H */ diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h new file mode 100644 index 0000000000..8b2e95f5ff --- /dev/null +++ b/include/block/block_int-global-state.h @@ -0,0 +1,329 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BLOCK_INT_GLOBAL_STATE_H +#define BLOCK_INT_GLOBAL_STATE_H + +#include "block_int-common.h" + +/* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + +/** + * stream_start: + * @job_id: The id of the newly-created job, or %NULL to use the + * device name of @bs. + * @bs: Block device to operate on. + * @base: Block device that will become the new base, or %NULL to + * flatten the whole backing file chain onto @bs. + * @backing_file_str: The file name that will be written to @bs as the + * the new backing file if the job completes. Ignored if @base is %NULL. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags + * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @on_error: The action to take upon error. + * @filter_node_name: The node name that should be assigned to the filter + * driver that the stream job inserts into the graph above + * @bs. NULL means that a node name should be autogenerated. + * @errp: Error object. + * + * Start a streaming operation on @bs. Clusters that are unallocated + * in @bs, but allocated in any image between @base and @bs (both + * exclusive) will be written to @bs. At the end of a successful + * streaming job, the backing file of @bs will be changed to + * @backing_file_str in the written image and to @base in the live + * BlockDriverState. + */ +void stream_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *base, const char *backing_file_str, + BlockDriverState *bottom, + int creation_flags, int64_t speed, + BlockdevOnError on_error, + const char *filter_node_name, + Error **errp); + +/** + * commit_start: + * @job_id: The id of the newly-created job, or %NULL to use the + * device name of @bs. + * @bs: Active block device. + * @top: Top block device to be committed. + * @base: Block device that will be written into, and become the new top. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags + * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @on_error: The action to take upon error. + * @backing_file_str: String to use as the backing file in @top's overlay + * @filter_node_name: The node name that should be assigned to the filter + * driver that the commit job inserts into the graph above @top. NULL means + * that a node name should be autogenerated. + * @errp: Error object. + * + */ +void commit_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *base, BlockDriverState *top, + int creation_flags, int64_t speed, + BlockdevOnError on_error, const char *backing_file_str, + const char *filter_node_name, Error **errp); +/** + * commit_active_start: + * @job_id: The id of the newly-created job, or %NULL to use the + * device name of @bs. + * @bs: Active block device to be committed. + * @base: Block device that will be written into, and become the new top. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags + * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @on_error: The action to take upon error. + * @filter_node_name: The node name that should be assigned to the filter + * driver that the commit job inserts into the graph above @bs. NULL means that + * a node name should be autogenerated. + * @cb: Completion function for the job. + * @opaque: Opaque pointer value passed to @cb. + * @auto_complete: Auto complete the job. + * @errp: Error object. + * + */ +BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *base, int creation_flags, + int64_t speed, BlockdevOnError on_error, + const char *filter_node_name, + BlockCompletionFunc *cb, void *opaque, + bool auto_complete, Error **errp); +/* + * mirror_start: + * @job_id: The id of the newly-created job, or %NULL to use the + * device name of @bs. + * @bs: Block device to operate on. + * @target: Block device to write to. + * @replaces: Block graph node name to replace once the mirror is done. Can + * only be used when full mirroring is selected. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags + * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @granularity: The chosen granularity for the dirty bitmap. + * @buf_size: The amount of data that can be in flight at one time. + * @mode: Whether to collapse all images in the chain to the target. + * @backing_mode: How to establish the target's backing chain after completion. + * @zero_target: Whether the target should be explicitly zero-initialized + * @on_source_error: The action to take upon error reading from the source. + * @on_target_error: The action to take upon error writing to the target. + * @unmap: Whether to unmap target where source sectors only contain zeroes. + * @filter_node_name: The node name that should be assigned to the filter + * driver that the mirror job inserts into the graph above @bs. NULL means that + * a node name should be autogenerated. + * @copy_mode: When to trigger writes to the target. + * @errp: Error object. + * + * Start a mirroring operation on @bs. Clusters that are allocated + * in @bs will be written to @target until the job is cancelled or + * manually completed. At the end of a successful mirroring job, + * @bs will be switched to read from @target. + */ +void mirror_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *target, const char *replaces, + int creation_flags, int64_t speed, + uint32_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, + bool zero_target, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, + bool unmap, const char *filter_node_name, + MirrorCopyMode copy_mode, Error **errp); + +/* + * backup_job_create: + * @job_id: The id of the newly-created job, or %NULL to use the + * device name of @bs. + * @bs: Block device to operate on. + * @target: Block device to write to. + * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @sync_mode: What parts of the disk image should be copied to the destination. + * @sync_bitmap: The dirty bitmap if sync_mode is 'bitmap' or 'incremental' + * @bitmap_mode: The bitmap synchronization policy to use. + * @perf: Performance options. All actual fields assumed to be present, + * all ".has_*" fields are ignored. + * @on_source_error: The action to take upon error reading from the source. + * @on_target_error: The action to take upon error writing to the target. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags + * @cb: Completion function for the job. + * @opaque: Opaque pointer value passed to @cb. + * @txn: Transaction that this job is part of (may be NULL). + * + * Create a backup operation on @bs. Clusters in @bs are written to @target + * until the job is cancelled or manually completed. + */ +BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + BlockDriverState *target, int64_t speed, + MirrorSyncMode sync_mode, + BdrvDirtyBitmap *sync_bitmap, + BitmapSyncMode bitmap_mode, + bool compress, + const char *filter_node_name, + BackupPerf *perf, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, + int creation_flags, + BlockCompletionFunc *cb, void *opaque, + JobTxn *txn, Error **errp); + +BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, + const char *child_name, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + uint64_t perm, uint64_t shared_perm, + void *opaque, Error **errp); +void bdrv_root_unref_child(BdrvChild *child); + +void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, + uint64_t *shared_perm); + +/** + * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use + * bdrv_child_refresh_perms() instead and make the parent's + * .bdrv_child_perm() implementation return the correct values. + */ +int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, + Error **errp); + +/** + * Calls bs->drv->bdrv_child_perm() and updates the child's permission + * masks with the result. + * Drivers should invoke this function whenever an event occurs that + * makes their .bdrv_child_perm() implementation return different + * values than before, but which will not result in the block layer + * automatically refreshing the permissions. + */ +int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); + +bool bdrv_recurse_can_replace(BlockDriverState *bs, + BlockDriverState *to_replace); + +/* + * Default implementation for BlockDriver.bdrv_child_perm() that can + * be used by block filters and image formats, as long as they use the + * child_of_bds child class and set an appropriate BdrvChildRole. + */ +void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, + BdrvChildRole role, BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared); + +void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); +bool blk_dev_has_removable_media(BlockBackend *blk); +void blk_dev_eject_request(BlockBackend *blk, bool force); +bool blk_dev_is_medium_locked(BlockBackend *blk); + +void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); + +void bdrv_set_monitor_owned(BlockDriverState *bs); + +void blockdev_close_all_bdrv_states(void); + +BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); + +/** + * Simple implementation of bdrv_co_create_opts for protocol drivers + * which only support creation via opening a file + * (usually existing raw storage device) + */ +int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, + const char *filename, + QemuOpts *opts, + Error **errp); + +BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, + const char *name, + BlockDriverState **pbs, + Error **errp); +BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, + BlockDirtyBitmapOrStrList *bms, + HBitmap **backup, Error **errp); +BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, + bool release, + BlockDriverState **bitmap_bs, + Error **errp); + + +BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs); + +/** + * bdrv_add_aio_context_notifier: + * + * If a long-running job intends to be always run in the same AioContext as a + * certain BDS, it may use this function to be notified of changes regarding the + * association of the BDS to an AioContext. + * + * attached_aio_context() is called after the target BDS has been attached to a + * new AioContext; detach_aio_context() is called before the target BDS is being + * detached from its old AioContext. + */ +void bdrv_add_aio_context_notifier(BlockDriverState *bs, + void (*attached_aio_context)(AioContext *new_context, void *opaque), + void (*detach_aio_context)(void *opaque), void *opaque); + +/** + * bdrv_remove_aio_context_notifier: + * + * Unsubscribe of change notifications regarding the BDS's AioContext. The + * parameters given here have to be the same as those given to + * bdrv_add_aio_context_notifier(). + */ +void bdrv_remove_aio_context_notifier(BlockDriverState *bs, + void (*aio_context_attached)(AioContext *, + void *), + void (*aio_context_detached)(void *), + void *opaque); + +/** + * End all quiescent sections started by bdrv_drain_all_begin(). This is + * needed when deleting a BDS before bdrv_drain_all_end() is called. + * + * NOTE: this is an internal helper for bdrv_close() *only*. No one else + * should call it. + */ +void bdrv_drain_all_end_quiesce(BlockDriverState *bs); + +/** + * Make sure that the function is running under both drain and BQL. + * The latter protects from concurrent writings + * from the GS API, while the former prevents concurrent reads + * from I/O. + */ +static inline void assert_bdrv_graph_writable(BlockDriverState *bs) +{ + /* + * TODO: this function is incomplete. Because the users of this + * assert lack the necessary drains, check only for BQL. + * Once the necessary drains are added, + * assert also for qatomic_read(&bs->quiesce_counter) > 0 + */ + assert(qemu_in_main_thread()); +} + +#endif /* BLOCK_INT_GLOBAL_STATE */ diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h new file mode 100644 index 0000000000..bb454200e5 --- /dev/null +++ b/include/block/block_int-io.h @@ -0,0 +1,194 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BLOCK_INT_IO_H +#define BLOCK_INT_IO_H + +#include "block_int-common.h" + +/* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + +int coroutine_fn bdrv_co_preadv_snapshot(BdrvChild *child, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); +int coroutine_fn bdrv_co_snapshot_block_status(BlockDriverState *bs, + bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file); +int coroutine_fn bdrv_co_pdiscard_snapshot(BlockDriverState *bs, + int64_t offset, int64_t bytes); + + +int coroutine_fn bdrv_co_preadv(BdrvChild *child, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); +int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); +int coroutine_fn bdrv_co_pwritev(BdrvChild *child, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); +int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, + int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); + +static inline int coroutine_fn bdrv_co_pread(BdrvChild *child, + int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_CODE(); + + return bdrv_co_preadv(child, offset, bytes, &qiov, flags); +} + +static inline int coroutine_fn bdrv_co_pwrite(BdrvChild *child, + int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_CODE(); + + return bdrv_co_pwritev(child, offset, bytes, &qiov, flags); +} + +bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, + uint64_t align); +BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs); + +BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, + const char *filename); + +/** + * bdrv_wakeup: + * @bs: The BlockDriverState for which an I/O operation has been completed. + * + * Wake up the main thread if it is waiting on BDRV_POLL_WHILE. During + * synchronous I/O on a BlockDriverState that is attached to another + * I/O thread, the main thread lets the I/O thread's event loop run, + * waiting for the I/O operation to complete. A bdrv_wakeup will wake + * up the main thread if necessary. + * + * Manual calls to bdrv_wakeup are rarely necessary, because + * bdrv_dec_in_flight already calls it. + */ +void bdrv_wakeup(BlockDriverState *bs); + +const char *bdrv_get_parent_name(const BlockDriverState *bs); +bool blk_dev_has_tray(BlockBackend *blk); +bool blk_dev_is_tray_open(BlockBackend *blk); + +void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); + +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); +bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, + const BdrvDirtyBitmap *src, + HBitmap **backup, bool lock); + +void bdrv_inc_in_flight(BlockDriverState *bs); +void bdrv_dec_in_flight(BlockDriverState *bs); + +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + +int refresh_total_sectors(BlockDriverState *bs, int64_t hint); + +BdrvChild *bdrv_cow_child(BlockDriverState *bs); +BdrvChild *bdrv_filter_child(BlockDriverState *bs); +BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs); +BdrvChild *bdrv_primary_child(BlockDriverState *bs); +BlockDriverState *bdrv_skip_filters(BlockDriverState *bs); +BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs); + +static inline BlockDriverState *bdrv_cow_bs(BlockDriverState *bs) +{ + IO_CODE(); + return child_bs(bdrv_cow_child(bs)); +} + +static inline BlockDriverState *bdrv_filter_bs(BlockDriverState *bs) +{ + IO_CODE(); + return child_bs(bdrv_filter_child(bs)); +} + +static inline BlockDriverState *bdrv_filter_or_cow_bs(BlockDriverState *bs) +{ + IO_CODE(); + return child_bs(bdrv_filter_or_cow_child(bs)); +} + +static inline BlockDriverState *bdrv_primary_bs(BlockDriverState *bs) +{ + IO_CODE(); + return child_bs(bdrv_primary_child(bs)); +} + +/** + * Check whether the given offset is in the cached block-status data + * region. + * + * If it is, and @pnum is not NULL, *pnum is set to + * `bsc.data_end - offset`, i.e. how many bytes, starting from + * @offset, are data (according to the cache). + * Otherwise, *pnum is not touched. + */ +bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum); + +/** + * If [offset, offset + bytes) overlaps with the currently cached + * block-status region, invalidate the cache. + * + * (To be used by I/O paths that cause data regions to be zero or + * holes.) + */ +void bdrv_bsc_invalidate_range(BlockDriverState *bs, + int64_t offset, int64_t bytes); + +/** + * Mark the range [offset, offset + bytes) as a data region. + */ +void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes); + + +/* + * "I/O or GS" API functions. These functions can run without + * the BQL, but only in one specific iothread/main loop. + * + * See include/block/block-io.h for more information about + * the "I/O or GS" API. + */ + +void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); +void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); + +#endif /* BLOCK_INT_IO_H */ diff --git a/include/block/block_int.h b/include/block/block_int.h index 27008cfb22..7d50b6bbd1 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -24,1478 +24,9 @@ #ifndef BLOCK_INT_H #define BLOCK_INT_H -#include "block/accounting.h" -#include "block/block.h" -#include "block/aio-wait.h" -#include "qemu/queue.h" -#include "qemu/coroutine.h" -#include "qemu/stats64.h" -#include "qemu/timer.h" -#include "qemu/hbitmap.h" -#include "block/snapshot.h" -#include "qemu/throttle.h" -#include "qemu/rcu.h" +#include "block_int-global-state.h" +#include "block_int-io.h" -#define BLOCK_FLAG_LAZY_REFCOUNTS 8 - -#define BLOCK_OPT_SIZE "size" -#define BLOCK_OPT_ENCRYPT "encryption" -#define BLOCK_OPT_ENCRYPT_FORMAT "encrypt.format" -#define BLOCK_OPT_COMPAT6 "compat6" -#define BLOCK_OPT_HWVERSION "hwversion" -#define BLOCK_OPT_BACKING_FILE "backing_file" -#define BLOCK_OPT_BACKING_FMT "backing_fmt" -#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" -#define BLOCK_OPT_TABLE_SIZE "table_size" -#define BLOCK_OPT_PREALLOC "preallocation" -#define BLOCK_OPT_SUBFMT "subformat" -#define BLOCK_OPT_COMPAT_LEVEL "compat" -#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts" -#define BLOCK_OPT_ADAPTER_TYPE "adapter_type" -#define BLOCK_OPT_REDUNDANCY "redundancy" -#define BLOCK_OPT_NOCOW "nocow" -#define BLOCK_OPT_EXTENT_SIZE_HINT "extent_size_hint" -#define BLOCK_OPT_OBJECT_SIZE "object_size" -#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" -#define BLOCK_OPT_DATA_FILE "data_file" -#define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" -#define BLOCK_OPT_COMPRESSION_TYPE "compression_type" -#define BLOCK_OPT_EXTL2 "extended_l2" - -#define BLOCK_PROBE_BUF_SIZE 512 - -enum BdrvTrackedRequestType { - BDRV_TRACKED_READ, - BDRV_TRACKED_WRITE, - BDRV_TRACKED_DISCARD, - BDRV_TRACKED_TRUNCATE, -}; - -/* - * That is not quite good that BdrvTrackedRequest structure is public, - * as block/io.c is very careful about incoming offset/bytes being - * correct. Be sure to assert bdrv_check_request() succeeded after any - * modification of BdrvTrackedRequest object out of block/io.c - */ -typedef struct BdrvTrackedRequest { - BlockDriverState *bs; - int64_t offset; - int64_t bytes; - enum BdrvTrackedRequestType type; - - bool serialising; - int64_t overlap_offset; - int64_t overlap_bytes; - - QLIST_ENTRY(BdrvTrackedRequest) list; - Coroutine *co; /* owner, used for deadlock detection */ - CoQueue wait_queue; /* coroutines blocked on this request */ - - struct BdrvTrackedRequest *waiting_for; -} BdrvTrackedRequest; - -int bdrv_check_qiov_request(int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - Error **errp); -int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp); - -struct BlockDriver { - const char *format_name; - int instance_size; - - /* set to true if the BlockDriver is a block filter. Block filters pass - * certain callbacks that refer to data (see block.c) to their bs->file - * or bs->backing (whichever one exists) if the driver doesn't implement - * them. Drivers that do not wish to forward must implement them and return - * -ENOTSUP. - * Note that filters are not allowed to modify data. - * - * Filters generally cannot have more than a single filtered child, - * because the data they present must at all times be the same as - * that on their filtered child. That would be impossible to - * achieve for multiple filtered children. - * (And this filtered child must then be bs->file or bs->backing.) - */ - bool is_filter; - /* - * Set to true if the BlockDriver is a format driver. Format nodes - * generally do not expect their children to be other format nodes - * (except for backing files), and so format probing is disabled - * on those children. - */ - bool is_format; - /* - * Return true if @to_replace can be replaced by a BDS with the - * same data as @bs without it affecting @bs's behavior (that is, - * without it being visible to @bs's parents). - */ - bool (*bdrv_recurse_can_replace)(BlockDriverState *bs, - BlockDriverState *to_replace); - - int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); - int (*bdrv_probe_device)(const char *filename); - - /* Any driver implementing this callback is expected to be able to handle - * NULL file names in its .bdrv_open() implementation */ - void (*bdrv_parse_filename)(const char *filename, QDict *options, Error **errp); - /* Drivers not implementing bdrv_parse_filename nor bdrv_open should have - * this field set to true, except ones that are defined only by their - * child's bs. - * An example of the last type will be the quorum block driver. - */ - bool bdrv_needs_filename; - - /* - * Set if a driver can support backing files. This also implies the - * following semantics: - * - * - Return status 0 of .bdrv_co_block_status means that corresponding - * blocks are not allocated in this layer of backing-chain - * - For such (unallocated) blocks, read will: - * - fill buffer with zeros if there is no backing file - * - read from the backing file otherwise, where the block layer - * takes care of reading zeros beyond EOF if backing file is short - */ - bool supports_backing; - - /* For handling image reopen for split or non-split files */ - int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state, - BlockReopenQueue *queue, Error **errp); - void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); - void (*bdrv_reopen_commit_post)(BDRVReopenState *reopen_state); - void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); - void (*bdrv_join_options)(QDict *options, QDict *old_options); - - int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags, - Error **errp); - - /* Protocol drivers should implement this instead of bdrv_open */ - int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, - Error **errp); - void (*bdrv_close)(BlockDriverState *bs); - - - int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts, - Error **errp); - int coroutine_fn (*bdrv_co_create_opts)(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp); - - int coroutine_fn (*bdrv_co_amend)(BlockDriverState *bs, - BlockdevAmendOptions *opts, - bool force, - Error **errp); - - int (*bdrv_amend_options)(BlockDriverState *bs, - QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, - bool force, - Error **errp); - - int (*bdrv_make_empty)(BlockDriverState *bs); - - /* - * Refreshes the bs->exact_filename field. If that is impossible, - * bs->exact_filename has to be left empty. - */ - void (*bdrv_refresh_filename)(BlockDriverState *bs); - - /* - * Gathers the open options for all children into @target. - * A simple format driver (without backing file support) might - * implement this function like this: - * - * QINCREF(bs->file->bs->full_open_options); - * qdict_put(target, "file", bs->file->bs->full_open_options); - * - * If not specified, the generic implementation will simply put - * all children's options under their respective name. - * - * @backing_overridden is true when bs->backing seems not to be - * the child that would result from opening bs->backing_file. - * Therefore, if it is true, the backing child's options should be - * gathered; otherwise, there is no need since the backing child - * is the one implied by the image header. - * - * Note that ideally this function would not be needed. Every - * block driver which implements it is probably doing something - * shady regarding its runtime option structure. - */ - void (*bdrv_gather_child_options)(BlockDriverState *bs, QDict *target, - bool backing_overridden); - - /* - * Returns an allocated string which is the directory name of this BDS: It - * will be used to make relative filenames absolute by prepending this - * function's return value to them. - */ - char *(*bdrv_dirname)(BlockDriverState *bs, Error **errp); - - /* aio */ - BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, - BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_pdiscard)(BlockDriverState *bs, - int64_t offset, int bytes, - BlockCompletionFunc *cb, void *opaque); - - int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); - - /** - * @offset: position in bytes to read at - * @bytes: number of bytes to read - * @qiov: the buffers to fill with read data - * @flags: currently unused, always 0 - * - * @offset and @bytes will be a multiple of 'request_alignment', - * but the length of individual @qiov elements does not have to - * be a multiple. - * - * @bytes will always equal the total size of @qiov, and will be - * no larger than 'max_transfer'. - * - * The buffer in @qiov may point directly to guest memory. - */ - int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_preadv_part)(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); - /** - * @offset: position in bytes to write at - * @bytes: number of bytes to write - * @qiov: the buffers containing data to write - * @flags: zero or more bits allowed by 'supported_write_flags' - * - * @offset and @bytes will be a multiple of 'request_alignment', - * but the length of individual @qiov elements does not have to - * be a multiple. - * - * @bytes will always equal the total size of @qiov, and will be - * no larger than 'max_transfer'. - * - * The buffer in @qiov may point directly to guest memory. - */ - int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_pwritev_part)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags); - - /* - * Efficiently zero a region of the disk image. Typically an image format - * would use a compact metadata representation to implement this. This - * function pointer may be NULL or return -ENOSUP and .bdrv_co_writev() - * will be called instead. - */ - int coroutine_fn (*bdrv_co_pwrite_zeroes)(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, - int64_t offset, int64_t bytes); - - /* Map [offset, offset + nbytes) range onto a child of @bs to copy from, - * and invoke bdrv_co_copy_range_from(child, ...), or invoke - * bdrv_co_copy_range_to() if @bs is the leaf child to copy data from. - * - * See the comment of bdrv_co_copy_range for the parameter and return value - * semantics. - */ - int coroutine_fn (*bdrv_co_copy_range_from)(BlockDriverState *bs, - BdrvChild *src, - int64_t offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); - - /* Map [offset, offset + nbytes) range onto a child of bs to copy data to, - * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy - * operation if @bs is the leaf and @src has the same BlockDriver. Return - * -ENOTSUP if @bs is the leaf but @src has a different BlockDriver. - * - * See the comment of bdrv_co_copy_range for the parameter and return value - * semantics. - */ - int coroutine_fn (*bdrv_co_copy_range_to)(BlockDriverState *bs, - BdrvChild *src, - int64_t src_offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); - - /* - * Building block for bdrv_block_status[_above] and - * bdrv_is_allocated[_above]. The driver should answer only - * according to the current layer, and should only need to set - * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID, - * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing - * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). See - * block.h for the overall meaning of the bits. As a hint, the - * flag want_zero is true if the caller cares more about precise - * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for - * overall allocation (favor larger *pnum, perhaps by reporting - * _DATA instead of _ZERO). The block layer guarantees input - * clamped to bdrv_getlength() and aligned to request_alignment, - * as well as non-NULL pnum, map, and file; in turn, the driver - * must return an error or set pnum to an aligned non-zero value. - * - * Note that @bytes is just a hint on how big of a region the - * caller wants to inspect. It is not a limit on *pnum. - * Implementations are free to return larger values of *pnum if - * doing so does not incur a performance penalty. - * - * block/io.c's bdrv_co_block_status() will utilize an unclamped - * *pnum value for the block-status cache on protocol nodes, prior - * to clamping *pnum for return to its caller. - */ - int coroutine_fn (*bdrv_co_block_status)(BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, - int64_t *map, BlockDriverState **file); - - /* - * This informs the driver that we are no longer interested in the result - * of in-flight requests, so don't waste the time if possible. - * - * One example usage is to avoid waiting for an nbd target node reconnect - * timeout during job-cancel with force=true. - */ - void (*bdrv_cancel_in_flight)(BlockDriverState *bs); - - /* - * Invalidate any cached meta-data. - */ - void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs, - Error **errp); - int (*bdrv_inactivate)(BlockDriverState *bs); - - /* - * Flushes all data for all layers by calling bdrv_co_flush for underlying - * layers, if needed. This function is needed for deterministic - * synchronization of the flush finishing callback. - */ - int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs); - - /* Delete a created file. */ - int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs, - Error **errp); - - /* - * Flushes all data that was already written to the OS all the way down to - * the disk (for example file-posix.c calls fsync()). - */ - int coroutine_fn (*bdrv_co_flush_to_disk)(BlockDriverState *bs); - - /* - * Flushes all internal caches to the OS. The data may still sit in a - * writeback cache of the host OS, but it will survive a crash of the qemu - * process. - */ - int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs); - - /* - * Drivers setting this field must be able to work with just a plain - * filename with ':' as a prefix, and no other options. - * Options may be extracted from the filename by implementing - * bdrv_parse_filename. - */ - const char *protocol_name; - - /* - * Truncate @bs to @offset bytes using the given @prealloc mode - * when growing. Modes other than PREALLOC_MODE_OFF should be - * rejected when shrinking @bs. - * - * If @exact is true, @bs must be resized to exactly @offset. - * Otherwise, it is sufficient for @bs (if it is a host block - * device and thus there is no way to resize it) to be at least - * @offset bytes in length. - * - * If @exact is true and this function fails but would succeed - * with @exact = false, it should return -ENOTSUP. - */ - int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset, - bool exact, PreallocMode prealloc, - BdrvRequestFlags flags, Error **errp); - - int64_t (*bdrv_getlength)(BlockDriverState *bs); - bool has_variable_length; - int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs); - BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs, - Error **errp); - - int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov); - int coroutine_fn (*bdrv_co_pwritev_compressed_part)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); - - int (*bdrv_snapshot_create)(BlockDriverState *bs, - QEMUSnapshotInfo *sn_info); - int (*bdrv_snapshot_goto)(BlockDriverState *bs, - const char *snapshot_id); - int (*bdrv_snapshot_delete)(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); - int (*bdrv_snapshot_list)(BlockDriverState *bs, - QEMUSnapshotInfo **psn_info); - int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); - int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); - ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs, - Error **errp); - BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs); - - int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs, - QEMUIOVector *qiov, - int64_t pos); - int coroutine_fn (*bdrv_load_vmstate)(BlockDriverState *bs, - QEMUIOVector *qiov, - int64_t pos); - - int (*bdrv_change_backing_file)(BlockDriverState *bs, - const char *backing_file, const char *backing_fmt); - - /* removable device specific */ - bool (*bdrv_is_inserted)(BlockDriverState *bs); - void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag); - void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked); - - /* to control generic scsi devices */ - BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, void *opaque); - int coroutine_fn (*bdrv_co_ioctl)(BlockDriverState *bs, - unsigned long int req, void *buf); - - /* List of options for creating images, terminated by name == NULL */ - QemuOptsList *create_opts; - - /* List of options for image amend */ - QemuOptsList *amend_opts; - - /* - * If this driver supports reopening images this contains a - * NULL-terminated list of the runtime options that can be - * modified. If an option in this list is unspecified during - * reopen then it _must_ be reset to its default value or return - * an error. - */ - const char *const *mutable_opts; - - /* - * Returns 0 for completed check, -errno for internal errors. - * The check results are stored in result. - */ - int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix); - - void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); - - /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ - int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event, - const char *tag); - int (*bdrv_debug_remove_breakpoint)(BlockDriverState *bs, - const char *tag); - int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag); - bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag); - - void (*bdrv_refresh_limits)(BlockDriverState *bs, Error **errp); - - /* - * Returns 1 if newly created images are guaranteed to contain only - * zeros, 0 otherwise. - */ - int (*bdrv_has_zero_init)(BlockDriverState *bs); - - /* Remove fd handlers, timers, and other event loop callbacks so the event - * loop is no longer in use. Called with no in-flight requests and in - * depth-first traversal order with parents before child nodes. - */ - void (*bdrv_detach_aio_context)(BlockDriverState *bs); - - /* Add fd handlers, timers, and other event loop callbacks so I/O requests - * can be processed again. Called with no in-flight requests and in - * depth-first traversal order with child nodes before parent nodes. - */ - void (*bdrv_attach_aio_context)(BlockDriverState *bs, - AioContext *new_context); - - /* io queue for linux-aio */ - void (*bdrv_io_plug)(BlockDriverState *bs); - void (*bdrv_io_unplug)(BlockDriverState *bs); - - /** - * Try to get @bs's logical and physical block size. - * On success, store them in @bsz and return zero. - * On failure, return negative errno. - */ - int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz); - /** - * Try to get @bs's geometry (cyls, heads, sectors) - * On success, store them in @geo and return 0. - * On failure return -errno. - * Only drivers that want to override guest geometry implement this - * callback; see hd_geometry_guess(). - */ - int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); - - /** - * bdrv_co_drain_begin is called if implemented in the beginning of a - * drain operation to drain and stop any internal sources of requests in - * the driver. - * bdrv_co_drain_end is called if implemented at the end of the drain. - * - * They should be used by the driver to e.g. manage scheduled I/O - * requests, or toggle an internal state. After the end of the drain new - * requests will continue normally. - */ - void coroutine_fn (*bdrv_co_drain_begin)(BlockDriverState *bs); - void coroutine_fn (*bdrv_co_drain_end)(BlockDriverState *bs); - - void (*bdrv_add_child)(BlockDriverState *parent, BlockDriverState *child, - Error **errp); - void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child, - Error **errp); - - /** - * Informs the block driver that a permission change is intended. The - * driver checks whether the change is permissible and may take other - * preparations for the change (e.g. get file system locks). This operation - * is always followed either by a call to either .bdrv_set_perm or - * .bdrv_abort_perm_update. - * - * Checks whether the requested set of cumulative permissions in @perm - * can be granted for accessing @bs and whether no other users are using - * permissions other than those given in @shared (both arguments take - * BLK_PERM_* bitmasks). - * - * If both conditions are met, 0 is returned. Otherwise, -errno is returned - * and errp is set to an error describing the conflict. - */ - int (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, - uint64_t shared, Error **errp); - - /** - * Called to inform the driver that the set of cumulative set of used - * permissions for @bs has changed to @perm, and the set of sharable - * permission to @shared. The driver can use this to propagate changes to - * its children (i.e. request permissions only if a parent actually needs - * them). - * - * This function is only invoked after bdrv_check_perm(), so block drivers - * may rely on preparations made in their .bdrv_check_perm implementation. - */ - void (*bdrv_set_perm)(BlockDriverState *bs, uint64_t perm, uint64_t shared); - - /* - * Called to inform the driver that after a previous bdrv_check_perm() - * call, the permission update is not performed and any preparations made - * for it (e.g. taken file locks) need to be undone. - * - * This function can be called even for nodes that never saw a - * bdrv_check_perm() call. It is a no-op then. - */ - void (*bdrv_abort_perm_update)(BlockDriverState *bs); - - /** - * Returns in @nperm and @nshared the permissions that the driver for @bs - * needs on its child @c, based on the cumulative permissions requested by - * the parents in @parent_perm and @parent_shared. - * - * If @c is NULL, return the permissions for attaching a new child for the - * given @child_class and @role. - * - * If @reopen_queue is non-NULL, don't return the currently needed - * permissions, but those that will be needed after applying the - * @reopen_queue. - */ - void (*bdrv_child_perm)(BlockDriverState *bs, BdrvChild *c, - BdrvChildRole role, - BlockReopenQueue *reopen_queue, - uint64_t parent_perm, uint64_t parent_shared, - uint64_t *nperm, uint64_t *nshared); - - bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs); - bool (*bdrv_co_can_store_new_dirty_bitmap)(BlockDriverState *bs, - const char *name, - uint32_t granularity, - Error **errp); - int (*bdrv_co_remove_persistent_dirty_bitmap)(BlockDriverState *bs, - const char *name, - Error **errp); - - /** - * Register/unregister a buffer for I/O. For example, when the driver is - * interested to know the memory areas that will later be used in iovs, so - * that it can do IOMMU mapping with VFIO etc., in order to get better - * performance. In the case of VFIO drivers, this callback is used to do - * DMA mapping for hot buffers. - */ - void (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size); - void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host); - QLIST_ENTRY(BlockDriver) list; - - /* Pointer to a NULL-terminated array of names of strong options - * that can be specified for bdrv_open(). A strong option is one - * that changes the data of a BDS. - * If this pointer is NULL, the array is considered empty. - * "filename" and "driver" are always considered strong. */ - const char *const *strong_runtime_opts; -}; - -static inline bool block_driver_can_compress(BlockDriver *drv) -{ - return drv->bdrv_co_pwritev_compressed || - drv->bdrv_co_pwritev_compressed_part; -} - -typedef struct BlockLimits { - /* Alignment requirement, in bytes, for offset/length of I/O - * requests. Must be a power of 2 less than INT_MAX; defaults to - * 1 for drivers with modern byte interfaces, and to 512 - * otherwise. */ - uint32_t request_alignment; - - /* - * Maximum number of bytes that can be discarded at once. Must be multiple - * of pdiscard_alignment, but need not be power of 2. May be 0 if no - * inherent 64-bit limit. - */ - int64_t max_pdiscard; - - /* Optimal alignment for discard requests in bytes. A power of 2 - * is best but not mandatory. Must be a multiple of - * bl.request_alignment, and must be less than max_pdiscard if - * that is set. May be 0 if bl.request_alignment is good enough */ - uint32_t pdiscard_alignment; - - /* - * Maximum number of bytes that can zeroized at once. Must be multiple of - * pwrite_zeroes_alignment. 0 means no limit. - */ - int64_t max_pwrite_zeroes; - - /* Optimal alignment for write zeroes requests in bytes. A power - * of 2 is best but not mandatory. Must be a multiple of - * bl.request_alignment, and must be less than max_pwrite_zeroes - * if that is set. May be 0 if bl.request_alignment is good - * enough */ - uint32_t pwrite_zeroes_alignment; - - /* Optimal transfer length in bytes. A power of 2 is best but not - * mandatory. Must be a multiple of bl.request_alignment, or 0 if - * no preferred size */ - uint32_t opt_transfer; - - /* Maximal transfer length in bytes. Need not be power of 2, but - * must be multiple of opt_transfer and bl.request_alignment, or 0 - * for no 32-bit limit. For now, anything larger than INT_MAX is - * clamped down. */ - uint32_t max_transfer; - - /* Maximal hardware transfer length in bytes. Applies whenever - * transfers to the device bypass the kernel I/O scheduler, for - * example with SG_IO. If larger than max_transfer or if zero, - * blk_get_max_hw_transfer will fall back to max_transfer. - */ - uint64_t max_hw_transfer; - - /* Maximal number of scatter/gather elements allowed by the hardware. - * Applies whenever transfers to the device bypass the kernel I/O - * scheduler, for example with SG_IO. If larger than max_iov - * or if zero, blk_get_max_hw_iov will fall back to max_iov. - */ - int max_hw_iov; - - /* memory alignment, in bytes so that no bounce buffer is needed */ - size_t min_mem_alignment; - - /* memory alignment, in bytes, for bounce buffer */ - size_t opt_mem_alignment; - - /* maximum number of iovec elements */ - int max_iov; -} BlockLimits; - -typedef struct BdrvOpBlocker BdrvOpBlocker; - -typedef struct BdrvAioNotifier { - void (*attached_aio_context)(AioContext *new_context, void *opaque); - void (*detach_aio_context)(void *opaque); - - void *opaque; - bool deleted; - - QLIST_ENTRY(BdrvAioNotifier) list; -} BdrvAioNotifier; - -struct BdrvChildClass { - /* If true, bdrv_replace_node() doesn't change the node this BdrvChild - * points to. */ - bool stay_at_node; - - /* If true, the parent is a BlockDriverState and bdrv_next_all_states() - * will return it. This information is used for drain_all, where every node - * will be drained separately, so the drain only needs to be propagated to - * non-BDS parents. */ - bool parent_is_bds; - - void (*inherit_options)(BdrvChildRole role, bool parent_is_format, - int *child_flags, QDict *child_options, - int parent_flags, QDict *parent_options); - - void (*change_media)(BdrvChild *child, bool load); - void (*resize)(BdrvChild *child); - - /* Returns a name that is supposedly more useful for human users than the - * node name for identifying the node in question (in particular, a BB - * name), or NULL if the parent can't provide a better name. */ - const char *(*get_name)(BdrvChild *child); - - /* Returns a malloced string that describes the parent of the child for a - * human reader. This could be a node-name, BlockBackend name, qdev ID or - * QOM path of the device owning the BlockBackend, job type and ID etc. The - * caller is responsible for freeing the memory. */ - char *(*get_parent_desc)(BdrvChild *child); - - /* - * If this pair of functions is implemented, the parent doesn't issue new - * requests after returning from .drained_begin() until .drained_end() is - * called. - * - * These functions must not change the graph (and therefore also must not - * call aio_poll(), which could change the graph indirectly). - * - * If drained_end() schedules background operations, it must atomically - * increment *drained_end_counter for each such operation and atomically - * decrement it once the operation has settled. - * - * Note that this can be nested. If drained_begin() was called twice, new - * I/O is allowed only after drained_end() was called twice, too. - */ - void (*drained_begin)(BdrvChild *child); - void (*drained_end)(BdrvChild *child, int *drained_end_counter); - - /* - * Returns whether the parent has pending requests for the child. This - * callback is polled after .drained_begin() has been called until all - * activity on the child has stopped. - */ - bool (*drained_poll)(BdrvChild *child); - - /* Notifies the parent that the child has been activated/inactivated (e.g. - * when migration is completing) and it can start/stop requesting - * permissions and doing I/O on it. */ - void (*activate)(BdrvChild *child, Error **errp); - int (*inactivate)(BdrvChild *child); - - void (*attach)(BdrvChild *child); - void (*detach)(BdrvChild *child); - - /* Notifies the parent that the filename of its child has changed (e.g. - * because the direct child was removed from the backing chain), so that it - * can update its reference. */ - int (*update_filename)(BdrvChild *child, BlockDriverState *new_base, - const char *filename, Error **errp); - - bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx, - GSList **ignore, Error **errp); - void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore); - - AioContext *(*get_parent_aio_context)(BdrvChild *child); -}; - -extern const BdrvChildClass child_of_bds; - -struct BdrvChild { - BlockDriverState *bs; - char *name; - const BdrvChildClass *klass; - BdrvChildRole role; - void *opaque; - - /** - * Granted permissions for operating on this BdrvChild (BLK_PERM_* bitmask) - */ - uint64_t perm; - - /** - * Permissions that can still be granted to other users of @bs while this - * BdrvChild is still attached to it. (BLK_PERM_* bitmask) - */ - uint64_t shared_perm; - - /* - * This link is frozen: the child can neither be replaced nor - * detached from the parent. - */ - bool frozen; - - /* - * How many times the parent of this child has been drained - * (through klass->drained_*). - * Usually, this is equal to bs->quiesce_counter (potentially - * reduced by bdrv_drain_all_count). It may differ while the - * child is entering or leaving a drained section. - */ - int parent_quiesce_counter; - - QLIST_ENTRY(BdrvChild) next; - QLIST_ENTRY(BdrvChild) next_parent; -}; - -/* - * Allows bdrv_co_block_status() to cache one data region for a - * protocol node. - * - * @valid: Whether the cache is valid (should be accessed with atomic - * functions so this can be reset by RCU readers) - * @data_start: Offset where we know (or strongly assume) is data - * @data_end: Offset where the data region ends (which is not necessarily - * the start of a zeroed region) - */ -typedef struct BdrvBlockStatusCache { - struct rcu_head rcu; - - bool valid; - int64_t data_start; - int64_t data_end; -} BdrvBlockStatusCache; - -struct BlockDriverState { - /* Protected by big QEMU lock or read-only after opening. No special - * locking needed during I/O... - */ - int open_flags; /* flags used to open the file, re-used for re-open */ - bool encrypted; /* if true, the media is encrypted */ - bool sg; /* if true, the device is a /dev/sg* */ - bool probed; /* if true, format was probed rather than specified */ - bool force_share; /* if true, always allow all shared permissions */ - bool implicit; /* if true, this filter node was automatically inserted */ - - BlockDriver *drv; /* NULL means no media */ - void *opaque; - - AioContext *aio_context; /* event loop used for fd handlers, timers, etc */ - /* long-running tasks intended to always use the same AioContext as this - * BDS may register themselves in this list to be notified of changes - * regarding this BDS's context */ - QLIST_HEAD(, BdrvAioNotifier) aio_notifiers; - bool walking_aio_notifiers; /* to make removal during iteration safe */ - - char filename[PATH_MAX]; - /* - * If not empty, this image is a diff in relation to backing_file. - * Note that this is the name given in the image header and - * therefore may or may not be equal to .backing->bs->filename. - * If this field contains a relative path, it is to be resolved - * relatively to the overlay's location. - */ - char backing_file[PATH_MAX]; - /* - * The backing filename indicated by the image header. Contrary - * to backing_file, if we ever open this file, auto_backing_file - * is replaced by the resulting BDS's filename (i.e. after a - * bdrv_refresh_filename() run). - */ - char auto_backing_file[PATH_MAX]; - char backing_format[16]; /* if non-zero and backing_file exists */ - - QDict *full_open_options; - char exact_filename[PATH_MAX]; - - BdrvChild *backing; - BdrvChild *file; - - /* I/O Limits */ - BlockLimits bl; - - /* - * Flags honored during pread - */ - unsigned int supported_read_flags; - /* Flags honored during pwrite (so far: BDRV_REQ_FUA, - * BDRV_REQ_WRITE_UNCHANGED). - * If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those - * writes will be issued as normal writes without the flag set. - * This is important to note for drivers that do not explicitly - * request a WRITE permission for their children and instead take - * the same permissions as their parent did (this is commonly what - * block filters do). Such drivers have to be aware that the - * parent may have taken a WRITE_UNCHANGED permission only and is - * issuing such requests. Drivers either must make sure that - * these requests do not result in plain WRITE accesses (usually - * by supporting BDRV_REQ_WRITE_UNCHANGED, and then forwarding - * every incoming write request as-is, including potentially that - * flag), or they have to explicitly take the WRITE permission for - * their children. */ - unsigned int supported_write_flags; - /* Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, - * BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) */ - unsigned int supported_zero_flags; - /* - * Flags honoured during truncate (so far: BDRV_REQ_ZERO_WRITE). - * - * If BDRV_REQ_ZERO_WRITE is given, the truncate operation must make sure - * that any added space reads as all zeros. If this can't be guaranteed, - * the operation must fail. - */ - unsigned int supported_truncate_flags; - - /* the following member gives a name to every node on the bs graph. */ - char node_name[32]; - /* element of the list of named nodes building the graph */ - QTAILQ_ENTRY(BlockDriverState) node_list; - /* element of the list of all BlockDriverStates (all_bdrv_states) */ - QTAILQ_ENTRY(BlockDriverState) bs_list; - /* element of the list of monitor-owned BDS */ - QTAILQ_ENTRY(BlockDriverState) monitor_list; - int refcnt; - - /* operation blockers */ - QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MAX]; - - /* The node that this node inherited default options from (and a reopen on - * which can affect this node by changing these defaults). This is always a - * parent node of this node. */ - BlockDriverState *inherits_from; - QLIST_HEAD(, BdrvChild) children; - QLIST_HEAD(, BdrvChild) parents; - - QDict *options; - QDict *explicit_options; - BlockdevDetectZeroesOptions detect_zeroes; - - /* The error object in use for blocking operations on backing_hd */ - Error *backing_blocker; - - /* Protected by AioContext lock */ - - /* If we are reading a disk image, give its size in sectors. - * Generally read-only; it is written to by load_snapshot and - * save_snaphost, but the block layer is quiescent during those. - */ - int64_t total_sectors; - - /* threshold limit for writes, in bytes. "High water mark". */ - uint64_t write_threshold_offset; - - /* Writing to the list requires the BQL _and_ the dirty_bitmap_mutex. - * Reading from the list can be done with either the BQL or the - * dirty_bitmap_mutex. Modifying a bitmap only requires - * dirty_bitmap_mutex. */ - QemuMutex dirty_bitmap_mutex; - QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps; - - /* Offset after the highest byte written to */ - Stat64 wr_highest_offset; - - /* If true, copy read backing sectors into image. Can be >1 if more - * than one client has requested copy-on-read. Accessed with atomic - * ops. - */ - int copy_on_read; - - /* number of in-flight requests; overall and serialising. - * Accessed with atomic ops. - */ - unsigned int in_flight; - unsigned int serialising_in_flight; - - /* counter for nested bdrv_io_plug. - * Accessed with atomic ops. - */ - unsigned io_plugged; - - /* do we need to tell the quest if we have a volatile write cache? */ - int enable_write_cache; - - /* Accessed with atomic ops. */ - int quiesce_counter; - int recursive_quiesce_counter; - - unsigned int write_gen; /* Current data generation */ - - /* Protected by reqs_lock. */ - CoMutex reqs_lock; - QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; - CoQueue flush_queue; /* Serializing flush queue */ - bool active_flush_req; /* Flush request in flight? */ - - /* Only read/written by whoever has set active_flush_req to true. */ - unsigned int flushed_gen; /* Flushed write generation */ - - /* BdrvChild links to this node may never be frozen */ - bool never_freeze; - - /* Lock for block-status cache RCU writers */ - CoMutex bsc_modify_lock; - /* Always non-NULL, but must only be dereferenced under an RCU read guard */ - BdrvBlockStatusCache *block_status_cache; -}; - -struct BlockBackendRootState { - int open_flags; - BlockdevDetectZeroesOptions detect_zeroes; -}; - -typedef enum BlockMirrorBackingMode { - /* Reuse the existing backing chain from the source for the target. - * - sync=full: Set backing BDS to NULL. - * - sync=top: Use source's backing BDS. - * - sync=none: Use source as the backing BDS. */ - MIRROR_SOURCE_BACKING_CHAIN, - - /* Open the target's backing chain completely anew */ - MIRROR_OPEN_BACKING_CHAIN, - - /* Do not change the target's backing BDS after job completion */ - MIRROR_LEAVE_BACKING_CHAIN, -} BlockMirrorBackingMode; - - -/* Essential block drivers which must always be statically linked into qemu, and - * which therefore can be accessed without using bdrv_find_format() */ -extern BlockDriver bdrv_file; -extern BlockDriver bdrv_raw; -extern BlockDriver bdrv_qcow2; - -int coroutine_fn bdrv_co_preadv(BdrvChild *child, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); -int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); -int coroutine_fn bdrv_co_pwritev(BdrvChild *child, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); -int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); - -static inline int coroutine_fn bdrv_co_pread(BdrvChild *child, - int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - - return bdrv_co_preadv(child, offset, bytes, &qiov, flags); -} - -static inline int coroutine_fn bdrv_co_pwrite(BdrvChild *child, - int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - - return bdrv_co_pwritev(child, offset, bytes, &qiov, flags); -} - -extern unsigned int bdrv_drain_all_count; -void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); -void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); - -bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, - uint64_t align); -BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs); - -int get_tmp_filename(char *filename, int size); -BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, - const char *filename); - -void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, - QDict *options); - -/** - * bdrv_add_aio_context_notifier: - * - * If a long-running job intends to be always run in the same AioContext as a - * certain BDS, it may use this function to be notified of changes regarding the - * association of the BDS to an AioContext. - * - * attached_aio_context() is called after the target BDS has been attached to a - * new AioContext; detach_aio_context() is called before the target BDS is being - * detached from its old AioContext. - */ -void bdrv_add_aio_context_notifier(BlockDriverState *bs, - void (*attached_aio_context)(AioContext *new_context, void *opaque), - void (*detach_aio_context)(void *opaque), void *opaque); - -/** - * bdrv_remove_aio_context_notifier: - * - * Unsubscribe of change notifications regarding the BDS's AioContext. The - * parameters given here have to be the same as those given to - * bdrv_add_aio_context_notifier(). - */ -void bdrv_remove_aio_context_notifier(BlockDriverState *bs, - void (*aio_context_attached)(AioContext *, - void *), - void (*aio_context_detached)(void *), - void *opaque); - -/** - * bdrv_wakeup: - * @bs: The BlockDriverState for which an I/O operation has been completed. - * - * Wake up the main thread if it is waiting on BDRV_POLL_WHILE. During - * synchronous I/O on a BlockDriverState that is attached to another - * I/O thread, the main thread lets the I/O thread's event loop run, - * waiting for the I/O operation to complete. A bdrv_wakeup will wake - * up the main thread if necessary. - * - * Manual calls to bdrv_wakeup are rarely necessary, because - * bdrv_dec_in_flight already calls it. - */ -void bdrv_wakeup(BlockDriverState *bs); - -#ifdef _WIN32 -int is_windows_drive(const char *filename); -#endif - -/** - * stream_start: - * @job_id: The id of the newly-created job, or %NULL to use the - * device name of @bs. - * @bs: Block device to operate on. - * @base: Block device that will become the new base, or %NULL to - * flatten the whole backing file chain onto @bs. - * @backing_file_str: The file name that will be written to @bs as the - * the new backing file if the job completes. Ignored if @base is %NULL. - * @creation_flags: Flags that control the behavior of the Job lifetime. - * See @BlockJobCreateFlags - * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @on_error: The action to take upon error. - * @filter_node_name: The node name that should be assigned to the filter - * driver that the stream job inserts into the graph above - * @bs. NULL means that a node name should be autogenerated. - * @errp: Error object. - * - * Start a streaming operation on @bs. Clusters that are unallocated - * in @bs, but allocated in any image between @base and @bs (both - * exclusive) will be written to @bs. At the end of a successful - * streaming job, the backing file of @bs will be changed to - * @backing_file_str in the written image and to @base in the live - * BlockDriverState. - */ -void stream_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *base, const char *backing_file_str, - BlockDriverState *bottom, - int creation_flags, int64_t speed, - BlockdevOnError on_error, - const char *filter_node_name, - Error **errp); - -/** - * commit_start: - * @job_id: The id of the newly-created job, or %NULL to use the - * device name of @bs. - * @bs: Active block device. - * @top: Top block device to be committed. - * @base: Block device that will be written into, and become the new top. - * @creation_flags: Flags that control the behavior of the Job lifetime. - * See @BlockJobCreateFlags - * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @on_error: The action to take upon error. - * @backing_file_str: String to use as the backing file in @top's overlay - * @filter_node_name: The node name that should be assigned to the filter - * driver that the commit job inserts into the graph above @top. NULL means - * that a node name should be autogenerated. - * @errp: Error object. - * - */ -void commit_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *base, BlockDriverState *top, - int creation_flags, int64_t speed, - BlockdevOnError on_error, const char *backing_file_str, - const char *filter_node_name, Error **errp); -/** - * commit_active_start: - * @job_id: The id of the newly-created job, or %NULL to use the - * device name of @bs. - * @bs: Active block device to be committed. - * @base: Block device that will be written into, and become the new top. - * @creation_flags: Flags that control the behavior of the Job lifetime. - * See @BlockJobCreateFlags - * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @on_error: The action to take upon error. - * @filter_node_name: The node name that should be assigned to the filter - * driver that the commit job inserts into the graph above @bs. NULL means that - * a node name should be autogenerated. - * @cb: Completion function for the job. - * @opaque: Opaque pointer value passed to @cb. - * @auto_complete: Auto complete the job. - * @errp: Error object. - * - */ -BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *base, int creation_flags, - int64_t speed, BlockdevOnError on_error, - const char *filter_node_name, - BlockCompletionFunc *cb, void *opaque, - bool auto_complete, Error **errp); -/* - * mirror_start: - * @job_id: The id of the newly-created job, or %NULL to use the - * device name of @bs. - * @bs: Block device to operate on. - * @target: Block device to write to. - * @replaces: Block graph node name to replace once the mirror is done. Can - * only be used when full mirroring is selected. - * @creation_flags: Flags that control the behavior of the Job lifetime. - * See @BlockJobCreateFlags - * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @granularity: The chosen granularity for the dirty bitmap. - * @buf_size: The amount of data that can be in flight at one time. - * @mode: Whether to collapse all images in the chain to the target. - * @backing_mode: How to establish the target's backing chain after completion. - * @zero_target: Whether the target should be explicitly zero-initialized - * @on_source_error: The action to take upon error reading from the source. - * @on_target_error: The action to take upon error writing to the target. - * @unmap: Whether to unmap target where source sectors only contain zeroes. - * @filter_node_name: The node name that should be assigned to the filter - * driver that the mirror job inserts into the graph above @bs. NULL means that - * a node name should be autogenerated. - * @copy_mode: When to trigger writes to the target. - * @errp: Error object. - * - * Start a mirroring operation on @bs. Clusters that are allocated - * in @bs will be written to @target until the job is cancelled or - * manually completed. At the end of a successful mirroring job, - * @bs will be switched to read from @target. - */ -void mirror_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *target, const char *replaces, - int creation_flags, int64_t speed, - uint32_t granularity, int64_t buf_size, - MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, - BlockdevOnError on_source_error, - BlockdevOnError on_target_error, - bool unmap, const char *filter_node_name, - MirrorCopyMode copy_mode, Error **errp); - -/* - * backup_job_create: - * @job_id: The id of the newly-created job, or %NULL to use the - * device name of @bs. - * @bs: Block device to operate on. - * @target: Block device to write to. - * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @sync_mode: What parts of the disk image should be copied to the destination. - * @sync_bitmap: The dirty bitmap if sync_mode is 'bitmap' or 'incremental' - * @bitmap_mode: The bitmap synchronization policy to use. - * @perf: Performance options. All actual fields assumed to be present, - * all ".has_*" fields are ignored. - * @on_source_error: The action to take upon error reading from the source. - * @on_target_error: The action to take upon error writing to the target. - * @creation_flags: Flags that control the behavior of the Job lifetime. - * See @BlockJobCreateFlags - * @cb: Completion function for the job. - * @opaque: Opaque pointer value passed to @cb. - * @txn: Transaction that this job is part of (may be NULL). - * - * Create a backup operation on @bs. Clusters in @bs are written to @target - * until the job is cancelled or manually completed. - */ -BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - BlockDriverState *target, int64_t speed, - MirrorSyncMode sync_mode, - BdrvDirtyBitmap *sync_bitmap, - BitmapSyncMode bitmap_mode, - bool compress, - const char *filter_node_name, - BackupPerf *perf, - BlockdevOnError on_source_error, - BlockdevOnError on_target_error, - int creation_flags, - BlockCompletionFunc *cb, void *opaque, - JobTxn *txn, Error **errp); - -BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, - const char *child_name, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - uint64_t perm, uint64_t shared_perm, - void *opaque, Error **errp); -void bdrv_root_unref_child(BdrvChild *child); - -void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, - uint64_t *shared_perm); - -/** - * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use - * bdrv_child_refresh_perms() instead and make the parent's - * .bdrv_child_perm() implementation return the correct values. - */ -int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, - Error **errp); - -/** - * Calls bs->drv->bdrv_child_perm() and updates the child's permission - * masks with the result. - * Drivers should invoke this function whenever an event occurs that - * makes their .bdrv_child_perm() implementation return different - * values than before, but which will not result in the block layer - * automatically refreshing the permissions. - */ -int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); - -bool bdrv_recurse_can_replace(BlockDriverState *bs, - BlockDriverState *to_replace); - -/* - * Default implementation for BlockDriver.bdrv_child_perm() that can - * be used by block filters and image formats, as long as they use the - * child_of_bds child class and set an appropriate BdrvChildRole. - */ -void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, - BdrvChildRole role, BlockReopenQueue *reopen_queue, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared); - -const char *bdrv_get_parent_name(const BlockDriverState *bs); -void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); -bool blk_dev_has_removable_media(BlockBackend *blk); -bool blk_dev_has_tray(BlockBackend *blk); -void blk_dev_eject_request(BlockBackend *blk, bool force); -bool blk_dev_is_tray_open(BlockBackend *blk); -bool blk_dev_is_medium_locked(BlockBackend *blk); - -void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); - -void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); -void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); -bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, - const BdrvDirtyBitmap *src, - HBitmap **backup, bool lock); - -void bdrv_inc_in_flight(BlockDriverState *bs); -void bdrv_dec_in_flight(BlockDriverState *bs); - -void blockdev_close_all_bdrv_states(void); - -int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, - BdrvChild *dst, int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); -int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, - BdrvChild *dst, int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); - -int refresh_total_sectors(BlockDriverState *bs, int64_t hint); - -void bdrv_set_monitor_owned(BlockDriverState *bs); -BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); - -/** - * Simple implementation of bdrv_co_create_opts for protocol drivers - * which only support creation via opening a file - * (usually existing raw storage device) - */ -int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp); -extern QemuOptsList bdrv_create_opts_simple; - -BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, - const char *name, - BlockDriverState **pbs, - Error **errp); -BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, - BlockDirtyBitmapMergeSourceList *bms, - HBitmap **backup, Error **errp); -BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, - bool release, - BlockDriverState **bitmap_bs, - Error **errp); - -BdrvChild *bdrv_cow_child(BlockDriverState *bs); -BdrvChild *bdrv_filter_child(BlockDriverState *bs); -BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs); -BdrvChild *bdrv_primary_child(BlockDriverState *bs); -BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs); -BlockDriverState *bdrv_skip_filters(BlockDriverState *bs); -BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs); - -static inline BlockDriverState *child_bs(BdrvChild *child) -{ - return child ? child->bs : NULL; -} - -static inline BlockDriverState *bdrv_cow_bs(BlockDriverState *bs) -{ - return child_bs(bdrv_cow_child(bs)); -} - -static inline BlockDriverState *bdrv_filter_bs(BlockDriverState *bs) -{ - return child_bs(bdrv_filter_child(bs)); -} - -static inline BlockDriverState *bdrv_filter_or_cow_bs(BlockDriverState *bs) -{ - return child_bs(bdrv_filter_or_cow_child(bs)); -} - -static inline BlockDriverState *bdrv_primary_bs(BlockDriverState *bs) -{ - return child_bs(bdrv_primary_child(bs)); -} - -/** - * End all quiescent sections started by bdrv_drain_all_begin(). This is - * needed when deleting a BDS before bdrv_drain_all_end() is called. - * - * NOTE: this is an internal helper for bdrv_close() *only*. No one else - * should call it. - */ -void bdrv_drain_all_end_quiesce(BlockDriverState *bs); - -/** - * Check whether the given offset is in the cached block-status data - * region. - * - * If it is, and @pnum is not NULL, *pnum is set to - * `bsc.data_end - offset`, i.e. how many bytes, starting from - * @offset, are data (according to the cache). - * Otherwise, *pnum is not touched. - */ -bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum); - -/** - * If [offset, offset + bytes) overlaps with the currently cached - * block-status region, invalidate the cache. - * - * (To be used by I/O paths that cause data regions to be zero or - * holes.) - */ -void bdrv_bsc_invalidate_range(BlockDriverState *bs, - int64_t offset, int64_t bytes); - -/** - * Mark the range [offset, offset + bytes) as a data region. - */ -void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes); +/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ #endif /* BLOCK_INT_H */ diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 87fbb3985f..6525e16fd5 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -74,6 +74,13 @@ typedef struct BlockJob { GSList *nodes; } BlockJob; +/* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + /** * block_job_next: * @job: A block job, or %NULL. @@ -155,6 +162,21 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp); */ void block_job_iostatus_reset(BlockJob *job); +/* + * block_job_get_aio_context: + * + * Returns aio context associated with a block job. + */ +AioContext *block_job_get_aio_context(BlockJob *job); + + +/* + * Common functions that are neither I/O nor Global State. + * + * See include/block/block-common.h for more information about + * the Common API. + */ + /** * block_job_is_internal: * @job: The job to determine if it is user-visible or not. @@ -170,11 +192,4 @@ bool block_job_is_internal(BlockJob *job); */ const BlockJobDriver *block_job_driver(BlockJob *job); -/* - * block_job_get_aio_context: - * - * Returns aio context associated with a block job. - */ -AioContext *block_job_get_aio_context(BlockJob *job); - #endif diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 6633d83da2..6bd9ae2b20 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -38,6 +38,13 @@ struct BlockJobDriver { /** Generic JobDriver callbacks and settings */ JobDriver job_driver; + /* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + /* * Returns whether the job has pending requests for the child or will * submit new requests before the next pause point. This callback is polled @@ -46,6 +53,13 @@ struct BlockJobDriver { */ bool (*drained_poll)(BlockJob *job); + /* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + /* * If the callback is not NULL, it will be invoked before the job is * resumed in a new AioContext. This is the place to move any resources @@ -56,6 +70,13 @@ struct BlockJobDriver { void (*set_speed)(BlockJob *job, int64_t speed); }; +/* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + /** * block_job_create: * @job_id: The id of the newly-created job, or %NULL to have one @@ -98,6 +119,13 @@ void block_job_free(Job *job); */ void block_job_user_resume(Job *job); +/* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + /** * block_job_ratelimit_get_delay: * diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 40950ae3d5..6528336c4c 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -77,7 +77,7 @@ void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent); void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy); -void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, +bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, HBitmap **backup, Error **errp); void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip); bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset); @@ -115,6 +115,8 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, int64_t start, int64_t end, int64_t max_dirty_count, int64_t *dirty_start, int64_t *dirty_count); +bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes, int64_t *count); BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, Error **errp); diff --git a/include/block/nbd.h b/include/block/nbd.h index 78d101b774..a98eb665da 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -415,7 +415,8 @@ NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, bool do_negotiation, const char *export_name, const char *x_dirty_bitmap, - QCryptoTLSCreds *tlscreds); + QCryptoTLSCreds *tlscreds, + const char *tlshostname); void nbd_client_connection_release(NBDClientConnection *conn); QIOChannel *coroutine_fn diff --git a/include/block/nvme.h b/include/block/nvme.h index cd068ac891..3737351cc8 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -695,7 +695,8 @@ typedef struct QEMU_PACKED NvmeRwCmd { uint8_t flags; uint16_t cid; uint32_t nsid; - uint64_t rsvd2; + uint32_t cdw2; + uint32_t cdw3; uint64_t mptr; NvmeCmdDptr dptr; uint64_t slba; @@ -731,7 +732,6 @@ enum { NVME_RW_PRINFO_PRCHK_APP = 1 << 11, NVME_RW_PRINFO_PRCHK_REF = 1 << 10, NVME_RW_PRINFO_PRCHK_MASK = 7 << 10, - }; #define NVME_RW_PRINFO(control) ((control >> 10) & 0xf) @@ -770,6 +770,7 @@ typedef struct QEMU_PACKED NvmeDsmRange { enum { NVME_COPY_FORMAT_0 = 0x0, + NVME_COPY_FORMAT_1 = 0x1, }; typedef struct QEMU_PACKED NvmeCopyCmd { @@ -777,7 +778,9 @@ typedef struct QEMU_PACKED NvmeCopyCmd { uint8_t flags; uint16_t cid; uint32_t nsid; - uint32_t rsvd2[4]; + uint32_t cdw2; + uint32_t cdw3; + uint32_t rsvd2[2]; NvmeCmdDptr dptr; uint64_t sdlba; uint8_t nr; @@ -789,7 +792,7 @@ typedef struct QEMU_PACKED NvmeCopyCmd { uint16_t appmask; } NvmeCopyCmd; -typedef struct QEMU_PACKED NvmeCopySourceRange { +typedef struct QEMU_PACKED NvmeCopySourceRangeFormat0 { uint8_t rsvd0[8]; uint64_t slba; uint16_t nlb; @@ -797,7 +800,17 @@ typedef struct QEMU_PACKED NvmeCopySourceRange { uint32_t reftag; uint16_t apptag; uint16_t appmask; -} NvmeCopySourceRange; +} NvmeCopySourceRangeFormat0; + +typedef struct QEMU_PACKED NvmeCopySourceRangeFormat1 { + uint8_t rsvd0[8]; + uint64_t slba; + uint16_t nlb; + uint8_t rsvd18[8]; + uint8_t sr[10]; + uint16_t apptag; + uint16_t appmask; +} NvmeCopySourceRangeFormat1; enum NvmeAsyncEventRequest { NVME_AER_TYPE_ERROR = 0, @@ -908,6 +921,7 @@ enum NvmeStatusCodes { NVME_CMP_FAILURE = 0x0285, NVME_ACCESS_DENIED = 0x0286, NVME_DULB = 0x0287, + NVME_E2E_STORAGE_TAG_ERROR = 0x0288, NVME_MORE = 0x2000, NVME_DNR = 0x4000, NVME_NO_COMPLETE = 0xffff, @@ -1111,6 +1125,10 @@ enum NvmeIdCtrlOaes { NVME_OAES_NS_ATTR = 1 << 8, }; +enum NvmeIdCtrlCtratt { + NVME_CTRATT_ELBAS = 1 << 15, +}; + enum NvmeIdCtrlOacs { NVME_OACS_SECURITY = 1 << 0, NVME_OACS_FORMAT = 1 << 1, @@ -1131,7 +1149,8 @@ enum NvmeIdCtrlOncs { }; enum NvmeIdCtrlOcfs { - NVME_OCFS_COPY_FORMAT_0 = 1 << 0, + NVME_OCFS_COPY_FORMAT_0 = 1 << NVME_COPY_FORMAT_0, + NVME_OCFS_COPY_FORMAT_1 = 1 << NVME_COPY_FORMAT_1, }; enum NvmeIdctrlVwc { @@ -1216,6 +1235,7 @@ enum NvmeFeatureIds { NVME_WRITE_ATOMICITY = 0xa, NVME_ASYNCHRONOUS_EVENT_CONF = 0xb, NVME_TIMESTAMP = 0xe, + NVME_HOST_BEHAVIOR_SUPPORT = 0x16, NVME_COMMAND_SET_PROFILE = 0x19, NVME_SOFTWARE_PROGRESS_MARKER = 0x80, NVME_FID_MAX = 0x100, @@ -1257,6 +1277,13 @@ typedef struct QEMU_PACKED NvmeRangeType { uint8_t rsvd48[16]; } NvmeRangeType; +typedef struct NvmeHostBehaviorSupport { + uint8_t acre; + uint8_t etdas; + uint8_t lbafee; + uint8_t rsvd3[509]; +} NvmeHostBehaviorSupport; + typedef struct QEMU_PACKED NvmeLBAF { uint16_t ms; uint8_t ds; @@ -1270,6 +1297,7 @@ typedef struct QEMU_PACKED NvmeLBAFE { } NvmeLBAFE; #define NVME_NSID_BROADCAST 0xffffffff +#define NVME_MAX_NLBAF 64 typedef struct QEMU_PACKED NvmeIdNs { uint64_t nsze; @@ -1304,11 +1332,20 @@ typedef struct QEMU_PACKED NvmeIdNs { uint8_t rsvd81[23]; uint8_t nguid[16]; uint64_t eui64; - NvmeLBAF lbaf[16]; - uint8_t rsvd192[192]; + NvmeLBAF lbaf[NVME_MAX_NLBAF]; uint8_t vs[3712]; } NvmeIdNs; +#define NVME_ID_NS_NVM_ELBAF_PIF(elbaf) (((elbaf) >> 7) & 0x3) + +typedef struct QEMU_PACKED NvmeIdNsNvm { + uint64_t lbstm; + uint8_t pic; + uint8_t rsvd9[3]; + uint32_t elbaf[NVME_MAX_NLBAF]; + uint8_t rsvd268[3828]; +} NvmeIdNsNvm; + typedef struct QEMU_PACKED NvmeIdNsDescr { uint8_t nidt; uint8_t nidl; @@ -1410,10 +1447,23 @@ enum NvmeIdNsMc { #define NVME_ID_NS_DPS_TYPE(dps) (dps & NVME_ID_NS_DPS_TYPE_MASK) -typedef struct NvmeDifTuple { - uint16_t guard; - uint16_t apptag; - uint32_t reftag; +enum NvmePIFormat { + NVME_PI_GUARD_16 = 0, + NVME_PI_GUARD_64 = 2, +}; + +typedef union NvmeDifTuple { + struct { + uint16_t guard; + uint16_t apptag; + uint32_t reftag; + } g16; + + struct { + uint64_t guard; + uint16_t apptag; + uint8_t sr[6]; + } g64; } NvmeDifTuple; enum NvmeZoneAttr { @@ -1510,7 +1560,8 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeZonedResult) != 8); QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16); - QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRange) != 32); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat0) != 32); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat1) != 40); QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64); @@ -1520,6 +1571,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeDsmCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeCopyCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeRangeType) != 64); + QEMU_BUILD_BUG_ON(sizeof(NvmeHostBehaviorSupport) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512); @@ -1530,10 +1582,11 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeLBAF) != 4); QEMU_BUILD_BUG_ON(sizeof(NvmeLBAFE) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) != 4096); + QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsNvm) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsZoned) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeSglDescriptor) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsDescr) != 4); QEMU_BUILD_BUG_ON(sizeof(NvmeZoneDescr) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeDifTuple) != 8); + QEMU_BUILD_BUG_ON(sizeof(NvmeDifTuple) != 16); } #endif diff --git a/include/block/qdict.h b/include/block/qdict.h index ced2acfb92..b4c28d96a9 100644 --- a/include/block/qdict.h +++ b/include/block/qdict.h @@ -12,6 +12,9 @@ #include "qapi/qmp/qdict.h" +QObject *qdict_crumple(const QDict *src, Error **errp); +void qdict_flatten(QDict *qdict); + void qdict_copy_default(QDict *dst, QDict *src, const char *key); void qdict_set_default_str(QDict *dst, const char *key, const char *val); diff --git a/include/block/reqlist.h b/include/block/reqlist.h new file mode 100644 index 0000000000..5253497bae --- /dev/null +++ b/include/block/reqlist.h @@ -0,0 +1,75 @@ +/* + * reqlist API + * + * Copyright (C) 2013 Proxmox Server Solutions + * Copyright (c) 2021 Virtuozzo International GmbH. + * + * Authors: + * Dietmar Maurer (dietmar@proxmox.com) + * Vladimir Sementsov-Ogievskiy + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef REQLIST_H +#define REQLIST_H + +#include "qemu/coroutine.h" + +/* + * The API is not thread-safe and shouldn't be. The struct is public to be part + * of other structures and protected by third-party locks, see + * block/block-copy.c for example. + */ + +typedef struct BlockReq { + int64_t offset; + int64_t bytes; + + CoQueue wait_queue; /* coroutines blocked on this req */ + QLIST_ENTRY(BlockReq) list; +} BlockReq; + +typedef QLIST_HEAD(, BlockReq) BlockReqList; + +/* + * Initialize new request and add it to the list. Caller must be sure that + * there are no conflicting requests in the list. + */ +void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset, + int64_t bytes); +/* Search for request in the list intersecting with @offset/@bytes area. */ +BlockReq *reqlist_find_conflict(BlockReqList *reqs, int64_t offset, + int64_t bytes); + +/* + * If there are no intersecting requests return false. Otherwise, wait for the + * first found intersecting request to finish and return true. + * + * @lock is passed to qemu_co_queue_wait() + * False return value proves that lock was released at no point. + */ +bool coroutine_fn reqlist_wait_one(BlockReqList *reqs, int64_t offset, + int64_t bytes, CoMutex *lock); + +/* + * Wait for all intersecting requests. It just calls reqlist_wait_one() in a + * loop, caller is responsible to stop producing new requests in this region + * in parallel, otherwise reqlist_wait_all() may never return. + */ +void coroutine_fn reqlist_wait_all(BlockReqList *reqs, int64_t offset, + int64_t bytes, CoMutex *lock); + +/* + * Shrink request and wake all waiting coroutines (maybe some of them are not + * intersecting with shrunk request). + */ +void coroutine_fn reqlist_shrink_req(BlockReq *req, int64_t new_bytes); + +/* + * Remove request and wake all waiting coroutines. Do not release any memory. + */ +void coroutine_fn reqlist_remove_req(BlockReq *req); + +#endif /* REQLIST_H */ diff --git a/include/block/snapshot.h b/include/block/snapshot.h index 940345692f..50ff924710 100644 --- a/include/block/snapshot.h +++ b/include/block/snapshot.h @@ -45,6 +45,13 @@ typedef struct QEMUSnapshotInfo { uint64_t icount; /* record/replay step */ } QEMUSnapshotInfo; +/* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, const char *name); bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, @@ -73,9 +80,11 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, Error **errp); -/* Group operations. All block drivers are involved. +/* + * Group operations. All block drivers are involved. * These functions will properly handle dataplane (take aio_context_acquire - * when appropriate for appropriate block drivers */ + * when appropriate for appropriate block drivers + */ bool bdrv_all_can_snapshot(bool has_devices, strList *devices, Error **errp); diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 867ef1b3b2..8c420fa36e 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -172,7 +172,7 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open); * Chardev. */ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); typedef gboolean (*FEWatchFunc)(void *do_not_use, GIOCondition condition, void *data); diff --git a/include/crypto/sm4.h b/include/crypto/sm4.h new file mode 100644 index 0000000000..9bd3ebc62e --- /dev/null +++ b/include/crypto/sm4.h @@ -0,0 +1,6 @@ +#ifndef QEMU_SM4_H +#define QEMU_SM4_H + +extern const uint8_t sm4_sbox[256]; + +#endif diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 102a1e7f50..aebd23dc20 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -269,7 +269,7 @@ typedef struct symbol_cache_entry } asymbol; typedef int (*fprintf_function)(FILE *f, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); enum dis_insn_type { dis_noninsn, /* Not a valid instruction */ @@ -450,7 +450,6 @@ int print_insn_d10v (bfd_vma, disassemble_info*); int print_insn_v850 (bfd_vma, disassemble_info*); int print_insn_tic30 (bfd_vma, disassemble_info*); int print_insn_ppc (bfd_vma, disassemble_info*); -int print_insn_s390 (bfd_vma, disassemble_info*); int print_insn_crisv32 (bfd_vma, disassemble_info*); int print_insn_crisv10 (bfd_vma, disassemble_info*); int print_insn_microblaze (bfd_vma, disassemble_info*); diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 84caf5c3d9..5d5290deb5 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -34,13 +34,13 @@ /* some important defines: * - * HOST_WORDS_BIGENDIAN : if defined, the host cpu is big endian and + * HOST_BIG_ENDIAN : whether the host cpu is big endian and * otherwise little endian. * - * TARGET_WORDS_BIGENDIAN : same for target cpu + * TARGET_BIG_ENDIAN : same for the target cpu */ -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN #define BSWAP_NEEDED #endif @@ -120,7 +120,7 @@ static inline void tswap64s(uint64_t *s) /* Target-endianness CPU memory access functions. These fit into the * {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h. */ -#if defined(TARGET_WORDS_BIGENDIAN) +#if TARGET_BIG_ENDIAN #define lduw_p(p) lduw_be_p(p) #define ldsw_p(p) ldsw_be_p(p) #define ldl_p(p) ldl_be_p(p) @@ -433,10 +433,6 @@ int cpu_exec(CPUState *cpu); void tcg_exec_realizefn(CPUState *cpu, Error **errp); void tcg_exec_unrealizefn(CPUState *cpu); -/* Returns: 0 on success, -1 on error */ -int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - void *ptr, target_ulong len, bool is_write); - /** * cpu_set_cpustate_pointers(cpu) * @cpu: The cpu object diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index de5f444b19..5968551a05 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -7,6 +7,21 @@ #include "exec/hwaddr.h" #endif +/** + * vaddr: + * Type wide enough to contain any #target_ulong virtual address. + */ +typedef uint64_t vaddr; +#define VADDR_PRId PRId64 +#define VADDR_PRIu PRIu64 +#define VADDR_PRIo PRIo64 +#define VADDR_PRIx PRIx64 +#define VADDR_PRIX PRIX64 +#define VADDR_MAX UINT64_MAX + +void cpu_exec_init_all(void); +void cpu_exec_step_atomic(CPUState *cpu); + /* Using intptr_t ensures that qemu_*_page_mask is sign-extended even * when intptr_t is 32-bit and we are aligning a long long. */ @@ -14,7 +29,7 @@ extern uintptr_t qemu_host_page_size; extern intptr_t qemu_host_page_mask; #define HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_host_page_size) -#define REAL_HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_real_host_page_size) +#define REAL_HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_real_host_page_size()) /* The CPU list lock nests outside page_(un)lock or mmap_(un)lock */ void qemu_init_cpu_list(void); @@ -34,7 +49,7 @@ enum device_endian { DEVICE_LITTLE_ENDIAN, }; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define DEVICE_HOST_ENDIAN DEVICE_BIG_ENDIAN #else #define DEVICE_HOST_ENDIAN DEVICE_LITTLE_ENDIAN @@ -78,6 +93,28 @@ void qemu_ram_unset_migratable(RAMBlock *rb); size_t qemu_ram_pagesize(RAMBlock *block); size_t qemu_ram_pagesize_largest(void); +/** + * cpu_address_space_init: + * @cpu: CPU to add this address space to + * @asidx: integer index of this address space + * @prefix: prefix to be used as name of address space + * @mr: the root memory region of address space + * + * Add the specified address space to the CPU's cpu_ases list. + * The address space added with @asidx 0 is the one used for the + * convenience pointer cpu->as. + * The target-specific code which registers ASes is responsible + * for defining what semantics address space 0, 1, 2, etc have. + * + * Before the first call to this function, the caller must set + * cpu->num_ases to the total number of address spaces it needs + * to support. + * + * Note that with KVM only one address space is supported. + */ +void cpu_address_space_init(CPUState *cpu, int asidx, + const char *prefix, MemoryRegion *mr); + void cpu_physical_memory_rw(hwaddr addr, void *buf, hwaddr len, bool is_write); static inline void cpu_physical_memory_read(hwaddr addr, @@ -90,6 +127,7 @@ static inline void cpu_physical_memory_write(hwaddr addr, { cpu_physical_memory_rw(addr, (void *)buf, len, true); } +void cpu_reloading_memory_map(void); void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, bool is_write); @@ -116,7 +154,13 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); #endif +/* Returns: 0 on success, -1 on error */ +int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, + void *ptr, size_t len, bool is_write); + /* vl.c */ extern int singlestep; +void list_cpus(const char *optarg); + #endif /* CPU_COMMON_H */ diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index da987fe8ad..d0c7c0d5fe 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -64,6 +64,7 @@ #include "exec/memopidx.h" #include "qemu/int128.h" +#include "cpu.h" #if defined(CONFIG_USER_ONLY) /* sparc32plus has 64bit long but 32bit space address @@ -376,7 +377,7 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, #endif /* defined(CONFIG_USER_ONLY) */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data # define cpu_ldsw_data cpu_ldsw_be_data # define cpu_ldl_data cpu_ldl_be_data diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 227e10ba56..311e5fb422 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -24,7 +24,6 @@ #ifdef CONFIG_TCG #include "exec/cpu_ldst.h" #endif -#include "sysemu/cpu-timers.h" /* allow to see translation results - the slowdown should be negligible, so we leave it */ #define DEBUG_DISAS @@ -59,10 +58,10 @@ void restore_state_to_opc(CPUArchState *env, TranslationBlock *tb, */ bool cpu_restore_state(CPUState *cpu, uintptr_t searched_pc, bool will_exit); -void QEMU_NORETURN cpu_loop_exit_noexc(CPUState *cpu); -void QEMU_NORETURN cpu_loop_exit(CPUState *cpu); -void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); -void QEMU_NORETURN cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); +G_NORETURN void cpu_loop_exit_noexc(CPUState *cpu); +G_NORETURN void cpu_loop_exit(CPUState *cpu); +G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); +G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); /** * cpu_loop_exit_requested: @@ -81,31 +80,6 @@ static inline bool cpu_loop_exit_requested(CPUState *cpu) return (int32_t)qatomic_read(&cpu_neg(cpu)->icount_decr.u32) < 0; } -#if !defined(CONFIG_USER_ONLY) -void cpu_reloading_memory_map(void); -/** - * cpu_address_space_init: - * @cpu: CPU to add this address space to - * @asidx: integer index of this address space - * @prefix: prefix to be used as name of address space - * @mr: the root memory region of address space - * - * Add the specified address space to the CPU's cpu_ases list. - * The address space added with @asidx 0 is the one used for the - * convenience pointer cpu->as. - * The target-specific code which registers ASes is responsible - * for defining what semantics address space 0, 1, 2, etc have. - * - * Before the first call to this function, the caller must set - * cpu->num_ases to the total number of address spaces it needs - * to support. - * - * Note that with KVM only one address space is supported. - */ -void cpu_address_space_init(CPUState *cpu, int asidx, - const char *prefix, MemoryRegion *mr); -#endif - #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) /* cputlb.c */ /** @@ -695,9 +669,9 @@ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, * Use the TCGCPUOps hook to record cpu state, do guest operating system * specific things to raise SIGSEGV, and jump to the main cpu loop. */ -void QEMU_NORETURN cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); +G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); /** * cpu_loop_exit_sigbus: @@ -709,9 +683,9 @@ void QEMU_NORETURN cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, * Use the TCGCPUOps hook to record cpu state, do guest operating system * specific things to raise SIGBUS, and jump to the main cpu loop. */ -void QEMU_NORETURN cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, - MMUAccessType access_type, - uintptr_t ra); +G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, + MMUAccessType access_type, + uintptr_t ra); #else static inline void mmap_lock(void) {} diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index a024a0350d..c35d7334b4 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -45,17 +45,6 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...); */ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va); int use_gdb_syscalls(void); -void gdb_set_stop_cpu(CPUState *cpu); - -/** - * gdb_exit: exit gdb session, reporting inferior status - * @code: exit code reported - * - * This closes the session and sends a final packet to GDB reporting - * the exit status of the program. It also cleans up any connections - * detritus before returning. - */ -void gdb_exit(int code); #ifdef CONFIG_USER_ONLY /** @@ -121,7 +110,7 @@ static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi, uint64_t val_lo) { uint64_t to_quad; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN to_quad = tswap64(val_hi); g_byte_array_append(buf, (uint8_t *) &to_quad, 8); to_quad = tswap64(val_lo); @@ -165,7 +154,7 @@ static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len) #define ldtul_p(addr) ldl_p(addr) #endif -#endif +#endif /* NEED_CPU_H */ /** * gdbserver_start: start the gdb server @@ -177,6 +166,18 @@ static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len) */ int gdbserver_start(const char *port_or_device); +/** + * gdb_exit: exit gdb session, reporting inferior status + * @code: exit code reported + * + * This closes the session and sends a final packet to GDB reporting + * the exit status of the program. It also cleans up any connections + * detritus before returning. + */ +void gdb_exit(int code); + +void gdb_set_stop_cpu(CPUState *cpu); + /** * gdb_has_xml: * This is an ugly hack to cope with both new and old gdb. diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index b974eb394a..e242fed46e 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -46,20 +46,23 @@ #define dh_ctype_ptr void * #define dh_ctype_cptr const void * #define dh_ctype_void void -#define dh_ctype_noreturn void QEMU_NORETURN +#define dh_ctype_noreturn G_NORETURN void #define dh_ctype(t) dh_ctype_##t #ifdef NEED_CPU_H # ifdef TARGET_LONG_BITS # if TARGET_LONG_BITS == 32 # define dh_alias_tl i32 +# define dh_typecode_tl dh_typecode_i32 # else # define dh_alias_tl i64 +# define dh_typecode_tl dh_typecode_i64 # endif # endif -# define dh_alias_env ptr # define dh_ctype_tl target_ulong +# define dh_alias_env ptr # define dh_ctype_env CPUArchState * +# define dh_typecode_env dh_typecode_ptr #endif /* We can't use glue() here because it falls foul of C preprocessor @@ -92,18 +95,16 @@ #define dh_typecode_i64 4 #define dh_typecode_s64 5 #define dh_typecode_ptr 6 -#define dh_typecode(t) glue(dh_typecode_, dh_alias(t)) +#define dh_typecode_int dh_typecode_s32 +#define dh_typecode_f16 dh_typecode_i32 +#define dh_typecode_f32 dh_typecode_i32 +#define dh_typecode_f64 dh_typecode_i64 +#define dh_typecode_cptr dh_typecode_ptr +#define dh_typecode(t) dh_typecode_##t #define dh_callflag_i32 0 -#define dh_callflag_s32 0 -#define dh_callflag_int 0 #define dh_callflag_i64 0 -#define dh_callflag_s64 0 -#define dh_callflag_f16 0 -#define dh_callflag_f32 0 -#define dh_callflag_f64 0 #define dh_callflag_ptr 0 -#define dh_callflag_cptr dh_callflag_ptr #define dh_callflag_void 0 #define dh_callflag_noreturn TCG_CALL_NO_RETURN #define dh_callflag(t) glue(dh_callflag_, dh_alias(t)) diff --git a/include/exec/log.h b/include/exec/log.h index 3c7fa65ead..4a7375a45f 100644 --- a/include/exec/log.h +++ b/include/exec/log.h @@ -15,15 +15,10 @@ */ static inline void log_cpu_state(CPUState *cpu, int flags) { - QemuLogFile *logfile; - - if (qemu_log_enabled()) { - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - cpu_dump_state(cpu, logfile->fd, flags); - } - rcu_read_unlock(); + FILE *f = qemu_log_trylock(); + if (f) { + cpu_dump_state(cpu, f, flags); + qemu_log_unlock(f); } } @@ -42,43 +37,4 @@ static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) } } -#ifdef NEED_CPU_H -/* disas() and target_disas() to qemu_logfile: */ -static inline void log_target_disas(CPUState *cpu, target_ulong start, - target_ulong len) -{ - QemuLogFile *logfile; - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - target_disas(logfile->fd, cpu, start, len); - } - rcu_read_unlock(); -} - -static inline void log_disas(const void *code, unsigned long size) -{ - QemuLogFile *logfile; - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - disas(logfile->fd, code, size); - } - rcu_read_unlock(); -} - -#if defined(CONFIG_USER_ONLY) -/* page_dump() output to the log file: */ -static inline void log_page_dump(const char *operation) -{ - FILE *logfile = qemu_log_lock(); - if (logfile) { - qemu_log("page layout changed following %s\n", operation); - page_dump(logfile); - } - qemu_log_unlock(logfile); -} -#endif -#endif - #endif diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 95f2d20d55..9fb98bc1ef 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -35,6 +35,14 @@ typedef struct MemTxAttrs { unsigned int secure:1; /* Memory access is usermode (unprivileged) */ unsigned int user:1; + /* + * Bus interconnect and peripherals can access anything (memories, + * devices) by default. By setting the 'memory' bit, bus transaction + * are restricted to "normal" memories (per the AMBA documentation) + * versus devices. Access to devices will be logged and rejected + * (see MEMTX_ACCESS_ERROR). + */ + unsigned int memory:1; /* Requester ID (for MSI for example) */ unsigned int requester_id:16; /* Invert endianness for this page */ @@ -66,6 +74,7 @@ typedef struct MemTxAttrs { #define MEMTX_OK 0 #define MEMTX_ERROR (1U << 0) /* device returned an error */ #define MEMTX_DECODE_ERROR (1U << 1) /* nothing at that address */ +#define MEMTX_ACCESS_ERROR (1U << 2) /* access denied */ typedef uint32_t MemTxResult; #endif diff --git a/include/exec/memop.h b/include/exec/memop.h index 2a885f3917..25d027434a 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -28,7 +28,7 @@ typedef enum MemOp { MO_SIGN = 0x08, /* Sign-extended, otherwise zero-extended. */ MO_BSWAP = 0x10, /* Host reverse endian. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN MO_LE = MO_BSWAP, MO_BE = 0, #else @@ -36,7 +36,7 @@ typedef enum MemOp { MO_BE = MO_BSWAP, #endif #ifdef NEED_CPU_H -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN MO_TE = MO_BE, #else MO_TE = MO_LE, diff --git a/include/exec/memory.h b/include/exec/memory.h index 4d5997e6bb..f1c19451bc 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2931,7 +2931,7 @@ static inline MemOp devend_memop(enum device_endian end) QEMU_BUILD_BUG_ON(DEVICE_HOST_ENDIAN != DEVICE_LITTLE_ENDIAN && DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN /* Swap if non-host endianness or native (target) endianness */ return (end == DEVICE_HOST_ENDIAN) ? 0 : MO_BSWAP; #else diff --git a/include/exec/page-vary.h b/include/exec/page-vary.h index c22a7a742e..ebbe9b169b 100644 --- a/include/exec/page-vary.h +++ b/include/exec/page-vary.h @@ -31,4 +31,22 @@ extern bool set_preferred_target_page_bits_common(int bits); extern void finalize_target_page_bits_common(int min); #endif +/** + * set_preferred_target_page_bits: + * @bits: number of bits needed to represent an address within the page + * + * Set the preferred target page size (the actual target page + * size may be smaller than any given CPU's preference). + * Returns true on success, false on failure (which can only happen + * if this is called after the system has already finalized its + * choice of page size and the requested page size is smaller than that). + */ +bool set_preferred_target_page_bits(int bits); + +/** + * finalize_target_page_bits: + * Commit the final value set by set_preferred_target_page_bits. + */ +void finalize_target_page_bits(void); + #endif /* EXEC_PAGE_VARY_H */ diff --git a/include/exec/poison.h b/include/exec/poison.h index 7ad4ad18e8..9f1ca3409c 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -38,7 +38,7 @@ #pragma GCC poison TARGET_HAS_BFLT #pragma GCC poison TARGET_NAME #pragma GCC poison TARGET_SUPPORTS_MTTCG -#pragma GCC poison TARGET_WORDS_BIGENDIAN +#pragma GCC poison TARGET_BIG_ENDIAN #pragma GCC poison BSWAP_NEEDED #pragma GCC poison TARGET_LONG_BITS @@ -51,8 +51,6 @@ #pragma GCC poison TARGET_PAGE_BITS #pragma GCC poison TARGET_PAGE_ALIGN -#pragma GCC poison CPUArchState - #pragma GCC poison CPU_INTERRUPT_HARD #pragma GCC poison CPU_INTERRUPT_EXITTB #pragma GCC poison CPU_INTERRUPT_HALT diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 64fb936c7c..f3e0c78161 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -343,7 +343,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, hwaddr addr; ram_addr_t ram_addr; unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; - unsigned long hpratio = qemu_real_host_page_size / TARGET_PAGE_SIZE; + unsigned long hpratio = qemu_real_host_page_size() / TARGET_PAGE_SIZE; unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); /* start address is aligned at the start of a word? */ diff --git a/include/exec/translator.h b/include/exec/translator.h index 9bc46eda59..31d3fa76ff 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -118,7 +118,7 @@ typedef struct TranslatorOps { void (*insn_start)(DisasContextBase *db, CPUState *cpu); void (*translate_insn)(DisasContextBase *db, CPUState *cpu); void (*tb_stop)(DisasContextBase *db, CPUState *cpu); - void (*disas_log)(const DisasContextBase *db, CPUState *cpu); + void (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f); } TranslatorOps; /** diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 8abd9ab4ec..7a6ea881d8 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -103,7 +103,7 @@ typedef struct { #define make_floatx80(exp, mant) ((floatx80) { mant, exp }) #define make_floatx80_init(exp, mant) { .low = mant, .high = exp } typedef struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint64_t high, low; #else uint64_t low, high; diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index d34b2c44d2..3dcf20e3a2 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -95,6 +95,7 @@ typedef enum { #include "fpu/softfloat-types.h" #include "fpu/softfloat-helpers.h" +#include "qemu/int128.h" /*---------------------------------------------------------------------------- | Routine to raise any or all of the software IEC/IEEE floating-point @@ -182,7 +183,9 @@ floatx80 int64_to_floatx80(int64_t, float_status *status); float128 int32_to_float128(int32_t, float_status *status); float128 int64_to_float128(int64_t, float_status *status); +float128 int128_to_float128(Int128, float_status *status); float128 uint64_to_float128(uint64_t, float_status *status); +float128 uint128_to_float128(Int128, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. @@ -1201,9 +1204,13 @@ floatx80 floatx80_default_nan(float_status *status); int32_t float128_to_int32(float128, float_status *status); int32_t float128_to_int32_round_to_zero(float128, float_status *status); int64_t float128_to_int64(float128, float_status *status); +Int128 float128_to_int128(float128, float_status *status); int64_t float128_to_int64_round_to_zero(float128, float_status *status); +Int128 float128_to_int128_round_to_zero(float128, float_status *status); uint64_t float128_to_uint64(float128, float_status *status); +Int128 float128_to_uint128(float128, float_status *status); uint64_t float128_to_uint64_round_to_zero(float128, float_status *status); +Int128 float128_to_uint128_round_to_zero(float128, float_status *status); uint32_t float128_to_uint32(float128, float_status *status); uint32_t float128_to_uint32_round_to_zero(float128, float_status *status); float32 float128_to_float32(float128, float_status *status); diff --git a/include/glib-compat.h b/include/glib-compat.h index 3113a7d2af..43a562974d 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -147,4 +147,8 @@ qemu_g_test_slow(void) #pragma GCC diagnostic pop +#ifndef G_NORETURN +#define G_NORETURN G_GNUC_NORETURN +#endif + #endif diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index c97e8633ad..2b42e4192b 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -77,6 +77,7 @@ typedef struct AcpiFadtData { uint16_t plvl2_lat; /* P_LVL2_LAT */ uint16_t plvl3_lat; /* P_LVL3_LAT */ uint16_t arm_boot_arch; /* ARM_BOOT_ARCH */ + uint16_t iapc_boot_arch; /* IAPC_BOOT_ARCH */ uint8_t minor_ver; /* FADT Minor Version */ /* diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 8346003a22..d1fb08514b 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -289,7 +289,7 @@ void free_aml_allocator(void); void aml_append(Aml *parent_ctx, Aml *child); /* non block AML object primitives */ -Aml *aml_name(const char *name_format, ...) GCC_FMT_ATTR(1, 2); +Aml *aml_name(const char *name_format, ...) G_GNUC_PRINTF(1, 2); Aml *aml_name_decl(const char *name, Aml *val); Aml *aml_debug(void); Aml *aml_return(Aml *val); @@ -344,13 +344,13 @@ Aml *aml_irq_no_flags(uint8_t irq); Aml *aml_named_field(const char *name, unsigned length); Aml *aml_reserved_field(unsigned length); Aml *aml_local(int num); -Aml *aml_string(const char *name_format, ...) GCC_FMT_ATTR(1, 2); +Aml *aml_string(const char *name_format, ...) G_GNUC_PRINTF(1, 2); Aml *aml_lnot(Aml *arg); Aml *aml_equal(Aml *arg1, Aml *arg2); Aml *aml_lgreater(Aml *arg1, Aml *arg2); Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2); Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len, - const char *name_format, ...) GCC_FMT_ATTR(4, 5); + const char *name_format, ...) G_GNUC_PRINTF(4, 5); Aml *aml_eisaid(const char *str); Aml *aml_word_bus_number(AmlMinFixed min_fixed, AmlMaxFixed max_fixed, AmlDecode dec, uint16_t addr_gran, @@ -384,8 +384,8 @@ Aml *aml_sleep(uint64_t msec); Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source); /* Block AML object primitives */ -Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2); -Aml *aml_device(const char *name_format, ...) GCC_FMT_ATTR(1, 2); +Aml *aml_scope(const char *name_format, ...) G_GNUC_PRINTF(1, 2); +Aml *aml_device(const char *name_format, ...) G_GNUC_PRINTF(1, 2); Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag); Aml *aml_if(Aml *predicate); Aml *aml_else(void); @@ -460,7 +460,7 @@ build_xsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, int build_append_named_dword(GArray *array, const char *name_format, ...) -GCC_FMT_ATTR(2, 3); +G_GNUC_PRINTF(2, 3); void build_append_gas(GArray *table, AmlAddressSpace as, uint8_t bit_width, uint8_t bit_offset, diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index af1a169fc3..7e268c2c9c 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -73,8 +73,6 @@ void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off); extern const VMStateDescription vmstate_acpi_pcihp_pci_status; -bool vmstate_acpi_pcihp_use_acpi_index(void *opaque, int version_id); - #define VMSTATE_PCI_HOTPLUG(pcihp, state, test_pcihp, test_acpi_index) \ VMSTATE_UINT32_TEST(pcihp.hotplug_select, state, \ test_pcihp), \ diff --git a/include/hw/adc/aspeed_adc.h b/include/hw/adc/aspeed_adc.h index 2f166e8be1..ff1d06ea91 100644 --- a/include/hw/adc/aspeed_adc.h +++ b/include/hw/adc/aspeed_adc.h @@ -17,6 +17,7 @@ #define TYPE_ASPEED_2400_ADC TYPE_ASPEED_ADC "-ast2400" #define TYPE_ASPEED_2500_ADC TYPE_ASPEED_ADC "-ast2500" #define TYPE_ASPEED_2600_ADC TYPE_ASPEED_ADC "-ast2600" +#define TYPE_ASPEED_1030_ADC TYPE_ASPEED_ADC "-ast1030" OBJECT_DECLARE_TYPE(AspeedADCState, AspeedADCClass, ASPEED_ADC) #define TYPE_ASPEED_ADC_ENGINE "aspeed.adc.engine" diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index cae9906684..e13af374b9 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -13,6 +13,7 @@ #define ASPEED_SOC_H #include "hw/cpu/a15mpcore.h" +#include "hw/arm/armv7m.h" #include "hw/intc/aspeed_vic.h" #include "hw/misc/aspeed_scu.h" #include "hw/adc/aspeed_adc.h" @@ -24,6 +25,7 @@ #include "hw/misc/aspeed_i3c.h" #include "hw/ssi/aspeed_smc.h" #include "hw/misc/aspeed_hace.h" +#include "hw/misc/aspeed_sbc.h" #include "hw/watchdog/wdt_aspeed.h" #include "hw/net/ftgmac100.h" #include "target/arm/cpu.h" @@ -46,6 +48,7 @@ struct AspeedSoCState { /*< public >*/ ARMCPU cpu[ASPEED_CPUS_NUM]; A15MPPrivState a7mpcore; + ARMv7MState armv7m; MemoryRegion *dram_mr; MemoryRegion sram; AspeedVICState vic; @@ -60,6 +63,7 @@ struct AspeedSoCState { AspeedSMCState fmc; AspeedSMCState spi[ASPEED_SPIS_NUM]; EHCISysBusState ehci[ASPEED_EHCIS_NUM]; + AspeedSBCState sbc; AspeedSDMCState sdmc; AspeedWDTState wdt[ASPEED_WDTS_NUM]; FTGMAC100State ftgmac100[ASPEED_MACS_NUM]; @@ -70,6 +74,7 @@ struct AspeedSoCState { AspeedSDHCIState emmc; AspeedLPCState lpc; uint32_t uart_default; + Clock *sysclk; }; #define TYPE_ASPEED_SOC "aspeed-soc" @@ -109,6 +114,8 @@ enum { ASPEED_DEV_SDMC, ASPEED_DEV_SCU, ASPEED_DEV_ADC, + ASPEED_DEV_SBC, + ASPEED_DEV_EMMC_BC, ASPEED_DEV_VIDEO, ASPEED_DEV_SRAM, ASPEED_DEV_SDHCI, diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h index 60b9e126f5..97353f1c02 100644 --- a/include/hw/arm/exynos4210.h +++ b/include/hw/arm/exynos4210.h @@ -26,6 +26,10 @@ #include "hw/or-irq.h" #include "hw/sysbus.h" +#include "hw/cpu/a9mpcore.h" +#include "hw/intc/exynos4210_gic.h" +#include "hw/intc/exynos4210_combiner.h" +#include "hw/core/split-irq.h" #include "target/arm/cpu-qom.h" #include "qom/object.h" @@ -65,34 +69,25 @@ #define EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ \ (EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ * 8) -#define EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit) ((grp)*8 + (bit)) -#define EXYNOS4210_COMBINER_GET_GRP_NUM(irq) ((irq) / 8) -#define EXYNOS4210_COMBINER_GET_BIT_NUM(irq) \ - ((irq) - 8 * EXYNOS4210_COMBINER_GET_GRP_NUM(irq)) - -/* IRQs number for external and internal GIC */ -#define EXYNOS4210_EXT_GIC_NIRQ (160-32) -#define EXYNOS4210_INT_GIC_NIRQ 64 - #define EXYNOS4210_I2C_NUMBER 9 #define EXYNOS4210_NUM_DMA 3 -typedef struct Exynos4210Irq { - qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; - qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ]; - qemu_irq int_gic_irq[EXYNOS4210_INT_GIC_NIRQ]; - qemu_irq ext_gic_irq[EXYNOS4210_EXT_GIC_NIRQ]; - qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; -} Exynos4210Irq; +/* + * We need one splitter for every external combiner input, plus + * one for every non-zero entry in combiner_grp_to_gic_id[], + * minus one for every external combiner ID in second or later + * places in a combinermap[] line. + * We'll assert in exynos4210_init_board_irqs() if this is wrong. + */ +#define EXYNOS4210_NUM_SPLITTERS (EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ + 38) struct Exynos4210State { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ ARMCPU *cpu[EXYNOS4210_NCPUS]; - Exynos4210Irq irqs; - qemu_irq *irq_table; + qemu_irq irq_table[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; MemoryRegion chipid_mem; MemoryRegion iram_mem; @@ -102,6 +97,12 @@ struct Exynos4210State { MemoryRegion bootreg_mem; I2CBus *i2c_if[EXYNOS4210_I2C_NUMBER]; qemu_or_irq pl330_irq_orgate[EXYNOS4210_NUM_DMA]; + qemu_or_irq cpu_irq_orgate[EXYNOS4210_NCPUS]; + A9MPPrivState a9mpcore; + Exynos4210GicState ext_gic; + Exynos4210CombinerState int_combiner; + Exynos4210CombinerState ext_combiner; + SplitIRQ splitter[EXYNOS4210_NUM_SPLITTERS]; }; #define TYPE_EXYNOS4210_SOC "exynos4210" @@ -110,25 +111,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210State, EXYNOS4210_SOC) void exynos4210_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info); -/* Initialize exynos4210 IRQ subsystem stub */ -qemu_irq *exynos4210_init_irq(Exynos4210Irq *env); - -/* Initialize board IRQs. - * These IRQs contain splitted Int/External Combiner and External Gic IRQs */ -void exynos4210_init_board_irqs(Exynos4210Irq *s); - /* Get IRQ number from exynos4210 IRQ subsystem stub. * To identify IRQ source use internal combiner group and bit number * grp - group number * bit - bit number inside group */ uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit); -/* - * Get Combiner input GPIO into irqs structure - */ -void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, - int ext); - /* * exynos4210 UART */ diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 706be3c6d0..21e62342e9 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -71,6 +71,7 @@ typedef struct SMMUTransCfg { bool disabled; /* smmu is disabled */ bool bypassed; /* translation is bypassed */ bool aborted; /* translation is aborted */ + bool record_faults; /* record fault events */ uint64_t ttb; /* TT base address */ uint8_t oas; /* output address width */ uint8_t tbi; /* Top Byte Ignore */ diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index c1ea17d0de..15feabac63 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -113,6 +113,7 @@ typedef enum VirtGICType { VIRT_GIC_VERSION_HOST, VIRT_GIC_VERSION_2, VIRT_GIC_VERSION_3, + VIRT_GIC_VERSION_4, VIRT_GIC_VERSION_NOSEL, } VirtGICType; @@ -132,6 +133,7 @@ struct VirtMachineClass { bool no_secure_gpio; /* Machines < 6.2 have no support for describing cpu topology to guest */ bool no_cpu_topology; + bool no_tcg_lpa2; }; struct VirtMachineState { @@ -184,13 +186,25 @@ OBJECT_DECLARE_TYPE(VirtMachineState, VirtMachineClass, VIRT_MACHINE) void virt_acpi_setup(VirtMachineState *vms); bool virt_is_acpi_enabled(VirtMachineState *vms); +/* Return number of redistributors that fit in the specified region */ +static uint32_t virt_redist_capacity(VirtMachineState *vms, int region) +{ + uint32_t redist_size; + + if (vms->gic_version == VIRT_GIC_VERSION_3) { + redist_size = GICV3_REDIST_SIZE; + } else { + redist_size = GICV4_REDIST_SIZE; + } + return vms->memmap[region].size / redist_size; +} + /* Return the number of used redistributor regions */ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) { - uint32_t redist0_capacity = - vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST); - assert(vms->gic_version == VIRT_GIC_VERSION_3); + assert(vms->gic_version != VIRT_GIC_VERSION_2); return (MACHINE(vms)->smp.cpus > redist0_capacity && vms->highmem_redists) ? 2 : 1; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 0728316ec7..cbe8a19c10 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -14,6 +14,7 @@ #include "hw/sysbus.h" #include "hw/arm/boot.h" +#include "hw/cpu/cluster.h" #include "hw/or-irq.h" #include "hw/sd/sdhci.h" #include "hw/intc/arm_gicv3.h" @@ -28,12 +29,14 @@ #include "hw/nvram/xlnx-versal-efuse.h" #include "hw/ssi/xlnx-versal-ospi.h" #include "hw/dma/xlnx_csu_dma.h" +#include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #define TYPE_XLNX_VERSAL "xlnx-versal" OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) #define XLNX_VERSAL_NR_ACPUS 2 +#define XLNX_VERSAL_NR_RCPUS 2 #define XLNX_VERSAL_NR_UARTS 2 #define XLNX_VERSAL_NR_GEMS 2 #define XLNX_VERSAL_NR_ADMAS 8 @@ -49,6 +52,7 @@ struct Versal { struct { struct { MemoryRegion mr; + CPUClusterState cluster; ARMCPU cpu[XLNX_VERSAL_NR_ACPUS]; GICv3State gic; } apu; @@ -71,10 +75,21 @@ struct Versal { VersalUsb2 usb; } iou; + /* Real-time Processing Unit. */ + struct { + MemoryRegion mr; + MemoryRegion mr_ps_alias; + + CPUClusterState cluster; + ARMCPU cpu[XLNX_VERSAL_NR_RCPUS]; + } rpu; + struct { qemu_or_irq irq_orgate; XlnxXramCtrl ctrl[XLNX_VERSAL_NR_XRAM]; } xram; + + XlnxVersalCRL crl; } lpd; /* The Platform Management Controller subsystem. */ @@ -115,6 +130,7 @@ struct Versal { #define VERSAL_TIMER_NS_EL1_IRQ 14 #define VERSAL_TIMER_NS_EL2_IRQ 10 +#define VERSAL_CRL_IRQ 10 #define VERSAL_UART0_IRQ_0 18 #define VERSAL_UART1_IRQ_0 19 #define VERSAL_USB0_IRQ_0 22 diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 9424f81c37..85fd9f53da 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -39,6 +39,9 @@ #include "hw/nvram/xlnx-bbram.h" #include "hw/nvram/xlnx-zynqmp-efuse.h" #include "hw/or-irq.h" +#include "hw/misc/xlnx-zynqmp-apu-ctrl.h" +#include "hw/misc/xlnx-zynqmp-crf.h" +#include "hw/timer/cadence_ttc.h" #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) @@ -82,6 +85,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) #define XLNX_ZYNQMP_MAX_RAM_SIZE (XLNX_ZYNQMP_MAX_LOW_RAM_SIZE + \ XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE) +#define XLNX_ZYNQMP_NUM_TTC 4 + /* * Unimplemented mmio regions needed to boot some images. */ @@ -124,6 +129,9 @@ struct XlnxZynqMPState { XlnxZDMA adma[XLNX_ZYNQMP_NUM_ADMA_CH]; XlnxCSUDMA qspi_dma; qemu_or_irq qspi_irq_orgate; + XlnxZynqMPAPUCtrl apu_ctrl; + XlnxZynqMPCRF crf; + CadenceTTCState ttc[XLNX_ZYNQMP_NUM_TTC]; char *boot_cpu; ARMCPU *boot_cpu_ptr; diff --git a/include/hw/boards.h b/include/hw/boards.h index c92ac8815c..d64b5481e8 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -380,6 +380,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_7_0[]; +extern const size_t hw_compat_7_0_len; + extern GlobalProperty hw_compat_6_2[]; extern const size_t hw_compat_6_2_len; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 76ab3b851c..996f94059f 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -22,6 +22,7 @@ #include "hw/qdev-core.h" #include "disas/dis-asm.h" +#include "exec/cpu-common.h" #include "exec/hwaddr.h" #include "exec/memattrs.h" #include "qapi/qapi-types-run-state.h" @@ -35,18 +36,6 @@ typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, void *opaque); -/** - * vaddr: - * Type wide enough to contain any #target_ulong virtual address. - */ -typedef uint64_t vaddr; -#define VADDR_PRId PRId64 -#define VADDR_PRIu PRIu64 -#define VADDR_PRIo PRIo64 -#define VADDR_PRIx PRIx64 -#define VADDR_PRIX PRIX64 -#define VADDR_MAX UINT64_MAX - /** * SECTION:cpu * @section_id: QEMU-cpu @@ -66,6 +55,24 @@ typedef struct CPUClass CPUClass; DECLARE_CLASS_CHECKERS(CPUClass, CPU, TYPE_CPU) +/** + * OBJECT_DECLARE_CPU_TYPE: + * @CpuInstanceType: instance struct name + * @CpuClassType: class struct name + * @CPU_MODULE_OBJ_NAME: the CPU name in uppercase with underscore separators + * + * This macro is typically used in "cpu-qom.h" header file, and will: + * + * - create the typedefs for the CPU object and class structs + * - register the type for use with g_autoptr + * - provide three standard type cast functions + * + * The object struct and class struct need to be declared manually. + */ +#define OBJECT_DECLARE_CPU_TYPE(CpuInstanceType, CpuClassType, CPU_MODULE_OBJ_NAME) \ + typedef struct ArchCPU CpuInstanceType; \ + OBJECT_DECLARE_TYPE(ArchCPU, CpuClassType, CPU_MODULE_OBJ_NAME); + typedef enum MMUAccessType { MMU_DATA_LOAD = 0, MMU_DATA_STORE = 1, @@ -180,7 +187,7 @@ struct CPUClass { typedef union IcountDecr { uint32_t u32; struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint16_t high; uint16_t low; #else @@ -351,7 +358,7 @@ struct CPUState { AddressSpace *as; MemoryRegion *memory; - void *env_ptr; /* CPUArchState */ + CPUArchState *env_ptr; IcountDecr *icount_decr_ptr; /* Accessed in parallel; all accesses must be atomic */ @@ -1008,8 +1015,8 @@ int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len); */ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); -void QEMU_NORETURN cpu_abort(CPUState *cpu, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); +G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) + G_GNUC_PRINTF(2, 3); /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); @@ -1021,13 +1028,15 @@ void cpu_exec_unrealizefn(CPUState *cpu); * target_words_bigendian: * Returns true if the (default) endianness of the target is big endian, * false otherwise. Note that in target-specific code, you can use - * TARGET_WORDS_BIGENDIAN directly instead. On the other hand, common + * TARGET_BIG_ENDIAN directly instead. On the other hand, common * code should normally never need to know about the endianness of the * target, so please do *not* use this function unless you know very well * what you are doing! */ bool target_words_bigendian(void); +void page_size_init(void); + #ifdef NEED_CPU_H #ifdef CONFIG_SOFTMMU diff --git a/include/hw/arm/sysbus-fdt.h b/include/hw/core/sysbus-fdt.h similarity index 100% rename from include/hw/arm/sysbus-fdt.h rename to include/hw/core/sysbus-fdt.h diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index e13898553a..78c6c6635d 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -78,9 +78,9 @@ struct TCGCPUOps { * @do_unaligned_access: Callback for unaligned access handling * The callback must exit via raising an exception. */ - void (*do_unaligned_access)(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) QEMU_NORETURN; + G_NORETURN void (*do_unaligned_access)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); /** * @adjust_watchpoint_address: hack for cpu_check_watchpoint used by ARM @@ -90,6 +90,7 @@ struct TCGCPUOps { /** * @debug_check_watchpoint: return true if the architectural * watchpoint whose address has matched should really fire, used by ARM + * and RISC-V */ bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp); diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h index e52775aa21..55a50d3fb0 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -48,7 +48,8 @@ typedef struct MacFbMode { uint32_t offset; } MacFbMode; -#define MACFB_NUM_REGS 8 +#define MACFB_CTRL_TOPADDR 0x200 +#define MACFB_NUM_REGS (MACFB_CTRL_TOPADDR / sizeof(uint32_t)) typedef struct MacfbState { MemoryRegion mem_vram; @@ -66,8 +67,6 @@ typedef struct MacfbState { uint32_t regs[MACFB_NUM_REGS]; MacFbMode *mode; - uint32_t irq_state; - uint32_t irq_mask; QEMUTimer *vbl_timer; qemu_irq irq; } MacfbState; diff --git a/include/hw/hw.h b/include/hw/hw.h index fc5301f293..045c1c8b09 100644 --- a/include/hw/hw.h +++ b/include/hw/hw.h @@ -5,6 +5,6 @@ #error Cannot include hw/hw.h from user emulation #endif -void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +G_NORETURN void hw_error(const char *fmt, ...) G_GNUC_PRINTF(1, 2); #endif diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index 21dc28aee9..4a2297307b 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -24,12 +24,17 @@ #define HV_STATUS_INVALID_PORT_ID 17 #define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 +#define HV_STATUS_NOT_ACKNOWLEDGED 20 +#define HV_STATUS_NO_DATA 27 /* * Hypercall numbers */ #define HV_POST_MESSAGE 0x005c #define HV_SIGNAL_EVENT 0x005d +#define HV_POST_DEBUG_DATA 0x0069 +#define HV_RETRIEVE_DEBUG_DATA 0x006a +#define HV_RESET_DEBUG_SESSION 0x006b #define HV_HYPERCALL_FAST (1u << 16) /* @@ -127,4 +132,51 @@ struct hyperv_event_flags_page { struct hyperv_event_flags slot[HV_SINT_COUNT]; }; +/* + * Kernel debugger structures + */ + +/* Options flags for hyperv_reset_debug_session */ +#define HV_DEBUG_PURGE_INCOMING_DATA 0x00000001 +#define HV_DEBUG_PURGE_OUTGOING_DATA 0x00000002 +struct hyperv_reset_debug_session_input { + uint32_t options; +} __attribute__ ((__packed__)); + +struct hyperv_reset_debug_session_output { + uint32_t host_ip; + uint32_t target_ip; + uint16_t host_port; + uint16_t target_port; + uint8_t host_mac[6]; + uint8_t target_mac[6]; +} __attribute__ ((__packed__)); + +/* Options for hyperv_post_debug_data */ +#define HV_DEBUG_POST_LOOP 0x00000001 + +struct hyperv_post_debug_data_input { + uint32_t count; + uint32_t options; + /*uint8_t data[HV_HYP_PAGE_SIZE - 2 * sizeof(uint32_t)];*/ +} __attribute__ ((__packed__)); + +struct hyperv_post_debug_data_output { + uint32_t pending_count; +} __attribute__ ((__packed__)); + +/* Options for hyperv_retrieve_debug_data */ +#define HV_DEBUG_RETRIEVE_LOOP 0x00000001 +#define HV_DEBUG_RETRIEVE_TEST_ACTIVITY 0x00000002 + +struct hyperv_retrieve_debug_data_input { + uint32_t count; + uint32_t options; + uint64_t timeout; +} __attribute__ ((__packed__)); + +struct hyperv_retrieve_debug_data_output { + uint32_t retrieved_count; + uint32_t remaining_count; +} __attribute__ ((__packed__)); #endif diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h index a63ee0003c..015c3524b1 100644 --- a/include/hw/hyperv/hyperv.h +++ b/include/hw/hyperv/hyperv.h @@ -81,4 +81,62 @@ void hyperv_synic_update(CPUState *cs, bool enable, hwaddr msg_page_addr, hwaddr event_page_addr); bool hyperv_is_synic_enabled(void); +/* + * Process HVCALL_RESET_DEBUG_SESSION hypercall. + */ +uint16_t hyperv_hcall_reset_dbg_session(uint64_t outgpa); +/* + * Process HVCALL_RETREIVE_DEBUG_DATA hypercall. + */ +uint16_t hyperv_hcall_retreive_dbg_data(uint64_t ingpa, uint64_t outgpa, + bool fast); +/* + * Process HVCALL_POST_DEBUG_DATA hypercall. + */ +uint16_t hyperv_hcall_post_dbg_data(uint64_t ingpa, uint64_t outgpa, bool fast); + +uint32_t hyperv_syndbg_send(uint64_t ingpa, uint32_t count); +uint32_t hyperv_syndbg_recv(uint64_t ingpa, uint32_t count); +void hyperv_syndbg_set_pending_page(uint64_t ingpa); +uint64_t hyperv_syndbg_query_options(void); + +typedef enum HvSynthDbgMsgType { + HV_SYNDBG_MSG_CONNECTION_INFO, + HV_SYNDBG_MSG_SEND, + HV_SYNDBG_MSG_RECV, + HV_SYNDBG_MSG_SET_PENDING_PAGE, + HV_SYNDBG_MSG_QUERY_OPTIONS +} HvDbgSynthMsgType; + +typedef struct HvSynDbgMsg { + HvDbgSynthMsgType type; + union { + struct { + uint32_t host_ip; + uint16_t host_port; + } connection_info; + struct { + uint64_t buf_gpa; + uint32_t count; + uint32_t pending_count; + bool is_raw; + } send; + struct { + uint64_t buf_gpa; + uint32_t count; + uint32_t options; + uint64_t timeout; + uint32_t retrieved_count; + bool is_raw; + } recv; + struct { + uint64_t buf_gpa; + } pending_page; + struct { + uint64_t options; + } query_options; + } u; +} HvSynDbgMsg; +typedef uint16_t (*HvSynDbgHandler)(void *context, HvSynDbgMsg *msg); +void hyperv_set_syndbg_handler(HvSynDbgHandler handler, void *context); #endif diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index 62bd38c83f..0f4d6b3fad 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -43,6 +43,7 @@ enum pmbus_registers { PMBUS_VOUT_DROOP = 0x28, /* R/W word */ PMBUS_VOUT_SCALE_LOOP = 0x29, /* R/W word */ PMBUS_VOUT_SCALE_MONITOR = 0x2A, /* R/W word */ + PMBUS_VOUT_MIN = 0x2B, /* R/W word */ PMBUS_COEFFICIENTS = 0x30, /* Read-only block 5 bytes */ PMBUS_POUT_MAX = 0x31, /* R/W word */ PMBUS_MAX_DUTY = 0x32, /* R/W word */ @@ -227,6 +228,8 @@ enum pmbus_registers { #define PB_MAX_PAGES 0x1F #define PB_ALL_PAGES 0xFF +#define PMBUS_ERR_BYTE 0xFF + #define TYPE_PMBUS_DEVICE "pmbus-device" OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, PMBUS_DEVICE) @@ -255,6 +258,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, #define PB_HAS_TEMP3 BIT_ULL(42) #define PB_HAS_TEMP_RATING BIT_ULL(43) #define PB_HAS_MFR_INFO BIT_ULL(50) +#define PB_HAS_STATUS_MFR_SPECIFIC BIT_ULL(51) struct PMBusDeviceClass { SMBusDeviceClass parent_class; @@ -295,6 +299,7 @@ typedef struct PMBusPage { uint16_t vout_droop; /* R/W word */ uint16_t vout_scale_loop; /* R/W word */ uint16_t vout_scale_monitor; /* R/W word */ + uint16_t vout_min; /* R/W word */ uint8_t coefficients[5]; /* Read-only block 5 bytes */ uint16_t pout_max; /* R/W word */ uint16_t max_duty; /* R/W word */ @@ -443,7 +448,7 @@ typedef struct PMBusCoefficients { * * Y = (m * x - b) * 10^R * - * @return uint32_t + * @return uint16_t */ uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value); @@ -456,6 +461,24 @@ uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value); */ uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value); +/** + * Convert sensor values to linear mode format + * + * L = D * 2^(-e) + * + * @return uint16 + */ +uint16_t pmbus_data2linear_mode(uint16_t value, int exp); + +/** + * Convert linear mode formatted data into sensor reading + * + * D = L * 2^e + * + * @return uint16 + */ +uint16_t pmbus_linear_mode2data(uint16_t value, int exp); + /** * @brief Send a block of data over PMBus * Assumes that the bytes in the block are already ordered correctly, diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 41783ee46d..bfa982a419 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -145,7 +145,7 @@ enum { /* Interrupt Remapping Table Entry Definition */ union VTD_IR_TableEntry { struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint32_t __reserved_1:8; /* Reserved 1 */ uint32_t vector:8; /* Interrupt Vector */ uint32_t irte_mode:1; /* IRTE Mode */ @@ -172,7 +172,7 @@ union VTD_IR_TableEntry { #endif uint32_t dest_id; /* Destination ID */ uint16_t source_id; /* Source-ID */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint64_t __reserved_2:44; /* Reserved 2 */ uint64_t sid_vtype:2; /* Source-ID Validation Type */ uint64_t sid_q:2; /* Source-ID Qualifier */ @@ -191,7 +191,7 @@ union VTD_IR_TableEntry { /* Programming format for MSI/MSI-X addresses */ union VTD_IR_MSIAddress { struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint32_t __head:12; /* Should always be: 0x0fee */ uint32_t index_l:15; /* Interrupt index bit 14-0 */ uint32_t int_mode:1; /* Interrupt format */ @@ -228,6 +228,7 @@ struct IntelIOMMUState { bool caching_mode; /* RO - is cap CM enabled? */ bool scalable_mode; /* RO - is Scalable Mode supported? */ + bool snoop_control; /* RO - is SNP filed supported? */ dma_addr_t root; /* Current root table pointer */ bool root_scalable; /* Type of root table (scalable or not) */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 9c9f4ac748..637367dc5f 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -48,6 +48,7 @@ typedef struct PCMachineState { bool sata_enabled; bool pit_enabled; bool hpet_enabled; + bool i8042_enabled; bool default_bus_bypass_iommu; uint64_t max_fw_size; @@ -64,6 +65,7 @@ typedef struct PCMachineState { #define PC_MACHINE_SMBUS "smbus" #define PC_MACHINE_SATA "sata" #define PC_MACHINE_PIT "pit" +#define PC_MACHINE_I8042 "i8042" #define PC_MACHINE_MAX_FW_SIZE "max-fw-size" #define PC_MACHINE_SMBIOS_EP "smbios-entry-point-type" @@ -200,6 +202,9 @@ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_7_0[]; +extern const size_t pc_compat_7_0_len; + extern GlobalProperty pc_compat_6_2[]; extern const size_t pc_compat_6_2_len; diff --git a/include/hw/i386/x86-iommu.h b/include/hw/i386/x86-iommu.h index 5ba0c056d6..7637edb430 100644 --- a/include/hw/i386/x86-iommu.h +++ b/include/hw/i386/x86-iommu.h @@ -87,7 +87,7 @@ struct X86IOMMUIrq { struct X86IOMMU_MSIMessage { union { struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint32_t __addr_head:12; /* 0xfee */ uint32_t dest:8; uint32_t __reserved:8; @@ -108,7 +108,7 @@ struct X86IOMMU_MSIMessage { }; union { struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint16_t trigger_mode:1; uint16_t level:1; uint16_t __resved:3; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index a145a30370..4841a49f86 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -35,8 +35,6 @@ struct X86MachineClass { /* TSC rate migration: */ bool save_tsc_khz; - /* Enables contiguous-apic-ID mode */ - bool compat_apic_id_mode; /* use DMA capable linuxboot option rom */ bool fwcfg_dma_enabled; }; @@ -142,4 +140,7 @@ void gsi_handler(void *opaque, int n, int level); void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); DeviceState *ioapic_init_secondary(GSIState *gsi_state); +/* pc_sysfw.c */ +void x86_firmware_configure(void *ptr, int size); + #endif diff --git a/include/hw/input/i8042.h b/include/hw/input/i8042.h index 1d90432dae..e070f546e4 100644 --- a/include/hw/input/i8042.h +++ b/include/hw/input/i8042.h @@ -23,4 +23,19 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, void i8042_isa_mouse_fake_event(ISAKBDState *isa); void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out); +static inline bool i8042_present(void) +{ + bool amb = false; + return object_resolve_path_type("", TYPE_I8042, &amb) || amb; +} + +/* + * ACPI v2, Table 5-10 - Fixed ACPI Description Table Boot Architecture + * Flags, bit offset 1 - 8042. + */ +static inline uint16_t iapc_boot_arch_8042(void) +{ + return i8042_present() ? 0x1 << 1 : 0x0 ; +} + #endif /* HW_INPUT_I8042_H */ diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index fc38e4b7dc..4e41610055 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -38,7 +38,12 @@ #define GICV3_LPI_INTID_START 8192 +/* + * The redistributor in GICv3 has two 64KB frames per CPU; in + * GICv4 it has four 64KB frames per CPU. + */ #define GICV3_REDIST_SIZE 0x20000 +#define GICV4_REDIST_SIZE 0x40000 /* Number of SGI target-list bits */ #define GICV3_TARGETLIST_BITS 16 @@ -174,6 +179,9 @@ struct GICv3CPUState { uint32_t gicr_igrpmodr0; uint32_t gicr_nsacr; uint8_t gicr_ipriorityr[GIC_INTERNAL]; + /* VLPI_base page registers */ + uint64_t gicr_vpropbaser; + uint64_t gicr_vpendbaser; /* CPU interface */ uint64_t icc_sre_el1; @@ -211,6 +219,9 @@ struct GICv3CPUState { */ PendingIrq hpplpi; + /* Cached information recalculated from vLPI tables in guest memory */ + PendingIrq hppvlpi; + /* This is temporary working state, to avoid a malloc in gicv3_update() */ bool seenbetter; }; @@ -272,6 +283,8 @@ struct GICv3State { uint32_t gicd_nsacr[DIV_ROUND_UP(GICV3_MAXIRQ, 16)]; GICv3CPUState *cpu; + /* List of all ITSes connected to this GIC */ + GPtrArray *itslist; }; #define GICV3_BITMAP_ACCESSORS(BMP) \ diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h index 0f130494dd..a11a0f6654 100644 --- a/include/hw/intc/arm_gicv3_its_common.h +++ b/include/hw/intc/arm_gicv3_its_common.h @@ -78,6 +78,7 @@ struct GICv3ITSState { TableDesc dt; TableDesc ct; + TableDesc vpet; CmdQDesc cq; Error *migration_blocker; @@ -88,6 +89,24 @@ typedef struct GICv3ITSState GICv3ITSState; void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops, const MemoryRegionOps *tops); +/* + * The ITS should call this when it is realized to add itself + * to its GIC's list of connected ITSes. + */ +static inline void gicv3_add_its(GICv3State *s, DeviceState *its) +{ + g_ptr_array_add(s->itslist, its); +} + +/* + * The ITS can use this for operations that must be performed on + * every ITS connected to the same GIC that it is + */ +static inline void gicv3_foreach_its(GICv3State *s, GFunc func, void *opaque) +{ + g_ptr_array_foreach(s->itslist, func, opaque); +} + #define TYPE_ARM_GICV3_ITS_COMMON "arm-gicv3-its-common" typedef struct GICv3ITSCommonClass GICv3ITSCommonClass; DECLARE_OBJ_CHECKERS(GICv3ITSState, GICv3ITSCommonClass, diff --git a/include/hw/intc/exynos4210_combiner.h b/include/hw/intc/exynos4210_combiner.h new file mode 100644 index 0000000000..429844fed4 --- /dev/null +++ b/include/hw/intc/exynos4210_combiner.h @@ -0,0 +1,57 @@ +/* + * Samsung exynos4210 Interrupt Combiner + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef HW_INTC_EXYNOS4210_COMBINER +#define HW_INTC_EXYNOS4210_COMBINER + +#include "hw/sysbus.h" + +/* + * State for each output signal of internal combiner + */ +typedef struct CombinerGroupState { + uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ + uint8_t src_pending; /* Pending source interrupts before masking */ +} CombinerGroupState; + +#define TYPE_EXYNOS4210_COMBINER "exynos4210.combiner" +OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210CombinerState, EXYNOS4210_COMBINER) + +/* Number of groups and total number of interrupts for the internal combiner */ +#define IIC_NGRP 64 +#define IIC_NIRQ (IIC_NGRP * 8) +#define IIC_REGSET_SIZE 0x41 + +struct Exynos4210CombinerState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + struct CombinerGroupState group[IIC_NGRP]; + uint32_t reg_set[IIC_REGSET_SIZE]; + uint32_t icipsr[2]; + uint32_t external; /* 1 means that this combiner is external */ + + qemu_irq output_irq[IIC_NGRP]; +}; + +#endif diff --git a/include/hw/intc/exynos4210_gic.h b/include/hw/intc/exynos4210_gic.h new file mode 100644 index 0000000000..f64c4069c6 --- /dev/null +++ b/include/hw/intc/exynos4210_gic.h @@ -0,0 +1,43 @@ +/* + * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#ifndef HW_INTC_EXYNOS4210_GIC_H +#define HW_INTC_EXYNOS4210_GIC_H + +#include "hw/sysbus.h" + +#define TYPE_EXYNOS4210_GIC "exynos4210.gic" +OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210GicState, EXYNOS4210_GIC) + +#define EXYNOS4210_GIC_NCPUS 2 + +struct Exynos4210GicState { + SysBusDevice parent_obj; + + MemoryRegion cpu_container; + MemoryRegion dist_container; + MemoryRegion cpu_alias[EXYNOS4210_GIC_NCPUS]; + MemoryRegion dist_alias[EXYNOS4210_GIC_NCPUS]; + uint32_t num_cpu; + DeviceState *gic; +}; + +#endif diff --git a/include/hw/intc/nios2_vic.h b/include/hw/intc/nios2_vic.h new file mode 100644 index 0000000000..af1517a967 --- /dev/null +++ b/include/hw/intc/nios2_vic.h @@ -0,0 +1,64 @@ +/* + * Vectored Interrupt Controller for nios2 processor + * + * Copyright (c) 2022 Neuroblade + * + * Interface: + * QOM property "cpu": link to the Nios2 CPU (must be set) + * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines + * IRQ should be connected to nios2 IRQ0. + * + * Reference: "Embedded Peripherals IP User Guide + * for Intel® Quartus® Prime Design Suite: 21.4" + * Chapter 38 "Vectored Interrupt Controller Core" + * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_INTC_NIOS2_VIC +#define HW_INTC_NIOS2_VIC + +#define TYPE_NIOS2_VIC "nios2-vic" +OBJECT_DECLARE_SIMPLE_TYPE(Nios2VIC, NIOS2_VIC) + +#define NIOS2_VIC_MAX_IRQ 32 + +struct Nios2VIC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + qemu_irq output_int; + + /* properties */ + CPUState *cpu; + MemoryRegion csr; + + uint32_t int_config[NIOS2_VIC_MAX_IRQ]; + uint32_t vic_config; + uint32_t int_raw_status; + uint32_t int_enable; + uint32_t sw_int; + uint32_t vic_status; + uint32_t vec_tbl_base; + uint32_t vec_tbl_addr; +}; + +#endif /* HW_INTC_NIOS2_VIC */ diff --git a/include/hw/intc/riscv_aclint.h b/include/hw/intc/riscv_aclint.h index 229bd08d25..26d4048687 100644 --- a/include/hw/intc/riscv_aclint.h +++ b/include/hw/intc/riscv_aclint.h @@ -31,6 +31,7 @@ typedef struct RISCVAclintMTimerState { /*< private >*/ SysBusDevice parent_obj; + uint64_t time_delta; /*< public >*/ MemoryRegion mmio; diff --git a/include/hw/intc/riscv_imsic.h b/include/hw/intc/riscv_imsic.h new file mode 100644 index 0000000000..58c2aaa8dc --- /dev/null +++ b/include/hw/intc/riscv_imsic.h @@ -0,0 +1,68 @@ +/* + * RISC-V IMSIC (Incoming Message Signal Interrupt Controller) interface + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_RISCV_IMSIC_H +#define HW_RISCV_IMSIC_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_RISCV_IMSIC "riscv.imsic" + +typedef struct RISCVIMSICState RISCVIMSICState; +DECLARE_INSTANCE_CHECKER(RISCVIMSICState, RISCV_IMSIC, TYPE_RISCV_IMSIC) + +#define IMSIC_MMIO_PAGE_SHIFT 12 +#define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT) +#define IMSIC_MMIO_SIZE(__num_pages) ((__num_pages) * IMSIC_MMIO_PAGE_SZ) + +#define IMSIC_MMIO_HART_GUEST_MAX_BTIS 6 +#define IMSIC_MMIO_GROUP_MIN_SHIFT 24 + +#define IMSIC_HART_NUM_GUESTS(__guest_bits) \ + (1U << (__guest_bits)) +#define IMSIC_HART_SIZE(__guest_bits) \ + (IMSIC_HART_NUM_GUESTS(__guest_bits) * IMSIC_MMIO_PAGE_SZ) +#define IMSIC_GROUP_NUM_HARTS(__hart_bits) \ + (1U << (__hart_bits)) +#define IMSIC_GROUP_SIZE(__hart_bits, __guest_bits) \ + (IMSIC_GROUP_NUM_HARTS(__hart_bits) * IMSIC_HART_SIZE(__guest_bits)) + +struct RISCVIMSICState { + /*< private >*/ + SysBusDevice parent_obj; + qemu_irq *external_irqs; + + /*< public >*/ + MemoryRegion mmio; + uint32_t num_eistate; + uint32_t *eidelivery; + uint32_t *eithreshold; + uint32_t *eistate; + + /* config */ + bool mmode; + uint32_t hartid; + uint32_t num_pages; + uint32_t num_irqs; +}; + +DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode, + uint32_t num_pages, uint32_t num_ids); + +#endif diff --git a/include/hw/irq.h b/include/hw/irq.h index dc7abf199e..645b73d251 100644 --- a/include/hw/irq.h +++ b/include/hw/irq.h @@ -46,11 +46,6 @@ void qemu_free_irq(qemu_irq irq); /* Returns a new IRQ with opposite polarity. */ qemu_irq qemu_irq_invert(qemu_irq irq); -/* Returns a new IRQ which feeds into both the passed IRQs. - * It's probably better to use the TYPE_SPLIT_IRQ device instead. - */ -qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2); - /* For internal use in qtest. Similar to qemu_irq_split, but operating on an existing vector of qemu_irq. */ void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n); diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index d4417b34b6..034d706ba1 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -83,8 +83,6 @@ struct ISADevice { DeviceState parent_obj; /*< public >*/ - int8_t isairq[2]; /* -1 = unassigned */ - int nirqs; int ioport_id; }; @@ -92,7 +90,6 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, MemoryRegion *address_space_io, Error **errp); void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq); -void isa_init_irq(ISADevice *dev, qemu_irq *p, unsigned isairq); void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq); void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); IsaDma *isa_get_dma(ISABus *bus, int nchan); diff --git a/include/hw/loader.h b/include/hw/loader.h index 4fa485bd61..5572108ba5 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -343,4 +343,18 @@ int rom_add_option(const char *file, int32_t bootindex); * overflow on real hardware too. */ #define UBOOT_MAX_GUNZIP_BYTES (64 << 20) +typedef struct RomGap { + hwaddr base; + size_t size; +} RomGap; + +/** + * rom_find_largest_gap_between: return largest gap between ROMs in given range + * + * Given a range of addresses, this function finds the largest + * contiguous subrange which has no ROMs loaded to it. That is, + * it finds the biggest gap which is free for use for other things. + */ +RomGap rom_find_largest_gap_between(hwaddr base, size_t size); + #endif diff --git a/include/hw/mips/bios.h b/include/hw/mips/bios.h index c03007999a..44acb6815b 100644 --- a/include/hw/mips/bios.h +++ b/include/hw/mips/bios.h @@ -5,7 +5,7 @@ #include "cpu.h" #define BIOS_SIZE (4 * MiB) -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN #define BIOS_FILENAME "mips_bios.bin" #else #define BIOS_FILENAME "mipsel_bios.bin" diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h index 6c9c8805f3..101799f7d3 100644 --- a/include/hw/mips/mips.h +++ b/include/hw/mips/mips.h @@ -9,9 +9,6 @@ #include "exec/memory.h" -/* gt64xxx.c */ -PCIBus *gt64120_register(qemu_irq *pic); - /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index 94d5ada95f..4f9ce179bf 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -15,9 +15,12 @@ #define TYPE_ASPEED_AST2400_HACE TYPE_ASPEED_HACE "-ast2400" #define TYPE_ASPEED_AST2500_HACE TYPE_ASPEED_HACE "-ast2500" #define TYPE_ASPEED_AST2600_HACE TYPE_ASPEED_HACE "-ast2600" +#define TYPE_ASPEED_AST1030_HACE TYPE_ASPEED_HACE "-ast1030" + OBJECT_DECLARE_TYPE(AspeedHACEState, AspeedHACEClass, ASPEED_HACE) #define ASPEED_HACE_NR_REGS (0x64 >> 2) +#define ASPEED_HACE_MAX_SG 256 /* max number of entries */ struct AspeedHACEState { SysBusDevice parent; @@ -25,7 +28,10 @@ struct AspeedHACEState { MemoryRegion iomem; qemu_irq irq; + struct iovec iov_cache[ASPEED_HACE_MAX_SG]; uint32_t regs[ASPEED_HACE_NR_REGS]; + uint32_t total_req_len; + uint32_t iov_count; MemoryRegion *dram_mr; AddressSpace dram_as; @@ -37,6 +43,7 @@ struct AspeedHACEClass { uint32_t src_mask; uint32_t dest_mask; + uint32_t key_mask; uint32_t hash_mask; }; diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h new file mode 100644 index 0000000000..651747e28f --- /dev/null +++ b/include/hw/misc/aspeed_sbc.h @@ -0,0 +1,32 @@ +/* + * ASPEED Secure Boot Controller + * + * Copyright (C) 2021-2022 IBM Corp. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ASPEED_SBC_H +#define ASPEED_SBC_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_SBC "aspeed.sbc" +#define TYPE_ASPEED_AST2600_SBC TYPE_ASPEED_SBC "-ast2600" +OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPEED_SBC) + +#define ASPEED_SBC_NR_REGS (0x93c >> 2) + +struct AspeedSBCState { + SysBusDevice parent; + + MemoryRegion iomem; + + uint32_t regs[ASPEED_SBC_NR_REGS]; +}; + +struct AspeedSBCClass { + SysBusDeviceClass parent_class; +}; + +#endif /* _ASPEED_SBC_H_ */ diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index c14aff2bcb..5c7c04eedf 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -19,6 +19,7 @@ OBJECT_DECLARE_TYPE(AspeedSCUState, AspeedSCUClass, ASPEED_SCU) #define TYPE_ASPEED_2400_SCU TYPE_ASPEED_SCU "-ast2400" #define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500" #define TYPE_ASPEED_2600_SCU TYPE_ASPEED_SCU "-ast2600" +#define TYPE_ASPEED_1030_SCU TYPE_ASPEED_SCU "-ast1030" #define ASPEED_SCU_NR_REGS (0x1A8 >> 2) #define ASPEED_AST2600_SCU_NR_REGS (0xE20 >> 2) @@ -45,6 +46,8 @@ struct AspeedSCUState { #define AST2600_A1_SILICON_REV 0x05010303U #define AST2600_A2_SILICON_REV 0x05020303U #define AST2600_A3_SILICON_REV 0x05030303U +#define AST1030_A0_SILICON_REV 0x80000000U +#define AST1030_A1_SILICON_REV 0x80010000U #define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) @@ -56,8 +59,10 @@ struct AspeedSCUClass { const uint32_t *resets; uint32_t (*calc_hpll)(AspeedSCUState *s, uint32_t hpll_reg); + uint32_t (*get_apb)(AspeedSCUState *s); uint32_t apb_divider; uint32_t nr_regs; + bool clkin_25Mhz; const MemoryRegionOps *ops; }; @@ -316,4 +321,44 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); SCU_HW_STRAP_VGA_SIZE_SET(VGA_16M_DRAM) | \ SCU_AST2500_HW_STRAP_RESERVED1) +/* + * SCU200 H-PLL Parameter Register (for Aspeed AST2600 SOC) + * + * 28:26 H-PLL Parameters + * 25 Enable H-PLL reset + * 24 Enable H-PLL bypass mode + * 23 Turn off H-PLL + * 22:19 H-PLL Post Divider (P) + * 18:13 H-PLL Numerator (M) + * 12:0 H-PLL Denumerator (N) + * + * (Output frequency) = CLKIN(25MHz) * [(M+1) / (N+1)] / (P+1) + * + * The default frequency is 1200Mhz when CLKIN = 25MHz + */ +#define SCU_AST2600_H_PLL_BYPASS_EN (0x1 << 24) +#define SCU_AST2600_H_PLL_OFF (0x1 << 23) + +/* + * SCU310 Clock Selection Register Set 4 (for Aspeed AST1030 SOC) + * + * 31 I3C Clock Source selection + * 30:28 I3C clock divider selection + * 26:24 MAC AHB clock divider selection + * 22:20 RGMII 125MHz clock divider ration + * 19:16 RGMII 50MHz clock divider ration + * 15 LHCLK clock generation/output enable control + * 14:12 LHCLK divider selection + * 11:8 APB Bus PCLK divider selection + * 7 Select PECI clock source + * 6 Select UART debug port clock source + * 5 Select UART6 clock source + * 4 Select UART5 clock source + * 3 Select UART4 clock source + * 2 Select UART3 clock source + * 1 Select UART2 clock source + * 0 Select UART1 clock source + */ +#define SCU_AST1030_CLK_GET_PCLK_DIV(x) (((x) >> 8) & 0xf) + #endif /* ASPEED_SCU_H */ diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index b445565866..5fe7a7f592 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -18,19 +18,17 @@ #define VIA_SIZE 0x2000 /* VIA 1 */ -#define VIA1_IRQ_ONE_SECOND_BIT 0 -#define VIA1_IRQ_60HZ_BIT 1 -#define VIA1_IRQ_ADB_READY_BIT 2 -#define VIA1_IRQ_ADB_DATA_BIT 3 -#define VIA1_IRQ_ADB_CLOCK_BIT 4 +#define VIA1_IRQ_ONE_SECOND_BIT CA2_INT_BIT +#define VIA1_IRQ_60HZ_BIT CA1_INT_BIT +#define VIA1_IRQ_ADB_READY_BIT SR_INT_BIT +#define VIA1_IRQ_ADB_DATA_BIT CB2_INT_BIT +#define VIA1_IRQ_ADB_CLOCK_BIT CB1_INT_BIT -#define VIA1_IRQ_NB 8 - -#define VIA1_IRQ_ONE_SECOND (1 << VIA1_IRQ_ONE_SECOND_BIT) -#define VIA1_IRQ_60HZ (1 << VIA1_IRQ_60HZ_BIT) -#define VIA1_IRQ_ADB_READY (1 << VIA1_IRQ_ADB_READY_BIT) -#define VIA1_IRQ_ADB_DATA (1 << VIA1_IRQ_ADB_DATA_BIT) -#define VIA1_IRQ_ADB_CLOCK (1 << VIA1_IRQ_ADB_CLOCK_BIT) +#define VIA1_IRQ_ONE_SECOND BIT(VIA1_IRQ_ONE_SECOND_BIT) +#define VIA1_IRQ_60HZ BIT(VIA1_IRQ_60HZ_BIT) +#define VIA1_IRQ_ADB_READY BIT(VIA1_IRQ_ADB_READY_BIT) +#define VIA1_IRQ_ADB_DATA BIT(VIA1_IRQ_ADB_DATA_BIT) +#define VIA1_IRQ_ADB_CLOCK BIT(VIA1_IRQ_ADB_CLOCK_BIT) #define TYPE_MOS6522_Q800_VIA1 "mos6522-q800-via1" @@ -42,7 +40,6 @@ struct MOS6522Q800VIA1State { MemoryRegion via_mem; - qemu_irq irqs[VIA1_IRQ_NB]; qemu_irq auxmode_irq; uint8_t last_b; @@ -80,19 +77,16 @@ struct MOS6522Q800VIA1State { /* VIA 2 */ -#define VIA2_IRQ_SCSI_DATA_BIT 0 -#define VIA2_IRQ_NUBUS_BIT 1 -#define VIA2_IRQ_UNUSED_BIT 2 -#define VIA2_IRQ_SCSI_BIT 3 -#define VIA2_IRQ_ASC_BIT 4 +#define VIA2_IRQ_SCSI_DATA_BIT CA2_INT_BIT +#define VIA2_IRQ_NUBUS_BIT CA1_INT_BIT +#define VIA2_IRQ_SCSI_BIT CB2_INT_BIT +#define VIA2_IRQ_ASC_BIT CB1_INT_BIT -#define VIA2_IRQ_NB 8 - -#define VIA2_IRQ_SCSI_DATA (1 << VIA2_IRQ_SCSI_DATA_BIT) -#define VIA2_IRQ_NUBUS (1 << VIA2_IRQ_NUBUS_BIT) -#define VIA2_IRQ_UNUSED (1 << VIA2_IRQ_SCSI_BIT) -#define VIA2_IRQ_SCSI (1 << VIA2_IRQ_UNUSED_BIT) -#define VIA2_IRQ_ASC (1 << VIA2_IRQ_ASC_BIT) +#define VIA2_IRQ_SCSI_DATA BIT(VIA2_IRQ_SCSI_DATA_BIT) +#define VIA2_IRQ_NUBUS BIT(VIA2_IRQ_NUBUS_BIT) +#define VIA2_IRQ_UNUSED BIT(VIA2_IRQ_SCSI_BIT) +#define VIA2_IRQ_SCSI BIT(VIA2_IRQ_UNUSED_BIT) +#define VIA2_IRQ_ASC BIT(VIA2_IRQ_ASC_BIT) #define VIA2_NUBUS_IRQ_NB 7 diff --git a/include/hw/misc/macio/pmu.h b/include/hw/misc/macio/pmu.h index 78237d99a2..00fcdd23f5 100644 --- a/include/hw/misc/macio/pmu.h +++ b/include/hw/misc/macio/pmu.h @@ -193,8 +193,6 @@ struct PMUState { MemoryRegion mem; uint64_t frequency; - qemu_irq via_irq; - bool via_irq_state; /* PMU state */ MOS6522PMUState mos6522_pmu; diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index fc95d22b0f..0bc22a8395 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -32,6 +32,8 @@ #include "hw/input/adb.h" #include "qom/object.h" +#define MOS6522_NUM_REGS 16 + /* Bits in ACR */ #define SR_CTRL 0x1c /* Shift register control bits */ #define SR_EXT 0x0c /* Shift on external clock */ @@ -41,18 +43,43 @@ #define IER_SET 0x80 /* set bits in IER */ #define IER_CLR 0 /* clear bits in IER */ -#define CA2_INT 0x01 -#define CA1_INT 0x02 -#define SR_INT 0x04 /* Shift register full/empty */ -#define CB2_INT 0x08 -#define CB1_INT 0x10 -#define T2_INT 0x20 /* Timer 2 interrupt */ -#define T1_INT 0x40 /* Timer 1 interrupt */ +#define CA2_INT_BIT 0 +#define CA1_INT_BIT 1 +#define SR_INT_BIT 2 /* Shift register full/empty */ +#define CB2_INT_BIT 3 +#define CB1_INT_BIT 4 +#define T2_INT_BIT 5 /* Timer 2 interrupt */ +#define T1_INT_BIT 6 /* Timer 1 interrupt */ + +#define CA2_INT BIT(CA2_INT_BIT) +#define CA1_INT BIT(CA1_INT_BIT) +#define SR_INT BIT(SR_INT_BIT) +#define CB2_INT BIT(CB2_INT_BIT) +#define CB1_INT BIT(CB1_INT_BIT) +#define T2_INT BIT(T2_INT_BIT) +#define T1_INT BIT(T1_INT_BIT) + +#define VIA_NUM_INTS 5 /* Bits in ACR */ #define T1MODE 0xc0 /* Timer 1 mode */ #define T1MODE_CONT 0x40 /* continuous interrupts */ +/* Bits in PCR */ +#define CB2_CTRL_MASK 0xe0 +#define CB2_CTRL_SHIFT 5 +#define CB1_CTRL_MASK 0x10 +#define CB1_CTRL_SHIFT 4 +#define CA2_CTRL_MASK 0x0e +#define CA2_CTRL_SHIFT 1 +#define CA1_CTRL_MASK 0x1 +#define CA1_CTRL_SHIFT 0 + +#define C2_POS 0x2 +#define C2_IND 0x1 + +#define C1_POS 0x1 + /* VIA registers */ #define VIA_REG_B 0x00 #define VIA_REG_A 0x01 @@ -121,6 +148,7 @@ struct MOS6522State { uint64_t frequency; qemu_irq irq; + uint8_t last_irq_levels; }; #define TYPE_MOS6522 "mos6522" @@ -130,10 +158,8 @@ struct MOS6522DeviceClass { DeviceClass parent_class; DeviceReset parent_reset; - void (*set_sr_int)(MOS6522State *dev); void (*portB_write)(MOS6522State *dev); void (*portA_write)(MOS6522State *dev); - void (*update_irq)(MOS6522State *dev); /* These are used to influence the CUDA MacOS timebase calibration */ uint64_t (*get_timer1_counter_value)(MOS6522State *dev, MOS6522Timer *ti); uint64_t (*get_timer2_counter_value)(MOS6522State *dev, MOS6522Timer *ti); @@ -147,4 +173,6 @@ extern const VMStateDescription vmstate_mos6522; uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size); void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); +void hmp_info_via(Monitor *mon, const QDict *qdict); + #endif /* MOS6522_H */ diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm7xx_gcr.h index 13109d9d32..9419e0a7d2 100644 --- a/include/hw/misc/npcm7xx_gcr.h +++ b/include/hw/misc/npcm7xx_gcr.h @@ -19,6 +19,36 @@ #include "exec/memory.h" #include "hw/sysbus.h" +/* + * NPCM7XX PWRON STRAP bit fields + * 12: SPI0 powered by VSBV3 at 1.8V + * 11: System flash attached to BMC + * 10: BSP alternative pins. + * 9:8: Flash UART command route enabled. + * 7: Security enabled. + * 6: HI-Z state control. + * 5: ECC disabled. + * 4: Reserved + * 3: JTAG2 enabled. + * 2:0: CPU and DRAM clock frequency. + */ +#define NPCM7XX_PWRON_STRAP_SPI0F18 BIT(12) +#define NPCM7XX_PWRON_STRAP_SFAB BIT(11) +#define NPCM7XX_PWRON_STRAP_BSPA BIT(10) +#define NPCM7XX_PWRON_STRAP_FUP(x) ((x) << 8) +#define FUP_NORM_UART2 3 +#define FUP_PROG_UART3 2 +#define FUP_PROG_UART2 1 +#define FUP_NORM_UART3 0 +#define NPCM7XX_PWRON_STRAP_SECEN BIT(7) +#define NPCM7XX_PWRON_STRAP_HIZ BIT(6) +#define NPCM7XX_PWRON_STRAP_ECC BIT(5) +#define NPCM7XX_PWRON_STRAP_RESERVE1 BIT(4) +#define NPCM7XX_PWRON_STRAP_J2EN BIT(3) +#define NPCM7XX_PWRON_STRAP_CKFRQ(x) (x) +#define CKFRQ_SKIPINIT 0x000 +#define CKFRQ_DEFAULT 0x111 + /* * Number of registers in our device state structure. Don't change this without * incrementing the version_id in the vmstate. diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index ca3c5bb533..7f16cc9b16 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -22,14 +22,6 @@ #define PVPANIC_IOPORT_PROP "ioport" -/* The bit of supported pv event, TODO: include uapi header and remove this */ -#define PVPANIC_F_PANICKED 0 -#define PVPANIC_F_CRASHLOADED 1 - -/* The pv event value */ -#define PVPANIC_PANICKED (1 << PVPANIC_F_PANICKED) -#define PVPANIC_CRASHLOADED (1 << PVPANIC_F_CRASHLOADED) - /* * PVPanicState for any device type */ diff --git a/include/hw/misc/xlnx-versal-crl.h b/include/hw/misc/xlnx-versal-crl.h new file mode 100644 index 0000000000..2857f4169a --- /dev/null +++ b/include/hw/misc/xlnx-versal-crl.h @@ -0,0 +1,235 @@ +/* + * QEMU model of the Clock-Reset-LPD (CRL). + * + * Copyright (c) 2022 Xilinx Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Written by Edgar E. Iglesias + */ +#ifndef HW_MISC_XLNX_VERSAL_CRL_H +#define HW_MISC_XLNX_VERSAL_CRL_H + +#include "hw/sysbus.h" +#include "hw/register.h" +#include "target/arm/cpu.h" + +#define TYPE_XLNX_VERSAL_CRL "xlnx,versal-crl" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCRL, XLNX_VERSAL_CRL) + +REG32(ERR_CTRL, 0x0) + FIELD(ERR_CTRL, SLVERR_ENABLE, 0, 1) +REG32(IR_STATUS, 0x4) + FIELD(IR_STATUS, ADDR_DECODE_ERR, 0, 1) +REG32(IR_MASK, 0x8) + FIELD(IR_MASK, ADDR_DECODE_ERR, 0, 1) +REG32(IR_ENABLE, 0xc) + FIELD(IR_ENABLE, ADDR_DECODE_ERR, 0, 1) +REG32(IR_DISABLE, 0x10) + FIELD(IR_DISABLE, ADDR_DECODE_ERR, 0, 1) +REG32(WPROT, 0x1c) + FIELD(WPROT, ACTIVE, 0, 1) +REG32(PLL_CLK_OTHER_DMN, 0x20) + FIELD(PLL_CLK_OTHER_DMN, APLL_BYPASS, 0, 1) +REG32(RPLL_CTRL, 0x40) + FIELD(RPLL_CTRL, POST_SRC, 24, 3) + FIELD(RPLL_CTRL, PRE_SRC, 20, 3) + FIELD(RPLL_CTRL, CLKOUTDIV, 16, 2) + FIELD(RPLL_CTRL, FBDIV, 8, 8) + FIELD(RPLL_CTRL, BYPASS, 3, 1) + FIELD(RPLL_CTRL, RESET, 0, 1) +REG32(RPLL_CFG, 0x44) + FIELD(RPLL_CFG, LOCK_DLY, 25, 7) + FIELD(RPLL_CFG, LOCK_CNT, 13, 10) + FIELD(RPLL_CFG, LFHF, 10, 2) + FIELD(RPLL_CFG, CP, 5, 4) + FIELD(RPLL_CFG, RES, 0, 4) +REG32(RPLL_FRAC_CFG, 0x48) + FIELD(RPLL_FRAC_CFG, ENABLED, 31, 1) + FIELD(RPLL_FRAC_CFG, SEED, 22, 3) + FIELD(RPLL_FRAC_CFG, ALGRTHM, 19, 1) + FIELD(RPLL_FRAC_CFG, ORDER, 18, 1) + FIELD(RPLL_FRAC_CFG, DATA, 0, 16) +REG32(PLL_STATUS, 0x50) + FIELD(PLL_STATUS, RPLL_STABLE, 2, 1) + FIELD(PLL_STATUS, RPLL_LOCK, 0, 1) +REG32(RPLL_TO_XPD_CTRL, 0x100) + FIELD(RPLL_TO_XPD_CTRL, CLKACT, 25, 1) + FIELD(RPLL_TO_XPD_CTRL, DIVISOR0, 8, 10) +REG32(LPD_TOP_SWITCH_CTRL, 0x104) + FIELD(LPD_TOP_SWITCH_CTRL, CLKACT_ADMA, 26, 1) + FIELD(LPD_TOP_SWITCH_CTRL, CLKACT, 25, 1) + FIELD(LPD_TOP_SWITCH_CTRL, DIVISOR0, 8, 10) + FIELD(LPD_TOP_SWITCH_CTRL, SRCSEL, 0, 3) +REG32(LPD_LSBUS_CTRL, 0x108) + FIELD(LPD_LSBUS_CTRL, CLKACT, 25, 1) + FIELD(LPD_LSBUS_CTRL, DIVISOR0, 8, 10) + FIELD(LPD_LSBUS_CTRL, SRCSEL, 0, 3) +REG32(CPU_R5_CTRL, 0x10c) + FIELD(CPU_R5_CTRL, CLKACT_OCM2, 28, 1) + FIELD(CPU_R5_CTRL, CLKACT_OCM, 27, 1) + FIELD(CPU_R5_CTRL, CLKACT_CORE, 26, 1) + FIELD(CPU_R5_CTRL, CLKACT, 25, 1) + FIELD(CPU_R5_CTRL, DIVISOR0, 8, 10) + FIELD(CPU_R5_CTRL, SRCSEL, 0, 3) +REG32(IOU_SWITCH_CTRL, 0x114) + FIELD(IOU_SWITCH_CTRL, CLKACT, 25, 1) + FIELD(IOU_SWITCH_CTRL, DIVISOR0, 8, 10) + FIELD(IOU_SWITCH_CTRL, SRCSEL, 0, 3) +REG32(GEM0_REF_CTRL, 0x118) + FIELD(GEM0_REF_CTRL, CLKACT_RX, 27, 1) + FIELD(GEM0_REF_CTRL, CLKACT_TX, 26, 1) + FIELD(GEM0_REF_CTRL, CLKACT, 25, 1) + FIELD(GEM0_REF_CTRL, DIVISOR0, 8, 10) + FIELD(GEM0_REF_CTRL, SRCSEL, 0, 3) +REG32(GEM1_REF_CTRL, 0x11c) + FIELD(GEM1_REF_CTRL, CLKACT_RX, 27, 1) + FIELD(GEM1_REF_CTRL, CLKACT_TX, 26, 1) + FIELD(GEM1_REF_CTRL, CLKACT, 25, 1) + FIELD(GEM1_REF_CTRL, DIVISOR0, 8, 10) + FIELD(GEM1_REF_CTRL, SRCSEL, 0, 3) +REG32(GEM_TSU_REF_CTRL, 0x120) + FIELD(GEM_TSU_REF_CTRL, CLKACT, 25, 1) + FIELD(GEM_TSU_REF_CTRL, DIVISOR0, 8, 10) + FIELD(GEM_TSU_REF_CTRL, SRCSEL, 0, 3) +REG32(USB0_BUS_REF_CTRL, 0x124) + FIELD(USB0_BUS_REF_CTRL, CLKACT, 25, 1) + FIELD(USB0_BUS_REF_CTRL, DIVISOR0, 8, 10) + FIELD(USB0_BUS_REF_CTRL, SRCSEL, 0, 3) +REG32(UART0_REF_CTRL, 0x128) + FIELD(UART0_REF_CTRL, CLKACT, 25, 1) + FIELD(UART0_REF_CTRL, DIVISOR0, 8, 10) + FIELD(UART0_REF_CTRL, SRCSEL, 0, 3) +REG32(UART1_REF_CTRL, 0x12c) + FIELD(UART1_REF_CTRL, CLKACT, 25, 1) + FIELD(UART1_REF_CTRL, DIVISOR0, 8, 10) + FIELD(UART1_REF_CTRL, SRCSEL, 0, 3) +REG32(SPI0_REF_CTRL, 0x130) + FIELD(SPI0_REF_CTRL, CLKACT, 25, 1) + FIELD(SPI0_REF_CTRL, DIVISOR0, 8, 10) + FIELD(SPI0_REF_CTRL, SRCSEL, 0, 3) +REG32(SPI1_REF_CTRL, 0x134) + FIELD(SPI1_REF_CTRL, CLKACT, 25, 1) + FIELD(SPI1_REF_CTRL, DIVISOR0, 8, 10) + FIELD(SPI1_REF_CTRL, SRCSEL, 0, 3) +REG32(CAN0_REF_CTRL, 0x138) + FIELD(CAN0_REF_CTRL, CLKACT, 25, 1) + FIELD(CAN0_REF_CTRL, DIVISOR0, 8, 10) + FIELD(CAN0_REF_CTRL, SRCSEL, 0, 3) +REG32(CAN1_REF_CTRL, 0x13c) + FIELD(CAN1_REF_CTRL, CLKACT, 25, 1) + FIELD(CAN1_REF_CTRL, DIVISOR0, 8, 10) + FIELD(CAN1_REF_CTRL, SRCSEL, 0, 3) +REG32(I2C0_REF_CTRL, 0x140) + FIELD(I2C0_REF_CTRL, CLKACT, 25, 1) + FIELD(I2C0_REF_CTRL, DIVISOR0, 8, 10) + FIELD(I2C0_REF_CTRL, SRCSEL, 0, 3) +REG32(I2C1_REF_CTRL, 0x144) + FIELD(I2C1_REF_CTRL, CLKACT, 25, 1) + FIELD(I2C1_REF_CTRL, DIVISOR0, 8, 10) + FIELD(I2C1_REF_CTRL, SRCSEL, 0, 3) +REG32(DBG_LPD_CTRL, 0x148) + FIELD(DBG_LPD_CTRL, CLKACT, 25, 1) + FIELD(DBG_LPD_CTRL, DIVISOR0, 8, 10) + FIELD(DBG_LPD_CTRL, SRCSEL, 0, 3) +REG32(TIMESTAMP_REF_CTRL, 0x14c) + FIELD(TIMESTAMP_REF_CTRL, CLKACT, 25, 1) + FIELD(TIMESTAMP_REF_CTRL, DIVISOR0, 8, 10) + FIELD(TIMESTAMP_REF_CTRL, SRCSEL, 0, 3) +REG32(CRL_SAFETY_CHK, 0x150) +REG32(PSM_REF_CTRL, 0x154) + FIELD(PSM_REF_CTRL, DIVISOR0, 8, 10) + FIELD(PSM_REF_CTRL, SRCSEL, 0, 3) +REG32(DBG_TSTMP_CTRL, 0x158) + FIELD(DBG_TSTMP_CTRL, CLKACT, 25, 1) + FIELD(DBG_TSTMP_CTRL, DIVISOR0, 8, 10) + FIELD(DBG_TSTMP_CTRL, SRCSEL, 0, 3) +REG32(CPM_TOPSW_REF_CTRL, 0x15c) + FIELD(CPM_TOPSW_REF_CTRL, CLKACT, 25, 1) + FIELD(CPM_TOPSW_REF_CTRL, DIVISOR0, 8, 10) + FIELD(CPM_TOPSW_REF_CTRL, SRCSEL, 0, 3) +REG32(USB3_DUAL_REF_CTRL, 0x160) + FIELD(USB3_DUAL_REF_CTRL, CLKACT, 25, 1) + FIELD(USB3_DUAL_REF_CTRL, DIVISOR0, 8, 10) + FIELD(USB3_DUAL_REF_CTRL, SRCSEL, 0, 3) +REG32(RST_CPU_R5, 0x300) + FIELD(RST_CPU_R5, RESET_PGE, 4, 1) + FIELD(RST_CPU_R5, RESET_AMBA, 2, 1) + FIELD(RST_CPU_R5, RESET_CPU1, 1, 1) + FIELD(RST_CPU_R5, RESET_CPU0, 0, 1) +REG32(RST_ADMA, 0x304) + FIELD(RST_ADMA, RESET, 0, 1) +REG32(RST_GEM0, 0x308) + FIELD(RST_GEM0, RESET, 0, 1) +REG32(RST_GEM1, 0x30c) + FIELD(RST_GEM1, RESET, 0, 1) +REG32(RST_SPARE, 0x310) + FIELD(RST_SPARE, RESET, 0, 1) +REG32(RST_USB0, 0x314) + FIELD(RST_USB0, RESET, 0, 1) +REG32(RST_UART0, 0x318) + FIELD(RST_UART0, RESET, 0, 1) +REG32(RST_UART1, 0x31c) + FIELD(RST_UART1, RESET, 0, 1) +REG32(RST_SPI0, 0x320) + FIELD(RST_SPI0, RESET, 0, 1) +REG32(RST_SPI1, 0x324) + FIELD(RST_SPI1, RESET, 0, 1) +REG32(RST_CAN0, 0x328) + FIELD(RST_CAN0, RESET, 0, 1) +REG32(RST_CAN1, 0x32c) + FIELD(RST_CAN1, RESET, 0, 1) +REG32(RST_I2C0, 0x330) + FIELD(RST_I2C0, RESET, 0, 1) +REG32(RST_I2C1, 0x334) + FIELD(RST_I2C1, RESET, 0, 1) +REG32(RST_DBG_LPD, 0x338) + FIELD(RST_DBG_LPD, RPU_DBG1_RESET, 5, 1) + FIELD(RST_DBG_LPD, RPU_DBG0_RESET, 4, 1) + FIELD(RST_DBG_LPD, RESET_HSDP, 1, 1) + FIELD(RST_DBG_LPD, RESET, 0, 1) +REG32(RST_GPIO, 0x33c) + FIELD(RST_GPIO, RESET, 0, 1) +REG32(RST_TTC, 0x344) + FIELD(RST_TTC, TTC3_RESET, 3, 1) + FIELD(RST_TTC, TTC2_RESET, 2, 1) + FIELD(RST_TTC, TTC1_RESET, 1, 1) + FIELD(RST_TTC, TTC0_RESET, 0, 1) +REG32(RST_TIMESTAMP, 0x348) + FIELD(RST_TIMESTAMP, RESET, 0, 1) +REG32(RST_SWDT, 0x34c) + FIELD(RST_SWDT, RESET, 0, 1) +REG32(RST_OCM, 0x350) + FIELD(RST_OCM, RESET, 0, 1) +REG32(RST_IPI, 0x354) + FIELD(RST_IPI, RESET, 0, 1) +REG32(RST_SYSMON, 0x358) + FIELD(RST_SYSMON, SEQ_RST, 1, 1) + FIELD(RST_SYSMON, CFG_RST, 0, 1) +REG32(RST_FPD, 0x360) + FIELD(RST_FPD, SRST, 1, 1) + FIELD(RST_FPD, POR, 0, 1) +REG32(PSM_RST_MODE, 0x370) + FIELD(PSM_RST_MODE, WAKEUP, 2, 1) + FIELD(PSM_RST_MODE, RST_MODE, 0, 2) + +#define CRL_R_MAX (R_PSM_RST_MODE + 1) + +#define RPU_MAX_CPU 2 + +struct XlnxVersalCRL { + SysBusDevice parent_obj; + qemu_irq irq; + + struct { + ARMCPU *cpu_r5[RPU_MAX_CPU]; + DeviceState *adma[8]; + DeviceState *uart[2]; + DeviceState *gem[2]; + DeviceState *usb; + } cfg; + + RegisterInfoArray *reg_array; + uint32_t regs[CRL_R_MAX]; + RegisterInfo regs_info[CRL_R_MAX]; +}; +#endif diff --git a/include/hw/misc/xlnx-zynqmp-apu-ctrl.h b/include/hw/misc/xlnx-zynqmp-apu-ctrl.h new file mode 100644 index 0000000000..b8ca9434af --- /dev/null +++ b/include/hw/misc/xlnx-zynqmp-apu-ctrl.h @@ -0,0 +1,93 @@ +/* + * QEMU model of ZynqMP APU Control. + * + * Copyright (c) 2013-2022 Xilinx Inc + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Written by Peter Crosthwaite and + * Edgar E. Iglesias + * + */ +#ifndef HW_MISC_XLNX_ZYNQMP_APU_CTRL_H +#define HW_MISC_XLNX_ZYNQMP_APU_CTRL_H + +#include "hw/sysbus.h" +#include "hw/register.h" +#include "target/arm/cpu.h" + +#define TYPE_XLNX_ZYNQMP_APU_CTRL "xlnx.apu-ctrl" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPAPUCtrl, XLNX_ZYNQMP_APU_CTRL) + +REG32(APU_ERR_CTRL, 0x0) + FIELD(APU_ERR_CTRL, PSLVERR, 0, 1) +REG32(ISR, 0x10) + FIELD(ISR, INV_APB, 0, 1) +REG32(IMR, 0x14) + FIELD(IMR, INV_APB, 0, 1) +REG32(IEN, 0x18) + FIELD(IEN, INV_APB, 0, 1) +REG32(IDS, 0x1c) + FIELD(IDS, INV_APB, 0, 1) +REG32(CONFIG_0, 0x20) + FIELD(CONFIG_0, CFGTE, 24, 4) + FIELD(CONFIG_0, CFGEND, 16, 4) + FIELD(CONFIG_0, VINITHI, 8, 4) + FIELD(CONFIG_0, AA64NAA32, 0, 4) +REG32(CONFIG_1, 0x24) + FIELD(CONFIG_1, L2RSTDISABLE, 29, 1) + FIELD(CONFIG_1, L1RSTDISABLE, 28, 1) + FIELD(CONFIG_1, CP15DISABLE, 0, 4) +REG32(RVBARADDR0L, 0x40) + FIELD(RVBARADDR0L, ADDR, 2, 30) +REG32(RVBARADDR0H, 0x44) + FIELD(RVBARADDR0H, ADDR, 0, 8) +REG32(RVBARADDR1L, 0x48) + FIELD(RVBARADDR1L, ADDR, 2, 30) +REG32(RVBARADDR1H, 0x4c) + FIELD(RVBARADDR1H, ADDR, 0, 8) +REG32(RVBARADDR2L, 0x50) + FIELD(RVBARADDR2L, ADDR, 2, 30) +REG32(RVBARADDR2H, 0x54) + FIELD(RVBARADDR2H, ADDR, 0, 8) +REG32(RVBARADDR3L, 0x58) + FIELD(RVBARADDR3L, ADDR, 2, 30) +REG32(RVBARADDR3H, 0x5c) + FIELD(RVBARADDR3H, ADDR, 0, 8) +REG32(ACE_CTRL, 0x60) + FIELD(ACE_CTRL, AWQOS, 16, 4) + FIELD(ACE_CTRL, ARQOS, 0, 4) +REG32(SNOOP_CTRL, 0x80) + FIELD(SNOOP_CTRL, ACE_INACT, 4, 1) + FIELD(SNOOP_CTRL, ACP_INACT, 0, 1) +REG32(PWRCTL, 0x90) + FIELD(PWRCTL, CLREXMONREQ, 17, 1) + FIELD(PWRCTL, L2FLUSHREQ, 16, 1) + FIELD(PWRCTL, CPUPWRDWNREQ, 0, 4) +REG32(PWRSTAT, 0x94) + FIELD(PWRSTAT, CLREXMONACK, 17, 1) + FIELD(PWRSTAT, L2FLUSHDONE, 16, 1) + FIELD(PWRSTAT, DBGNOPWRDWN, 0, 4) + +#define APU_R_MAX ((R_PWRSTAT) + 1) + +#define APU_MAX_CPU 4 + +struct XlnxZynqMPAPUCtrl { + SysBusDevice busdev; + + ARMCPU *cpus[APU_MAX_CPU]; + /* WFIs towards PMU. */ + qemu_irq wfi_out[4]; + /* CPU Power status towards INTC Redirect. */ + qemu_irq cpu_power_status[4]; + qemu_irq irq_imr; + + uint8_t cpu_pwrdwn_req; + uint8_t cpu_in_wfi; + + RegisterInfoArray *reg_array; + uint32_t regs[APU_R_MAX]; + RegisterInfo regs_info[APU_R_MAX]; +}; + +#endif diff --git a/include/hw/misc/xlnx-zynqmp-crf.h b/include/hw/misc/xlnx-zynqmp-crf.h new file mode 100644 index 0000000000..02ef0bdeee --- /dev/null +++ b/include/hw/misc/xlnx-zynqmp-crf.h @@ -0,0 +1,211 @@ +/* + * QEMU model of the CRF - Clock Reset FPD. + * + * Copyright (c) 2022 Xilinx Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * Written by Edgar E. Iglesias + */ +#ifndef HW_MISC_XLNX_ZYNQMP_CRF_H +#define HW_MISC_XLNX_ZYNQMP_CRF_H + +#include "hw/sysbus.h" +#include "hw/register.h" + +#define TYPE_XLNX_ZYNQMP_CRF "xlnx.zynqmp_crf" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPCRF, XLNX_ZYNQMP_CRF) + +REG32(ERR_CTRL, 0x0) + FIELD(ERR_CTRL, SLVERR_ENABLE, 0, 1) +REG32(IR_STATUS, 0x4) + FIELD(IR_STATUS, ADDR_DECODE_ERR, 0, 1) +REG32(IR_MASK, 0x8) + FIELD(IR_MASK, ADDR_DECODE_ERR, 0, 1) +REG32(IR_ENABLE, 0xc) + FIELD(IR_ENABLE, ADDR_DECODE_ERR, 0, 1) +REG32(IR_DISABLE, 0x10) + FIELD(IR_DISABLE, ADDR_DECODE_ERR, 0, 1) +REG32(CRF_WPROT, 0x1c) + FIELD(CRF_WPROT, ACTIVE, 0, 1) +REG32(APLL_CTRL, 0x20) + FIELD(APLL_CTRL, POST_SRC, 24, 3) + FIELD(APLL_CTRL, PRE_SRC, 20, 3) + FIELD(APLL_CTRL, CLKOUTDIV, 17, 1) + FIELD(APLL_CTRL, DIV2, 16, 1) + FIELD(APLL_CTRL, FBDIV, 8, 7) + FIELD(APLL_CTRL, BYPASS, 3, 1) + FIELD(APLL_CTRL, RESET, 0, 1) +REG32(APLL_CFG, 0x24) + FIELD(APLL_CFG, LOCK_DLY, 25, 7) + FIELD(APLL_CFG, LOCK_CNT, 13, 10) + FIELD(APLL_CFG, LFHF, 10, 2) + FIELD(APLL_CFG, CP, 5, 4) + FIELD(APLL_CFG, RES, 0, 4) +REG32(APLL_FRAC_CFG, 0x28) + FIELD(APLL_FRAC_CFG, ENABLED, 31, 1) + FIELD(APLL_FRAC_CFG, SEED, 22, 3) + FIELD(APLL_FRAC_CFG, ALGRTHM, 19, 1) + FIELD(APLL_FRAC_CFG, ORDER, 18, 1) + FIELD(APLL_FRAC_CFG, DATA, 0, 16) +REG32(DPLL_CTRL, 0x2c) + FIELD(DPLL_CTRL, POST_SRC, 24, 3) + FIELD(DPLL_CTRL, PRE_SRC, 20, 3) + FIELD(DPLL_CTRL, CLKOUTDIV, 17, 1) + FIELD(DPLL_CTRL, DIV2, 16, 1) + FIELD(DPLL_CTRL, FBDIV, 8, 7) + FIELD(DPLL_CTRL, BYPASS, 3, 1) + FIELD(DPLL_CTRL, RESET, 0, 1) +REG32(DPLL_CFG, 0x30) + FIELD(DPLL_CFG, LOCK_DLY, 25, 7) + FIELD(DPLL_CFG, LOCK_CNT, 13, 10) + FIELD(DPLL_CFG, LFHF, 10, 2) + FIELD(DPLL_CFG, CP, 5, 4) + FIELD(DPLL_CFG, RES, 0, 4) +REG32(DPLL_FRAC_CFG, 0x34) + FIELD(DPLL_FRAC_CFG, ENABLED, 31, 1) + FIELD(DPLL_FRAC_CFG, SEED, 22, 3) + FIELD(DPLL_FRAC_CFG, ALGRTHM, 19, 1) + FIELD(DPLL_FRAC_CFG, ORDER, 18, 1) + FIELD(DPLL_FRAC_CFG, DATA, 0, 16) +REG32(VPLL_CTRL, 0x38) + FIELD(VPLL_CTRL, POST_SRC, 24, 3) + FIELD(VPLL_CTRL, PRE_SRC, 20, 3) + FIELD(VPLL_CTRL, CLKOUTDIV, 17, 1) + FIELD(VPLL_CTRL, DIV2, 16, 1) + FIELD(VPLL_CTRL, FBDIV, 8, 7) + FIELD(VPLL_CTRL, BYPASS, 3, 1) + FIELD(VPLL_CTRL, RESET, 0, 1) +REG32(VPLL_CFG, 0x3c) + FIELD(VPLL_CFG, LOCK_DLY, 25, 7) + FIELD(VPLL_CFG, LOCK_CNT, 13, 10) + FIELD(VPLL_CFG, LFHF, 10, 2) + FIELD(VPLL_CFG, CP, 5, 4) + FIELD(VPLL_CFG, RES, 0, 4) +REG32(VPLL_FRAC_CFG, 0x40) + FIELD(VPLL_FRAC_CFG, ENABLED, 31, 1) + FIELD(VPLL_FRAC_CFG, SEED, 22, 3) + FIELD(VPLL_FRAC_CFG, ALGRTHM, 19, 1) + FIELD(VPLL_FRAC_CFG, ORDER, 18, 1) + FIELD(VPLL_FRAC_CFG, DATA, 0, 16) +REG32(PLL_STATUS, 0x44) + FIELD(PLL_STATUS, VPLL_STABLE, 5, 1) + FIELD(PLL_STATUS, DPLL_STABLE, 4, 1) + FIELD(PLL_STATUS, APLL_STABLE, 3, 1) + FIELD(PLL_STATUS, VPLL_LOCK, 2, 1) + FIELD(PLL_STATUS, DPLL_LOCK, 1, 1) + FIELD(PLL_STATUS, APLL_LOCK, 0, 1) +REG32(APLL_TO_LPD_CTRL, 0x48) + FIELD(APLL_TO_LPD_CTRL, DIVISOR0, 8, 6) +REG32(DPLL_TO_LPD_CTRL, 0x4c) + FIELD(DPLL_TO_LPD_CTRL, DIVISOR0, 8, 6) +REG32(VPLL_TO_LPD_CTRL, 0x50) + FIELD(VPLL_TO_LPD_CTRL, DIVISOR0, 8, 6) +REG32(ACPU_CTRL, 0x60) + FIELD(ACPU_CTRL, CLKACT_HALF, 25, 1) + FIELD(ACPU_CTRL, CLKACT_FULL, 24, 1) + FIELD(ACPU_CTRL, DIVISOR0, 8, 6) + FIELD(ACPU_CTRL, SRCSEL, 0, 3) +REG32(DBG_TRACE_CTRL, 0x64) + FIELD(DBG_TRACE_CTRL, CLKACT, 24, 1) + FIELD(DBG_TRACE_CTRL, DIVISOR0, 8, 6) + FIELD(DBG_TRACE_CTRL, SRCSEL, 0, 3) +REG32(DBG_FPD_CTRL, 0x68) + FIELD(DBG_FPD_CTRL, CLKACT, 24, 1) + FIELD(DBG_FPD_CTRL, DIVISOR0, 8, 6) + FIELD(DBG_FPD_CTRL, SRCSEL, 0, 3) +REG32(DP_VIDEO_REF_CTRL, 0x70) + FIELD(DP_VIDEO_REF_CTRL, CLKACT, 24, 1) + FIELD(DP_VIDEO_REF_CTRL, DIVISOR1, 16, 6) + FIELD(DP_VIDEO_REF_CTRL, DIVISOR0, 8, 6) + FIELD(DP_VIDEO_REF_CTRL, SRCSEL, 0, 3) +REG32(DP_AUDIO_REF_CTRL, 0x74) + FIELD(DP_AUDIO_REF_CTRL, CLKACT, 24, 1) + FIELD(DP_AUDIO_REF_CTRL, DIVISOR1, 16, 6) + FIELD(DP_AUDIO_REF_CTRL, DIVISOR0, 8, 6) + FIELD(DP_AUDIO_REF_CTRL, SRCSEL, 0, 3) +REG32(DP_STC_REF_CTRL, 0x7c) + FIELD(DP_STC_REF_CTRL, CLKACT, 24, 1) + FIELD(DP_STC_REF_CTRL, DIVISOR1, 16, 6) + FIELD(DP_STC_REF_CTRL, DIVISOR0, 8, 6) + FIELD(DP_STC_REF_CTRL, SRCSEL, 0, 3) +REG32(DDR_CTRL, 0x80) + FIELD(DDR_CTRL, CLKACT, 24, 1) + FIELD(DDR_CTRL, DIVISOR0, 8, 6) + FIELD(DDR_CTRL, SRCSEL, 0, 3) +REG32(GPU_REF_CTRL, 0x84) + FIELD(GPU_REF_CTRL, PP1_CLKACT, 26, 1) + FIELD(GPU_REF_CTRL, PP0_CLKACT, 25, 1) + FIELD(GPU_REF_CTRL, CLKACT, 24, 1) + FIELD(GPU_REF_CTRL, DIVISOR0, 8, 6) + FIELD(GPU_REF_CTRL, SRCSEL, 0, 3) +REG32(SATA_REF_CTRL, 0xa0) + FIELD(SATA_REF_CTRL, CLKACT, 24, 1) + FIELD(SATA_REF_CTRL, DIVISOR0, 8, 6) + FIELD(SATA_REF_CTRL, SRCSEL, 0, 3) +REG32(PCIE_REF_CTRL, 0xb4) + FIELD(PCIE_REF_CTRL, CLKACT, 24, 1) + FIELD(PCIE_REF_CTRL, DIVISOR0, 8, 6) + FIELD(PCIE_REF_CTRL, SRCSEL, 0, 3) +REG32(GDMA_REF_CTRL, 0xb8) + FIELD(GDMA_REF_CTRL, CLKACT, 24, 1) + FIELD(GDMA_REF_CTRL, DIVISOR0, 8, 6) + FIELD(GDMA_REF_CTRL, SRCSEL, 0, 3) +REG32(DPDMA_REF_CTRL, 0xbc) + FIELD(DPDMA_REF_CTRL, CLKACT, 24, 1) + FIELD(DPDMA_REF_CTRL, DIVISOR0, 8, 6) + FIELD(DPDMA_REF_CTRL, SRCSEL, 0, 3) +REG32(TOPSW_MAIN_CTRL, 0xc0) + FIELD(TOPSW_MAIN_CTRL, CLKACT, 24, 1) + FIELD(TOPSW_MAIN_CTRL, DIVISOR0, 8, 6) + FIELD(TOPSW_MAIN_CTRL, SRCSEL, 0, 3) +REG32(TOPSW_LSBUS_CTRL, 0xc4) + FIELD(TOPSW_LSBUS_CTRL, CLKACT, 24, 1) + FIELD(TOPSW_LSBUS_CTRL, DIVISOR0, 8, 6) + FIELD(TOPSW_LSBUS_CTRL, SRCSEL, 0, 3) +REG32(DBG_TSTMP_CTRL, 0xf8) + FIELD(DBG_TSTMP_CTRL, DIVISOR0, 8, 6) + FIELD(DBG_TSTMP_CTRL, SRCSEL, 0, 3) +REG32(RST_FPD_TOP, 0x100) + FIELD(RST_FPD_TOP, PCIE_CFG_RESET, 19, 1) + FIELD(RST_FPD_TOP, PCIE_BRIDGE_RESET, 18, 1) + FIELD(RST_FPD_TOP, PCIE_CTRL_RESET, 17, 1) + FIELD(RST_FPD_TOP, DP_RESET, 16, 1) + FIELD(RST_FPD_TOP, SWDT_RESET, 15, 1) + FIELD(RST_FPD_TOP, AFI_FM5_RESET, 12, 1) + FIELD(RST_FPD_TOP, AFI_FM4_RESET, 11, 1) + FIELD(RST_FPD_TOP, AFI_FM3_RESET, 10, 1) + FIELD(RST_FPD_TOP, AFI_FM2_RESET, 9, 1) + FIELD(RST_FPD_TOP, AFI_FM1_RESET, 8, 1) + FIELD(RST_FPD_TOP, AFI_FM0_RESET, 7, 1) + FIELD(RST_FPD_TOP, GDMA_RESET, 6, 1) + FIELD(RST_FPD_TOP, GPU_PP1_RESET, 5, 1) + FIELD(RST_FPD_TOP, GPU_PP0_RESET, 4, 1) + FIELD(RST_FPD_TOP, GPU_RESET, 3, 1) + FIELD(RST_FPD_TOP, GT_RESET, 2, 1) + FIELD(RST_FPD_TOP, SATA_RESET, 1, 1) +REG32(RST_FPD_APU, 0x104) + FIELD(RST_FPD_APU, ACPU3_PWRON_RESET, 13, 1) + FIELD(RST_FPD_APU, ACPU2_PWRON_RESET, 12, 1) + FIELD(RST_FPD_APU, ACPU1_PWRON_RESET, 11, 1) + FIELD(RST_FPD_APU, ACPU0_PWRON_RESET, 10, 1) + FIELD(RST_FPD_APU, APU_L2_RESET, 8, 1) + FIELD(RST_FPD_APU, ACPU3_RESET, 3, 1) + FIELD(RST_FPD_APU, ACPU2_RESET, 2, 1) + FIELD(RST_FPD_APU, ACPU1_RESET, 1, 1) + FIELD(RST_FPD_APU, ACPU0_RESET, 0, 1) +REG32(RST_DDR_SS, 0x108) + FIELD(RST_DDR_SS, DDR_RESET, 3, 1) + FIELD(RST_DDR_SS, APM_RESET, 2, 1) + +#define CRF_R_MAX (R_RST_DDR_SS + 1) + +struct XlnxZynqMPCRF { + SysBusDevice parent_obj; + MemoryRegion iomem; + qemu_irq irq_ir; + + RegisterInfoArray *reg_array; + uint32_t regs[CRF_R_MAX]; + RegisterInfo regs_info[CRF_R_MAX]; +}; + +#endif diff --git a/include/hw/pci-bridge/xio3130_downstream.h b/include/hw/pci-bridge/xio3130_downstream.h new file mode 100644 index 0000000000..1d10139aea --- /dev/null +++ b/include/hw/pci-bridge/xio3130_downstream.h @@ -0,0 +1,15 @@ +/* + * TI X3130 pci express downstream port switch + * + * Copyright (C) 2022 Igor Mammedov + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PCI_BRIDGE_XIO3130_DOWNSTREAM_H +#define HW_PCI_BRIDGE_XIO3130_DOWNSTREAM_H + +#define TYPE_XIO3130_DOWNSTREAM "xio3130-downstream" + +#endif + diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h index 0c7635dec5..19dcbd6f87 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -49,6 +49,7 @@ typedef struct PnvPhb4DMASpace { */ #define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root" #define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port" +#define TYPE_PNV_PHB5_ROOT_PORT "pnv-phb5-root-port" typedef struct PnvPHB4RootPort { PCIESlot parent_obj; @@ -179,7 +180,7 @@ struct PnvPhb4PecState { MemoryRegion nest_regs_mr; /* PCI registers, excluding per-stack */ -#define PHB4_PEC_PCI_REGS_COUNT 0x2 +#define PHB4_PEC_PCI_REGS_COUNT 0x3 uint64_t pci_regs[PHB4_PEC_PCI_REGS_COUNT]; MemoryRegion pci_regs_mr; @@ -202,8 +203,24 @@ struct PnvPhb4PecClass { const char *stk_compat; int stk_compat_size; uint64_t version; + const char *phb_type; const uint32_t *num_phbs; const char *rp_model; }; +/* + * POWER10 definitions + */ + +#define TYPE_PNV_PHB5 "pnv-phb5" +#define PNV_PHB5(obj) \ + OBJECT_CHECK(PnvPhb4, (obj), TYPE_PNV_PHB5) + +#define PNV_PHB5_VERSION 0x000000a500000001ull +#define PNV_PHB5_DEVICE_ID 0x0652 + +#define TYPE_PNV_PHB5_PEC "pnv-phb5-pec" +#define PNV_PHB5_PEC(obj) \ + OBJECT_CHECK(PnvPhb4PecState, (obj), TYPE_PNV_PHB5_PEC) + #endif /* PCI_HOST_PNV_PHB4_H */ diff --git a/include/hw/pci-host/pnv_phb4_regs.h b/include/hw/pci-host/pnv_phb4_regs.h index 55df2c3e5e..4a0d3b28ef 100644 --- a/include/hw/pci-host/pnv_phb4_regs.h +++ b/include/hw/pci-host/pnv_phb4_regs.h @@ -220,11 +220,14 @@ #define PHB_PAPR_ERR_INJ_MASK_MMIO PPC_BITMASK(16, 63) #define PHB_ETU_ERR_SUMMARY 0x2c8 #define PHB_INT_NOTIFY_ADDR 0x300 +#define PHB_INT_NOTIFY_ADDR_64K PPC_BIT(1) /* P10 */ #define PHB_INT_NOTIFY_INDEX 0x308 /* Fundamental register set B */ #define PHB_VERSION 0x800 #define PHB_CTRLR 0x810 +#define PHB_CTRLR_IRQ_PQ_DISABLE PPC_BIT(9) /* P10 */ +#define PHB_CTRLR_IRQ_ABT_MODE PPC_BIT(10) /* P10 */ #define PHB_CTRLR_IRQ_PGSZ_64K PPC_BIT(11) #define PHB_CTRLR_IRQ_STORE_EOI PPC_BIT(12) #define PHB_CTRLR_MMIO_RD_STRICT PPC_BIT(13) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index c3f3c90473..3a32b8dd40 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -7,9 +7,6 @@ /* PCI includes legacy ISA access. */ #include "hw/isa/isa.h" -#include "hw/pci/pcie.h" -#include "qom/object.h" - extern bool pci_available; /* PCI bus */ @@ -157,6 +154,7 @@ enum { #define QEMU_PCI_VGA_IO_HI_SIZE 0x20 #include "hw/pci/pci_regs.h" +#include "hw/pci/pcie.h" /* PCI HEADER_TYPE */ #define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 @@ -499,6 +497,9 @@ typedef AddressSpace *(*PCIIOMMUFunc)(PCIBus *, void *, int); AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque); +pcibus_t pci_bar_address(PCIDevice *d, + int reg, uint8_t type, pcibus_t size); + static inline void pci_set_byte(uint8_t *config, uint8_t val) { @@ -779,6 +780,11 @@ static inline int pci_is_express_downstream_port(const PCIDevice *d) return type == PCI_EXP_TYPE_DOWNSTREAM || type == PCI_EXP_TYPE_ROOT_PORT; } +static inline int pci_is_vf(const PCIDevice *d) +{ + return d->exp.sriov_vf.pf != NULL; +} + static inline uint32_t pci_config_size(const PCIDevice *d) { return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; diff --git a/include/hw/pci/pci_regs.h b/include/hw/pci/pci_regs.h index 77ba64b931..a590140962 100644 --- a/include/hw/pci/pci_regs.h +++ b/include/hw/pci/pci_regs.h @@ -4,5 +4,6 @@ #include "standard-headers/linux/pci_regs.h" #define PCI_PM_CAP_VER_1_1 0x0002 /* PCI PM spec ver. 1.1 */ +#define PCI_PM_CAP_VER_1_2 0x0003 /* PCI PM spec ver. 1.2 */ #endif diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 6063bee0ec..798a262a0a 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -24,6 +24,7 @@ #include "hw/pci/pci_regs.h" #include "hw/pci/pcie_regs.h" #include "hw/pci/pcie_aer.h" +#include "hw/pci/pcie_sriov.h" #include "hw/hotplug.h" typedef enum { @@ -81,6 +82,11 @@ struct PCIExpressDevice { /* ACS */ uint16_t acs_cap; + + /* SR/IOV */ + uint16_t sriov_cap; + PCIESriovPF sriov_pf; + PCIESriovVF sriov_vf; }; #define COMPAT_PROP_PCP "power_controller_present" @@ -112,6 +118,7 @@ void pcie_cap_slot_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); int pcie_cap_slot_post_load(void *opaque, int version_id); void pcie_cap_slot_push_attention_button(PCIDevice *dev); +void pcie_cap_slot_enable_power(PCIDevice *dev); void pcie_cap_root_init(PCIDevice *dev); void pcie_cap_root_reset(PCIDevice *dev); diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h new file mode 100644 index 0000000000..80f5c84e75 --- /dev/null +++ b/include/hw/pci/pcie_sriov.h @@ -0,0 +1,77 @@ +/* + * pcie_sriov.h: + * + * Implementation of SR/IOV emulation support. + * + * Copyright (c) 2015 Knut Omang + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_PCIE_SRIOV_H +#define QEMU_PCIE_SRIOV_H + +struct PCIESriovPF { + uint16_t num_vfs; /* Number of virtual functions created */ + uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ + const char *vfname; /* Reference to the device type used for the VFs */ + PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ +}; + +struct PCIESriovVF { + PCIDevice *pf; /* Pointer back to owner physical function */ + uint16_t vf_number; /* Logical VF number of this function */ +}; + +void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, + const char *vfname, uint16_t vf_dev_id, + uint16_t init_vfs, uint16_t total_vfs, + uint16_t vf_offset, uint16_t vf_stride); +void pcie_sriov_pf_exit(PCIDevice *dev); + +/* Set up a VF bar in the SR/IOV bar area */ +void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, + uint8_t type, dma_addr_t size); + +/* Instantiate a bar for a VF */ +void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, + MemoryRegion *memory); + +/* + * Default (minimal) page size support values + * as required by the SR/IOV standard: + * 0x553 << 12 = 0x553000 = 4K + 8K + 64K + 256K + 1M + 4M + */ +#define SRIOV_SUP_PGSIZE_MINREQ 0x553 + +/* + * Optionally add supported page sizes to the mask of supported page sizes + * Page size values are interpreted as opt_sup_pgsize << 12. + */ +void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize); + +/* SR/IOV capability config write handler */ +void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, + uint32_t val, int len); + +/* Reset SR/IOV VF Enable bit to unregister all VFs */ +void pcie_sriov_pf_disable_vfs(PCIDevice *dev); + +/* Get logical VF number of a VF - only valid for VFs */ +uint16_t pcie_sriov_vf_number(PCIDevice *dev); + +/* + * Get the physical function that owns this VF. + * Returns NULL if dev is not a virtual function + */ +PCIDevice *pcie_sriov_get_pf(PCIDevice *dev); + +/* + * Get the n-th VF of this physical function - only valid for PF. + * Returns NULL if index is invalid + */ +PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n); + +#endif /* QEMU_PCIE_SRIOV_H */ diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 0e9e16544f..86cb7d7f97 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -125,10 +125,22 @@ struct Pnv10Chip { PnvChip parent_obj; /*< public >*/ + PnvXive2 xive; Pnv9Psi psi; PnvLpcController lpc; + PnvOCC occ; + PnvHomer homer; + + uint32_t nr_quads; + PnvQuad *quads; + +#define PNV10_CHIP_MAX_PEC 2 + PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC]; }; +#define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) +#define PNV10_PIR2CHIP(pir) (((pir) >> 8) & 0x7f) + struct PnvChipClass { /*< private >*/ SysBusDeviceClass parent_class; @@ -178,7 +190,6 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10, PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir); void pnv_phb_attach_root_port(PCIHostState *pci, const char *name); -void pnv_chip_parent_fixup(PnvChip *chip, Object *obj, int index); #define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv") typedef struct PnvMachineClass PnvMachineClass; @@ -329,10 +340,37 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV10_LPCM_SIZE 0x0000000100000000ull #define PNV10_LPCM_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030000000000ull) +#define PNV10_XIVE2_IC_SIZE 0x0000000002000000ull +#define PNV10_XIVE2_IC_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030200000000ull) + #define PNV10_PSIHB_ESB_SIZE 0x0000000000100000ull #define PNV10_PSIHB_ESB_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030202000000ull) #define PNV10_PSIHB_SIZE 0x0000000000100000ull #define PNV10_PSIHB_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030203000000ull) +#define PNV10_XIVE2_TM_SIZE 0x0000000000040000ull +#define PNV10_XIVE2_TM_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030203180000ull) + +#define PNV10_XIVE2_NVC_SIZE 0x0000000008000000ull +#define PNV10_XIVE2_NVC_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030208000000ull) + +#define PNV10_XIVE2_NVPG_SIZE 0x0000010000000000ull +#define PNV10_XIVE2_NVPG_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006040000000000ull) + +#define PNV10_XIVE2_ESB_SIZE 0x0000010000000000ull +#define PNV10_XIVE2_ESB_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006050000000000ull) + +#define PNV10_XIVE2_END_SIZE 0x0000020000000000ull +#define PNV10_XIVE2_END_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006060000000000ull) + +#define PNV10_OCC_COMMON_AREA_SIZE 0x0000000000800000ull +#define PNV10_OCC_COMMON_AREA_BASE 0x300fff800000ull +#define PNV10_OCC_SENSOR_BASE(chip) (PNV10_OCC_COMMON_AREA_BASE + \ + PNV_OCC_SENSOR_DATA_BLOCK_BASE((chip)->chip_id)) + +#define PNV10_HOMER_SIZE 0x0000000000400000ull +#define PNV10_HOMER_BASE(chip) \ + (0x300ffd800000ll + ((uint64_t)(chip)->chip_id) * PNV10_HOMER_SIZE) + #endif /* PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_homer.h b/include/hw/ppc/pnv_homer.h index 1889e3083c..07e8b19311 100644 --- a/include/hw/ppc/pnv_homer.h +++ b/include/hw/ppc/pnv_homer.h @@ -32,6 +32,9 @@ DECLARE_INSTANCE_CHECKER(PnvHomer, PNV8_HOMER, #define TYPE_PNV9_HOMER TYPE_PNV_HOMER "-POWER9" DECLARE_INSTANCE_CHECKER(PnvHomer, PNV9_HOMER, TYPE_PNV9_HOMER) +#define TYPE_PNV10_HOMER TYPE_PNV_HOMER "-POWER10" +DECLARE_INSTANCE_CHECKER(PnvHomer, PNV10_HOMER, + TYPE_PNV10_HOMER) struct PnvHomer { DeviceState parent; diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index e893e763dd..8a8d1a3d42 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -1,7 +1,7 @@ /* * QEMU PowerPC PowerNV LPC controller * - * Copyright (c) 2016, IBM Corporation. + * Copyright (c) 2016-2022, IBM Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,7 +20,6 @@ #ifndef PPC_PNV_LPC_H #define PPC_PNV_LPC_H -#include "hw/ppc/pnv_psi.h" #include "qom/object.h" #define TYPE_PNV_LPC "pnv-lpc" @@ -84,15 +83,12 @@ struct PnvLpcController { MemoryRegion xscom_regs; /* PSI to generate interrupts */ - PnvPsi *psi; + qemu_irq psi_irq; }; - struct PnvLpcClass { DeviceClass parent_class; - int psi_irq; - DeviceRealize parent_realize; }; diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h index b78185aeca..90a81dae2b 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -1,7 +1,7 @@ /* * QEMU PowerPC PowerNV Emulation of a few OCC related registers * - * Copyright (c) 2015-2017, IBM Corporation. + * Copyright (c) 2015-2022, IBM Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,7 +20,6 @@ #ifndef PPC_PNV_OCC_H #define PPC_PNV_OCC_H -#include "hw/ppc/pnv_psi.h" #include "qom/object.h" #define TYPE_PNV_OCC "pnv-occ" @@ -32,6 +31,8 @@ DECLARE_INSTANCE_CHECKER(PnvOCC, PNV8_OCC, #define TYPE_PNV9_OCC TYPE_PNV_OCC "-POWER9" DECLARE_INSTANCE_CHECKER(PnvOCC, PNV9_OCC, TYPE_PNV9_OCC) +#define TYPE_PNV10_OCC TYPE_PNV_OCC "-POWER10" +DECLARE_INSTANCE_CHECKER(PnvOCC, PNV10_OCC, TYPE_PNV10_OCC) #define PNV_OCC_SENSOR_DATA_BLOCK_OFFSET 0x00580000 #define PNV_OCC_SENSOR_DATA_BLOCK_SIZE 0x00025800 @@ -42,19 +43,17 @@ struct PnvOCC { /* OCC Misc interrupt */ uint64_t occmisc; - PnvPsi *psi; + qemu_irq psi_irq; MemoryRegion xscom_regs; MemoryRegion sram_regs; }; - struct PnvOCCClass { DeviceClass parent_class; int xscom_size; const MemoryRegionOps *xscom_ops; - int psi_irq; }; #define PNV_OCC_SENSOR_DATA_BLOCK_BASE(i) \ diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h index eb841b34a1..8253469b8f 100644 --- a/include/hw/ppc/pnv_psi.h +++ b/include/hw/ppc/pnv_psi.h @@ -1,7 +1,7 @@ /* * QEMU PowerPC PowerNV Processor Service Interface (PSI) model * - * Copyright (c) 2015-2017, IBM Corporation. + * Copyright (c) 2015-2022, IBM Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -79,13 +79,10 @@ struct PnvPsiClass { uint64_t bar_mask; const char *compat; int compat_size; - - void (*irq_set)(PnvPsi *psi, int, bool state); }; /* The PSI and FSP interrupts are muxed on the same IRQ number */ typedef enum PnvPsiIrq { - PSIHB_IRQ_PSI, /* internal use only */ PSIHB_IRQ_FSP, /* internal use only */ PSIHB_IRQ_OCC, PSIHB_IRQ_FSI, @@ -96,8 +93,6 @@ typedef enum PnvPsiIrq { #define PSI_NUM_INTERRUPTS 6 -void pnv_psi_irq_set(PnvPsi *psi, int irq, bool state); - /* P9 PSI Interrupts */ #define PSIHB9_IRQ_PSI 0 #define PSIHB9_IRQ_OCC 1 diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h index 7928e27963..b5d91505e5 100644 --- a/include/hw/ppc/pnv_xive.h +++ b/include/hw/ppc/pnv_xive.h @@ -12,6 +12,7 @@ #include "hw/ppc/xive.h" #include "qom/object.h" +#include "hw/ppc/xive2.h" struct PnvChip; @@ -95,4 +96,74 @@ struct PnvXiveClass { void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon); +/* + * XIVE2 interrupt controller (POWER10) + */ +#define TYPE_PNV_XIVE2 "pnv-xive2" +OBJECT_DECLARE_TYPE(PnvXive2, PnvXive2Class, PNV_XIVE2); + +typedef struct PnvXive2 { + Xive2Router parent_obj; + + /* Owning chip */ + struct PnvChip *chip; + + /* XSCOM addresses giving access to the controller registers */ + MemoryRegion xscom_regs; + + MemoryRegion ic_mmio; + MemoryRegion ic_mmios[8]; + MemoryRegion esb_mmio; + MemoryRegion end_mmio; + MemoryRegion nvc_mmio; + MemoryRegion nvpg_mmio; + MemoryRegion tm_mmio; + + /* Shortcut values for the Main MMIO regions */ + hwaddr ic_base; + uint32_t ic_shift; + hwaddr esb_base; + uint32_t esb_shift; + hwaddr end_base; + uint32_t end_shift; + hwaddr nvc_base; + uint32_t nvc_shift; + hwaddr nvpg_base; + uint32_t nvpg_shift; + hwaddr tm_base; + uint32_t tm_shift; + + /* Interrupt controller registers */ + uint64_t cq_regs[0x40]; + uint64_t vc_regs[0x100]; + uint64_t pc_regs[0x100]; + uint64_t tctxt_regs[0x30]; + + /* To change default behavior */ + uint64_t capabilities; + uint64_t config; + + /* Our XIVE source objects for IPIs and ENDs */ + XiveSource ipi_source; + Xive2EndSource end_source; + + /* + * Virtual Structure Descriptor tables + * These are in a SRAM protected by ECC. + */ + uint64_t vsds[9][XIVE_BLOCK_MAX]; + + /* Translation tables */ + uint64_t tables[8][XIVE_BLOCK_MAX]; + +} PnvXive2; + +typedef struct PnvXive2Class { + Xive2RouterClass parent_class; + + DeviceRealize parent_realize; +} PnvXive2Class; + +void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon); + #endif /* PPC_PNV_XIVE_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 2ff9f7a8d6..7c7440de0c 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -131,6 +131,21 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_PSIHB_BASE 0x3011D00 #define PNV10_XSCOM_PSIHB_SIZE 0x100 +#define PNV10_XSCOM_OCC_BASE PNV9_XSCOM_OCC_BASE +#define PNV10_XSCOM_OCC_SIZE PNV9_XSCOM_OCC_SIZE + +#define PNV10_XSCOM_PBA_BASE 0x01010CDA +#define PNV10_XSCOM_PBA_SIZE 0x40 + +#define PNV10_XSCOM_XIVE2_BASE 0x2010800 +#define PNV10_XSCOM_XIVE2_SIZE 0x400 + +#define PNV10_XSCOM_PEC_NEST_BASE 0x3011800 /* index goes downwards ... */ +#define PNV10_XSCOM_PEC_NEST_SIZE 0x100 + +#define PNV10_XSCOM_PEC_PCI_BASE 0x8010800 /* index goes upwards ... */ +#define PNV10_XSCOM_PEC_PCI_SIZE 0x200 + void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp); int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, uint64_t xscom_base, uint64_t xscom_size, diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index b0ba4bd6b9..02af03ada2 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -54,6 +54,7 @@ struct ppc_tb_t { uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq); +void cpu_ppc_tb_free(CPUPPCState *env); void cpu_ppc_hdecr_init(CPUPPCState *env); void cpu_ppc_hdecr_exit(CPUPPCState *env); @@ -98,11 +99,11 @@ enum { ARCH_MAC99_U3, }; -#define FW_CFG_PPC_WIDTH (FW_CFG_ARCH_LOCAL + 0x00) -#define FW_CFG_PPC_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01) -#define FW_CFG_PPC_DEPTH (FW_CFG_ARCH_LOCAL + 0x02) -#define FW_CFG_PPC_TBFREQ (FW_CFG_ARCH_LOCAL + 0x03) -#define FW_CFG_PPC_CLOCKFREQ (FW_CFG_ARCH_LOCAL + 0x04) +#define FW_CFG_PPC_WIDTH (FW_CFG_ARCH_LOCAL + 0x00) +#define FW_CFG_PPC_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01) +#define FW_CFG_PPC_DEPTH (FW_CFG_ARCH_LOCAL + 0x02) +#define FW_CFG_PPC_TBFREQ (FW_CFG_ARCH_LOCAL + 0x03) +#define FW_CFG_PPC_CLOCKFREQ (FW_CFG_ARCH_LOCAL + 0x04) #define FW_CFG_PPC_IS_KVM (FW_CFG_ARCH_LOCAL + 0x05) #define FW_CFG_PPC_KVM_HC (FW_CFG_ARCH_LOCAL + 0x06) #define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index f5c33dcc86..14b01c3f59 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -745,6 +745,7 @@ void push_sregs_to_kvm_pr(SpaprMachineState *spapr); #define RTAS_DDW_PGSIZE_128M 0x20 #define RTAS_DDW_PGSIZE_256M 0x40 #define RTAS_DDW_PGSIZE_16G 0x80 +#define RTAS_DDW_PGSIZE_2M 0x100 /* RTAS tokens */ #define RTAS_TOKEN_BASE 0x2000 diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index b8ab0bf749..126e4e2c3a 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -160,7 +160,7 @@ DECLARE_CLASS_CHECKERS(XiveNotifierClass, XIVE_NOTIFIER, struct XiveNotifierClass { InterfaceClass parent; - void (*notify)(XiveNotifier *xn, uint32_t lisn); + void (*notify)(XiveNotifier *xn, uint32_t lisn, bool pq_checked); }; /* @@ -176,6 +176,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(XiveSource, XIVE_SOURCE) */ #define XIVE_SRC_H_INT_ESB 0x1 /* ESB managed with hcall H_INT_ESB */ #define XIVE_SRC_STORE_EOI 0x2 /* Store EOI supported */ +#define XIVE_SRC_PQ_DISABLE 0x4 /* Disable check on the PQ state bits */ struct XiveSource { DeviceState parent; @@ -278,6 +279,7 @@ uint8_t xive_esb_set(uint8_t *pq, uint8_t value); #define XIVE_ESB_STORE_EOI 0x400 /* Store */ #define XIVE_ESB_LOAD_EOI 0x000 /* Load */ #define XIVE_ESB_GET 0x800 /* Load */ +#define XIVE_ESB_INJECT 0x800 /* Store */ #define XIVE_ESB_SET_PQ_00 0xc00 /* Load */ #define XIVE_ESB_SET_PQ_01 0xd00 /* Load */ #define XIVE_ESB_SET_PQ_10 0xe00 /* Load */ @@ -385,6 +387,10 @@ struct XiveRouterClass { /* XIVE table accessors */ int (*get_eas)(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, XiveEAS *eas); + int (*get_pq)(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq); + int (*set_pq)(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq); int (*get_end)(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, XiveEND *end); int (*write_end)(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, @@ -406,7 +412,7 @@ int xive_router_get_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt); int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt, uint8_t word_number); -void xive_router_notify(XiveNotifier *xn, uint32_t lisn); +void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); /* * XIVE Presenter diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h new file mode 100644 index 0000000000..e9e3ea135e --- /dev/null +++ b/include/hw/ppc/xive2.h @@ -0,0 +1,109 @@ +/* + * QEMU PowerPC XIVE2 interrupt controller model (POWER10) + * + * Copyright (c) 2019-2022, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + * + */ + +#ifndef PPC_XIVE2_H +#define PPC_XIVE2_H + +#include "hw/ppc/xive2_regs.h" + +/* + * XIVE2 Router (POWER10) + */ +typedef struct Xive2Router { + SysBusDevice parent; + + XiveFabric *xfb; +} Xive2Router; + +#define TYPE_XIVE2_ROUTER "xive2-router" +OBJECT_DECLARE_TYPE(Xive2Router, Xive2RouterClass, XIVE2_ROUTER); + +/* + * Configuration flags + */ + +#define XIVE2_GEN1_TIMA_OS 0x00000001 +#define XIVE2_VP_SAVE_RESTORE 0x00000002 +#define XIVE2_THREADID_8BITS 0x00000004 + +typedef struct Xive2RouterClass { + SysBusDeviceClass parent; + + /* XIVE table accessors */ + int (*get_eas)(Xive2Router *xrtr, uint8_t eas_blk, uint32_t eas_idx, + Xive2Eas *eas); + int (*get_pq)(Xive2Router *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq); + int (*set_pq)(Xive2Router *xrtr, uint8_t eas_blk, uint32_t eas_idx, + uint8_t *pq); + int (*get_end)(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, + Xive2End *end); + int (*write_end)(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, + Xive2End *end, uint8_t word_number); + int (*get_nvp)(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp); + int (*write_nvp)(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp, uint8_t word_number); + uint8_t (*get_block_id)(Xive2Router *xrtr); + uint32_t (*get_config)(Xive2Router *xrtr); +} Xive2RouterClass; + +int xive2_router_get_eas(Xive2Router *xrtr, uint8_t eas_blk, uint32_t eas_idx, + Xive2Eas *eas); +int xive2_router_get_end(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, + Xive2End *end); +int xive2_router_write_end(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, + Xive2End *end, uint8_t word_number); +int xive2_router_get_nvp(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp); +int xive2_router_write_nvp(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp, uint8_t word_number); +uint32_t xive2_router_get_config(Xive2Router *xrtr); + +void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); + +/* + * XIVE2 Presenter (POWER10) + */ + +int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, + uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint32_t logic_serv); + +/* + * XIVE2 END ESBs (POWER10) + */ + +#define TYPE_XIVE2_END_SOURCE "xive2-end-source" +OBJECT_DECLARE_SIMPLE_TYPE(Xive2EndSource, XIVE2_END_SOURCE) + +typedef struct Xive2EndSource { + DeviceState parent; + + uint32_t nr_ends; + + /* ESB memory region */ + uint32_t esb_shift; + MemoryRegion esb_mmio; + + Xive2Router *xrtr; +} Xive2EndSource; + +/* + * XIVE2 Thread Interrupt Management Area (POWER10) + */ + +void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size); +uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size); + +#endif /* PPC_XIVE2_H */ diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h new file mode 100644 index 0000000000..14605bd458 --- /dev/null +++ b/include/hw/ppc/xive2_regs.h @@ -0,0 +1,210 @@ +/* + * QEMU PowerPC XIVE2 internal structure definitions (POWER10) + * + * Copyright (c) 2019-2022, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PPC_XIVE2_REGS_H +#define PPC_XIVE2_REGS_H + +/* + * Thread Interrupt Management Area (TIMA) + * + * In Gen1 mode (P9 compat mode) word 2 is the same. However in Gen2 + * mode (P10), the CAM line is slightly different as the VP space was + * increased. + */ +#define TM2_QW0W2_VU PPC_BIT32(0) +#define TM2_QW0W2_LOGIC_SERV PPC_BITMASK32(4, 31) +#define TM2_QW1W2_VO PPC_BIT32(0) +#define TM2_QW1W2_HO PPC_BIT32(1) +#define TM2_QW1W2_OS_CAM PPC_BITMASK32(4, 31) +#define TM2_QW2W2_VP PPC_BIT32(0) +#define TM2_QW2W2_HP PPC_BIT32(1) +#define TM2_QW2W2_POOL_CAM PPC_BITMASK32(4, 31) +#define TM2_QW3W2_VT PPC_BIT32(0) +#define TM2_QW3W2_HT PPC_BIT32(1) +#define TM2_QW3W2_LP PPC_BIT32(6) +#define TM2_QW3W2_LE PPC_BIT32(7) + +/* + * Event Assignment Structure (EAS) + */ + +typedef struct Xive2Eas { + uint64_t w; +#define EAS2_VALID PPC_BIT(0) +#define EAS2_END_BLOCK PPC_BITMASK(4, 7) /* Destination EQ block# */ +#define EAS2_END_INDEX PPC_BITMASK(8, 31) /* Destination EQ index */ +#define EAS2_MASKED PPC_BIT(32) /* Masked */ +#define EAS2_END_DATA PPC_BITMASK(33, 63) /* written to the EQ */ +} Xive2Eas; + +#define xive2_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS2_VALID) +#define xive2_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS2_MASKED) + +void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, Monitor *mon); + +/* + * Event Notifification Descriptor (END) + */ + +typedef struct Xive2End { + uint32_t w0; +#define END2_W0_VALID PPC_BIT32(0) /* "v" bit */ +#define END2_W0_ENQUEUE PPC_BIT32(5) /* "q" bit */ +#define END2_W0_UCOND_NOTIFY PPC_BIT32(6) /* "n" bit */ +#define END2_W0_SILENT_ESCALATE PPC_BIT32(7) /* "s" bit */ +#define END2_W0_BACKLOG PPC_BIT32(8) /* "b" bit */ +#define END2_W0_PRECL_ESC_CTL PPC_BIT32(9) /* "p" bit */ +#define END2_W0_UNCOND_ESCALATE PPC_BIT32(10) /* "u" bit */ +#define END2_W0_ESCALATE_CTL PPC_BIT32(11) /* "e" bit */ +#define END2_W0_ADAPTIVE_ESC PPC_BIT32(12) /* "a" bit */ +#define END2_W0_ESCALATE_END PPC_BIT32(13) /* "N" bit */ +#define END2_W0_FIRMWARE1 PPC_BIT32(16) /* Owned by FW */ +#define END2_W0_FIRMWARE2 PPC_BIT32(17) /* Owned by FW */ +#define END2_W0_AEC_SIZE PPC_BITMASK32(18, 19) +#define END2_W0_AEG_SIZE PPC_BITMASK32(20, 23) +#define END2_W0_EQ_VG_PREDICT PPC_BITMASK32(24, 31) /* Owned by HW */ + uint32_t w1; +#define END2_W1_ESn PPC_BITMASK32(0, 1) +#define END2_W1_ESn_P PPC_BIT32(0) +#define END2_W1_ESn_Q PPC_BIT32(1) +#define END2_W1_ESe PPC_BITMASK32(2, 3) +#define END2_W1_ESe_P PPC_BIT32(2) +#define END2_W1_ESe_Q PPC_BIT32(3) +#define END2_W1_GEN_FLIPPED PPC_BIT32(8) +#define END2_W1_GENERATION PPC_BIT32(9) +#define END2_W1_PAGE_OFF PPC_BITMASK32(10, 31) + uint32_t w2; +#define END2_W2_RESERVED PPC_BITMASK32(4, 7) +#define END2_W2_EQ_ADDR_HI PPC_BITMASK32(8, 31) + uint32_t w3; +#define END2_W3_EQ_ADDR_LO PPC_BITMASK32(0, 24) +#define END2_W3_QSIZE PPC_BITMASK32(28, 31) + uint32_t w4; +#define END2_W4_END_BLOCK PPC_BITMASK32(4, 7) +#define END2_W4_ESC_END_INDEX PPC_BITMASK32(8, 31) +#define END2_W4_ESB_BLOCK PPC_BITMASK32(0, 3) +#define END2_W4_ESC_ESB_INDEX PPC_BITMASK32(4, 31) + uint32_t w5; +#define END2_W5_ESC_END_DATA PPC_BITMASK32(1, 31) + uint32_t w6; +#define END2_W6_FORMAT_BIT PPC_BIT32(0) +#define END2_W6_IGNORE PPC_BIT32(1) +#define END2_W6_VP_BLOCK PPC_BITMASK32(4, 7) +#define END2_W6_VP_OFFSET PPC_BITMASK32(8, 31) +#define END2_W6_VP_OFFSET_GEN1 PPC_BITMASK32(13, 31) + uint32_t w7; +#define END2_W7_TOPO PPC_BITMASK32(0, 3) /* Owned by HW */ +#define END2_W7_F0_PRIORITY PPC_BITMASK32(8, 15) +#define END2_W7_F1_LOG_SERVER_ID PPC_BITMASK32(4, 31) +} Xive2End; + +#define xive2_end_is_valid(end) (be32_to_cpu((end)->w0) & END2_W0_VALID) +#define xive2_end_is_enqueue(end) (be32_to_cpu((end)->w0) & END2_W0_ENQUEUE) +#define xive2_end_is_notify(end) \ + (be32_to_cpu((end)->w0) & END2_W0_UCOND_NOTIFY) +#define xive2_end_is_backlog(end) (be32_to_cpu((end)->w0) & END2_W0_BACKLOG) +#define xive2_end_is_escalate(end) \ + (be32_to_cpu((end)->w0) & END2_W0_ESCALATE_CTL) +#define xive2_end_is_uncond_escalation(end) \ + (be32_to_cpu((end)->w0) & END2_W0_UNCOND_ESCALATE) +#define xive2_end_is_silent_escalation(end) \ + (be32_to_cpu((end)->w0) & END2_W0_SILENT_ESCALATE) +#define xive2_end_is_escalate_end(end) \ + (be32_to_cpu((end)->w0) & END2_W0_ESCALATE_END) +#define xive2_end_is_firmware1(end) \ + (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE1) +#define xive2_end_is_firmware2(end) \ + (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE2) + +static inline uint64_t xive2_end_qaddr(Xive2End *end) +{ + return ((uint64_t) be32_to_cpu(end->w2) & END2_W2_EQ_ADDR_HI) << 32 | + (be32_to_cpu(end->w3) & END2_W3_EQ_ADDR_LO); +} + +void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon); +void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, + Monitor *mon); +void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, + Monitor *mon); + +/* + * Notification Virtual Processor (NVP) + */ +typedef struct Xive2Nvp { + uint32_t w0; +#define NVP2_W0_VALID PPC_BIT32(0) +#define NVP2_W0_HW PPC_BIT32(7) +#define NVP2_W0_ESC_END PPC_BIT32(25) /* 'N' bit 0:ESB 1:END */ + uint32_t w1; +#define NVP2_W1_CO PPC_BIT32(13) +#define NVP2_W1_CO_PRIV PPC_BITMASK32(14, 15) +#define NVP2_W1_CO_THRID_VALID PPC_BIT32(16) +#define NVP2_W1_CO_THRID PPC_BITMASK32(17, 31) + uint32_t w2; +#define NVP2_W2_CPPR PPC_BITMASK32(0, 7) +#define NVP2_W2_IPB PPC_BITMASK32(8, 15) +#define NVP2_W2_LSMFB PPC_BITMASK32(16, 23) + uint32_t w3; + uint32_t w4; +#define NVP2_W4_ESC_ESB_BLOCK PPC_BITMASK32(0, 3) /* N:0 */ +#define NVP2_W4_ESC_ESB_INDEX PPC_BITMASK32(4, 31) /* N:0 */ +#define NVP2_W4_ESC_END_BLOCK PPC_BITMASK32(4, 7) /* N:1 */ +#define NVP2_W4_ESC_END_INDEX PPC_BITMASK32(8, 31) /* N:1 */ + uint32_t w5; +#define NVP2_W5_PSIZE PPC_BITMASK32(0, 1) +#define NVP2_W5_VP_END_BLOCK PPC_BITMASK32(4, 7) +#define NVP2_W5_VP_END_INDEX PPC_BITMASK32(8, 31) + uint32_t w6; + uint32_t w7; +} Xive2Nvp; + +#define xive2_nvp_is_valid(nvp) (be32_to_cpu((nvp)->w0) & NVP2_W0_VALID) +#define xive2_nvp_is_hw(nvp) (be32_to_cpu((nvp)->w0) & NVP2_W0_HW) +#define xive2_nvp_is_co(nvp) (be32_to_cpu((nvp)->w1) & NVP2_W1_CO) + +/* + * The VP number space in a block is defined by the END2_W6_VP_OFFSET + * field of the XIVE END. When running in Gen1 mode (P9 compat mode), + * the VP space is reduced to (1 << 19) VPs per block + */ +#define XIVE2_NVP_SHIFT 24 +#define XIVE2_NVP_COUNT (1 << XIVE2_NVP_SHIFT) + +static inline uint32_t xive2_nvp_cam_line(uint8_t nvp_blk, uint32_t nvp_idx) +{ + return (nvp_blk << XIVE2_NVP_SHIFT) | nvp_idx; +} + +static inline uint32_t xive2_nvp_idx(uint32_t cam_line) +{ + return cam_line & ((1 << XIVE2_NVP_SHIFT) - 1); +} + +static inline uint32_t xive2_nvp_blk(uint32_t cam_line) +{ + return (cam_line >> XIVE2_NVP_SHIFT) & 0xf; +} + +/* + * Notification Virtual Group or Crowd (NVG/NVC) + */ +typedef struct Xive2Nvgc { + uint32_t w0; +#define NVGC2_W0_VALID PPC_BIT32(0) + uint32_t w1; + uint32_t w2; + uint32_t w3; + uint32_t w4; + uint32_t w5; + uint32_t w6; + uint32_t w7; +} Xive2Nvgc; + +#endif /* PPC_XIVE2_REGS_H */ diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h index f2a3c9c41f..3a88e135d0 100644 --- a/include/hw/registerfields.h +++ b/include/hw/registerfields.h @@ -59,6 +59,19 @@ extract64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX8(storage, reg, field) \ + sextract8((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX16(storage, reg, field) \ + sextract16((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX32(storage, reg, field) \ + sextract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX64(storage, reg, field) \ + sextract64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) + /* Extract a field from an array of registers */ #define ARRAY_FIELD_EX32(regs, reg, field) \ FIELD_EX32((regs)[R_ ## reg], reg, field) @@ -95,7 +108,40 @@ _d; }) #define FIELD_DP64(storage, reg, field, val) ({ \ struct { \ - uint64_t v:R_ ## reg ## _ ## field ## _LENGTH; \ + uint64_t v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint64_t _d; \ + _d = deposit64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) + +#define FIELD_SDP8(storage, reg, field, val) ({ \ + struct { \ + signed int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint8_t _d; \ + _d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) +#define FIELD_SDP16(storage, reg, field, val) ({ \ + struct { \ + signed int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint16_t _d; \ + _d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) +#define FIELD_SDP32(storage, reg, field, val) ({ \ + struct { \ + signed int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint32_t _d; \ + _d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) +#define FIELD_SDP64(storage, reg, field, val) ({ \ + struct { \ + int64_t v:R_ ## reg ## _ ## field ## _LENGTH; \ } _v = { .v = val }; \ uint64_t _d; \ _d = deposit64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index d937c5c224..d2db29721a 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -46,12 +46,12 @@ target_ulong riscv_load_kernel(const char *kernel_filename, symbol_fn_t sym_cb); hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size, uint64_t kernel_entry, hwaddr *start); -uint32_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt); +uint64_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt); void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, hwaddr saddr, hwaddr rom_base, hwaddr rom_size, uint64_t kernel_entry, - uint32_t fdt_load_addr, void *fdt); + uint64_t fdt_load_addr, void *fdt); void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, hwaddr rom_size, uint32_t reset_vec_size, diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index eac35ef590..68892cd8e5 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -23,11 +23,18 @@ #include "hw/intc/sifive_plic.h" #include "hw/char/ibex_uart.h" #include "hw/timer/ibex_timer.h" +#include "hw/ssi/ibex_spi_host.h" #include "qom/object.h" #define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc" OBJECT_DECLARE_SIMPLE_TYPE(LowRISCIbexSoCState, RISCV_IBEX_SOC) +enum { + OPENTITAN_SPI_HOST0, + OPENTITAN_SPI_HOST1, + OPENTITAN_NUM_SPI_HOSTS, +}; + struct LowRISCIbexSoCState { /*< private >*/ SysBusDevice parent_obj; @@ -37,6 +44,7 @@ struct LowRISCIbexSoCState { SiFivePLICState plic; IbexUartState uart; IbexTimerState timer; + IbexSPIHostState spi_host[OPENTITAN_NUM_SPI_HOSTS]; MemoryRegion flash_mem; MemoryRegion rom; @@ -57,8 +65,10 @@ enum { IBEX_DEV_FLASH, IBEX_DEV_FLASH_VIRTUAL, IBEX_DEV_UART, + IBEX_DEV_SPI_DEVICE, + IBEX_DEV_SPI_HOST0, + IBEX_DEV_SPI_HOST1, IBEX_DEV_GPIO, - IBEX_DEV_SPI, IBEX_DEV_I2C, IBEX_DEV_PATTGEN, IBEX_DEV_TIMER, @@ -87,15 +97,19 @@ enum { }; enum { - IBEX_TIMER_TIMEREXPIRED0_0 = 126, - IBEX_UART0_RX_PARITY_ERR_IRQ = 8, - IBEX_UART0_RX_TIMEOUT_IRQ = 7, - IBEX_UART0_RX_BREAK_ERR_IRQ = 6, - IBEX_UART0_RX_FRAME_ERR_IRQ = 5, - IBEX_UART0_RX_OVERFLOW_IRQ = 4, - IBEX_UART0_TX_EMPTY_IRQ = 3, - IBEX_UART0_RX_WATERMARK_IRQ = 2, - IBEX_UART0_TX_WATERMARK_IRQ = 1, + IBEX_UART0_TX_WATERMARK_IRQ = 1, + IBEX_UART0_RX_WATERMARK_IRQ = 2, + IBEX_UART0_TX_EMPTY_IRQ = 3, + IBEX_UART0_RX_OVERFLOW_IRQ = 4, + IBEX_UART0_RX_FRAME_ERR_IRQ = 5, + IBEX_UART0_RX_BREAK_ERR_IRQ = 6, + IBEX_UART0_RX_TIMEOUT_IRQ = 7, + IBEX_UART0_RX_PARITY_ERR_IRQ = 8, + IBEX_TIMER_TIMEREXPIRED0_0 = 126, + IBEX_SPI_HOST0_ERR_IRQ = 150, + IBEX_SPI_HOST0_SPI_EVENT_IRQ = 151, + IBEX_SPI_HOST1_ERR_IRQ = 152, + IBEX_SPI_HOST1_SPI_EVENT_IRQ = 153, }; #endif diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 6e9f61ccd9..984e55c77f 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -24,26 +24,38 @@ #include "hw/block/flash.h" #include "qom/object.h" -#define VIRT_CPUS_MAX 32 -#define VIRT_SOCKETS_MAX 8 +#define VIRT_CPUS_MAX_BITS 9 +#define VIRT_CPUS_MAX (1 << VIRT_CPUS_MAX_BITS) +#define VIRT_SOCKETS_MAX_BITS 2 +#define VIRT_SOCKETS_MAX (1 << VIRT_SOCKETS_MAX_BITS) #define TYPE_RISCV_VIRT_MACHINE MACHINE_TYPE_NAME("virt") typedef struct RISCVVirtState RISCVVirtState; DECLARE_INSTANCE_CHECKER(RISCVVirtState, RISCV_VIRT_MACHINE, TYPE_RISCV_VIRT_MACHINE) +typedef enum RISCVVirtAIAType { + VIRT_AIA_TYPE_NONE = 0, + VIRT_AIA_TYPE_APLIC, + VIRT_AIA_TYPE_APLIC_IMSIC, +} RISCVVirtAIAType; + struct RISCVVirtState { /*< private >*/ MachineState parent; /*< public >*/ + Notifier machine_done; + DeviceState *platform_bus_dev; RISCVHartArrayState soc[VIRT_SOCKETS_MAX]; - DeviceState *plic[VIRT_SOCKETS_MAX]; + DeviceState *irqchip[VIRT_SOCKETS_MAX]; PFlashCFI01 *flash[2]; FWCfgState *fw_cfg; int fdt_size; bool have_aclint; + RISCVVirtAIAType aia_type; + int aia_guests; }; enum { @@ -54,13 +66,18 @@ enum { VIRT_CLINT, VIRT_ACLINT_SSWI, VIRT_PLIC, + VIRT_APLIC_M, + VIRT_APLIC_S, VIRT_UART0, VIRT_VIRTIO, VIRT_FW_CFG, + VIRT_IMSIC_M, + VIRT_IMSIC_S, VIRT_FLASH, VIRT_DRAM, VIRT_PCIE_MMIO, VIRT_PCIE_PIO, + VIRT_PLATFORM_BUS, VIRT_PCIE_ECAM }; @@ -70,11 +87,19 @@ enum { VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ - VIRTIO_NDEV = 0x35 /* Arbitrary maximum number of interrupts */ + VIRT_PLATFORM_BUS_IRQ = 64, /* 64 to 96 */ + VIRTIO_NDEV = 96 /* Arbitrary maximum number of interrupts */ }; -#define VIRT_PLIC_NUM_SOURCES 127 -#define VIRT_PLIC_NUM_PRIORITIES 7 +#define VIRT_PLATFORM_BUS_NUM_IRQS 32 + +#define VIRT_IRQCHIP_IPI_MSI 1 +#define VIRT_IRQCHIP_NUM_MSIS 255 +#define VIRT_IRQCHIP_NUM_SOURCES VIRTIO_NDEV +#define VIRT_IRQCHIP_NUM_PRIO_BITS 3 +#define VIRT_IRQCHIP_MAX_GUESTS_BITS 3 +#define VIRT_IRQCHIP_MAX_GUESTS ((1U << VIRT_IRQCHIP_MAX_GUESTS_BITS) - 1U) + #define VIRT_PLIC_PRIORITY_BASE 0x04 #define VIRT_PLIC_PENDING_BASE 0x1000 #define VIRT_PLIC_ENABLE_BASE 0x2000 @@ -86,9 +111,15 @@ enum { #define FDT_PCI_ADDR_CELLS 3 #define FDT_PCI_INT_CELLS 1 -#define FDT_PLIC_ADDR_CELLS 0 #define FDT_PLIC_INT_CELLS 1 -#define FDT_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + 1 + \ - FDT_PLIC_ADDR_CELLS + FDT_PLIC_INT_CELLS) +#define FDT_APLIC_INT_CELLS 2 +#define FDT_IMSIC_INT_CELLS 0 +#define FDT_MAX_INT_CELLS 2 +#define FDT_MAX_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ + 1 + FDT_MAX_INT_CELLS) +#define FDT_PLIC_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ + 1 + FDT_PLIC_INT_CELLS) +#define FDT_APLIC_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ + 1 + FDT_APLIC_INT_CELLS) #endif diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 5b45b22924..deef93f89a 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -25,6 +25,7 @@ struct RTCState { MemoryRegion coalesced_io; uint8_t cmos_data[128]; uint8_t cmos_index; + uint8_t isairq; int32_t base_year; uint64_t base_rtc; uint64_t last_update; diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index b1ec27612f..13b17496f8 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -51,7 +51,7 @@ struct ESPState { ESPDMAMemoryReadWriteFunc dma_memory_write; void *dma_opaque; void (*dma_cb)(ESPState *s); - void (*pdma_cb)(ESPState *s); + uint8_t pdma_cb; uint8_t mig_version_id; @@ -150,6 +150,15 @@ struct SysBusESPState { #define TCHI_FAS100A 0x4 #define TCHI_AM53C974 0x12 +/* PDMA callbacks */ +enum pdma_cb { + SATN_PDMA_CB = 0, + S_WITHOUT_SATN_PDMA_CB = 1, + SATN_STOP_PDMA_CB = 2, + WRITE_RESPONSE_PDMA_CB = 3, + DO_DMA_PDMA_CB = 4 +}; + void esp_dma_enable(ESPState *s, int irq, int level); void esp_request_cancelled(SCSIRequest *req); void esp_command_complete(SCSIRequest *req, size_t resid); diff --git a/include/hw/sensor/isl_pmbus_vr.h b/include/hw/sensor/isl_pmbus_vr.h new file mode 100644 index 0000000000..3e47ff7e48 --- /dev/null +++ b/include/hw/sensor/isl_pmbus_vr.h @@ -0,0 +1,52 @@ +/* + * PMBus device for Renesas Digital Multiphase Voltage Regulators + * + * Copyright 2022 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_ISL_PMBUS_VR_H +#define HW_MISC_ISL_PMBUS_VR_H + +#include "hw/i2c/pmbus_device.h" +#include "qom/object.h" + +#define TYPE_ISL69260 "isl69260" +#define TYPE_RAA228000 "raa228000" +#define TYPE_RAA229004 "raa229004" + +struct ISLState { + PMBusDevice parent; +}; + +OBJECT_DECLARE_SIMPLE_TYPE(ISLState, ISL69260) + +#define ISL_CAPABILITY_DEFAULT 0x40 +#define ISL_OPERATION_DEFAULT 0x80 +#define ISL_ON_OFF_CONFIG_DEFAULT 0x16 +#define ISL_VOUT_MODE_DEFAULT 0x40 +#define ISL_VOUT_COMMAND_DEFAULT 0x0384 +#define ISL_VOUT_MAX_DEFAULT 0x08FC +#define ISL_VOUT_MARGIN_HIGH_DEFAULT 0x0640 +#define ISL_VOUT_MARGIN_LOW_DEFAULT 0xFA +#define ISL_VOUT_TRANSITION_RATE_DEFAULT 0x64 +#define ISL_VOUT_OV_FAULT_LIMIT_DEFAULT 0x076C +#define ISL_OT_FAULT_LIMIT_DEFAULT 0x7D +#define ISL_OT_WARN_LIMIT_DEFAULT 0x07D0 +#define ISL_VIN_OV_WARN_LIMIT_DEFAULT 0x36B0 +#define ISL_VIN_UV_WARN_LIMIT_DEFAULT 0x1F40 +#define ISL_IIN_OC_FAULT_LIMIT_DEFAULT 0x32 +#define ISL_TON_DELAY_DEFAULT 0x14 +#define ISL_TON_RISE_DEFAULT 0x01F4 +#define ISL_TOFF_FALL_DEFAULT 0x01F4 +#define ISL_REVISION_DEFAULT 0x33 +#define ISL_READ_VOUT_DEFAULT 1000 +#define ISL_READ_IOUT_DEFAULT 40 +#define ISL_READ_POUT_DEFAULT 4 +#define ISL_READ_TEMP_DEFAULT 25 +#define ISL_READ_VIN_DEFAULT 1100 +#define ISL_READ_IIN_DEFAULT 40 +#define ISL_READ_PIN_DEFAULT 4 + +#endif diff --git a/include/hw/southbridge/piix.h b/include/hw/southbridge/piix.h index 6387f2b612..f63f83e5c6 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -70,8 +70,6 @@ typedef struct PIIXState PIIX3State; DECLARE_INSTANCE_CHECKER(PIIX3State, PIIX3_PCI_DEVICE, TYPE_PIIX3_PCI_DEVICE) -extern PCIDevice *piix4_dev; - PIIX3State *piix3_create(PCIBus *pci_bus, ISABus **isa_bus); DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus); diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index e265555819..2d5f8f3d8f 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -57,7 +57,6 @@ struct AspeedSMCState { qemu_irq irq; - uint32_t num_cs; qemu_irq *cs_lines; bool inject_failure; @@ -96,9 +95,10 @@ struct AspeedSMCClass { uint8_t r_timings; uint8_t nregs_timings; uint8_t conf_enable_w0; - uint8_t max_peripherals; + uint8_t cs_num_max; const uint32_t *resets; const AspeedSegments *segments; + uint32_t segment_addr_mask; hwaddr flash_window_base; uint32_t flash_window_size; uint32_t features; diff --git a/include/hw/ssi/ibex_spi_host.h b/include/hw/ssi/ibex_spi_host.h new file mode 100644 index 0000000000..3fedcb6805 --- /dev/null +++ b/include/hw/ssi/ibex_spi_host.h @@ -0,0 +1,94 @@ + +/* + * QEMU model of the Ibex SPI Controller + * SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/ + * + * Copyright (C) 2022 Western Digital + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef IBEX_SPI_HOST_H +#define IBEX_SPI_HOST_H + +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/ssi/ssi.h" +#include "qemu/fifo8.h" +#include "qom/object.h" +#include "hw/registerfields.h" +#include "qemu/timer.h" + +#define TYPE_IBEX_SPI_HOST "ibex-spi" +#define IBEX_SPI_HOST(obj) \ + OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST) + +/* SPI Registers */ +#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw */ +#define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */ +#define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */ +#define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */ +#define IBEX_SPI_HOST_CONTROL (0x10 / 4) /* rw */ +#define IBEX_SPI_HOST_STATUS (0x14 / 4) /* ro */ +#define IBEX_SPI_HOST_CONFIGOPTS (0x18 / 4) /* rw */ +#define IBEX_SPI_HOST_CSID (0x1c / 4) /* rw */ +#define IBEX_SPI_HOST_COMMAND (0x20 / 4) /* wo */ +/* RX/TX Modelled by FIFO */ +#define IBEX_SPI_HOST_RXDATA (0x24 / 4) +#define IBEX_SPI_HOST_TXDATA (0x28 / 4) + +#define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */ +#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw */ +#define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */ + +/* FIFO Len in Bytes */ +#define IBEX_SPI_HOST_TXFIFO_LEN 288 +#define IBEX_SPI_HOST_RXFIFO_LEN 256 + +/* Max Register (Based on addr) */ +#define IBEX_SPI_HOST_MAX_REGS (IBEX_SPI_HOST_EVENT_ENABLE + 1) + +/* MISC */ +#define TX_INTERRUPT_TRIGGER_DELAY_NS 100 +#define BIDIRECTIONAL_TRANSFER 3 + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + uint32_t regs[IBEX_SPI_HOST_MAX_REGS]; + /* Multi-reg that sets config opts per CS */ + uint32_t *config_opts; + Fifo8 rx_fifo; + Fifo8 tx_fifo; + QEMUTimer *fifo_trigger_handle; + + qemu_irq event; + qemu_irq host_err; + uint32_t num_cs; + qemu_irq *cs_lines; + SSIBus *ssi; + + /* Used to track the init status, for replicating TXDATA ghost writes */ + bool init_status; +} IbexSPIHostState; + +#endif diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index d36034a10c..07dc6b6f2c 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -31,6 +31,7 @@ OBJECT_DECLARE_TYPE(AspeedTimerCtrlState, AspeedTimerClass, ASPEED_TIMER) #define TYPE_ASPEED_2400_TIMER TYPE_ASPEED_TIMER "-ast2400" #define TYPE_ASPEED_2500_TIMER TYPE_ASPEED_TIMER "-ast2500" #define TYPE_ASPEED_2600_TIMER TYPE_ASPEED_TIMER "-ast2600" +#define TYPE_ASPEED_1030_TIMER TYPE_ASPEED_TIMER "-ast1030" #define ASPEED_TIMER_NR_TIMERS 8 diff --git a/include/hw/timer/cadence_ttc.h b/include/hw/timer/cadence_ttc.h new file mode 100644 index 0000000000..e1251383f2 --- /dev/null +++ b/include/hw/timer/cadence_ttc.h @@ -0,0 +1,54 @@ +/* + * Xilinx Zynq cadence TTC model + * + * Copyright (c) 2011 Xilinx Inc. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) + * Copyright (c) 2012 PetaLogix Pty Ltd. + * Written By Haibing Ma + * M. Habib + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#ifndef HW_TIMER_CADENCE_TTC_H +#define HW_TIMER_CADENCE_TTC_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" + +typedef struct { + QEMUTimer *timer; + int freq; + + uint32_t reg_clock; + uint32_t reg_count; + uint32_t reg_value; + uint16_t reg_interval; + uint16_t reg_match[3]; + uint32_t reg_intr; + uint32_t reg_intr_en; + uint32_t reg_event_ctrl; + uint32_t reg_event; + + uint64_t cpu_time; + unsigned int cpu_time_valid; + + qemu_irq irq; +} CadenceTimerState; + +#define TYPE_CADENCE_TTC "cadence_ttc" +OBJECT_DECLARE_SIMPLE_TYPE(CadenceTTCState, CADENCE_TTC) + +struct CadenceTTCState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + CadenceTimerState timer[3]; +}; + +#endif diff --git a/include/hw/virtio/vhost-user-i2c.h b/include/hw/virtio/vhost-user-i2c.h index deae47a76d..d8372f3b43 100644 --- a/include/hw/virtio/vhost-user-i2c.h +++ b/include/hw/virtio/vhost-user-i2c.h @@ -25,4 +25,7 @@ struct VHostUserI2C { bool connected; }; +/* Virtio Feature bits */ +#define VIRTIO_I2C_F_ZERO_LENGTH_REQUEST 0 + #endif /* _QEMU_VHOST_USER_I2C_H */ diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h index a9abca3288..e44a41bb70 100644 --- a/include/hw/virtio/vhost-user.h +++ b/include/hw/virtio/vhost-user.h @@ -12,9 +12,10 @@ #include "hw/virtio/virtio.h" typedef struct VhostUserHostNotifier { + struct rcu_head rcu; MemoryRegion mr; void *addr; - bool set; + void *unmap_addr; } VhostUserHostNotifier; typedef struct VhostUserState { diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index 3ce79a646d..a29dbb3f53 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -12,6 +12,9 @@ #ifndef HW_VIRTIO_VHOST_VDPA_H #define HW_VIRTIO_VHOST_VDPA_H +#include + +#include "hw/virtio/vhost-iova-tree.h" #include "hw/virtio/virtio.h" #include "standard-headers/linux/vhost_types.h" @@ -27,6 +30,11 @@ typedef struct vhost_vdpa { bool iotlb_batch_begin_sent; MemoryListener listener; struct vhost_vdpa_iova_range iova_range; + uint64_t acked_features; + bool shadow_vqs_enabled; + /* IOVA mapping used by the Shadow Virtqueue */ + VhostIOVATree *iova_tree; + GPtrArray *shadow_vqs; struct vhost_dev *dev; VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX]; } VhostVDPA; diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h index 6818a23a2d..07aae69042 100644 --- a/include/hw/virtio/virtio-access.h +++ b/include/hw/virtio/virtio-access.h @@ -28,7 +28,7 @@ static inline bool virtio_access_is_big_endian(VirtIODevice *vdev) { #if defined(LEGACY_VIRTIO_IS_BIENDIAN) return virtio_is_big_endian(vdev); -#elif defined(TARGET_WORDS_BIGENDIAN) +#elif TARGET_BIG_ENDIAN if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { /* Devices conforming to VIRTIO 1.0 or later are always LE. */ return false; @@ -149,7 +149,7 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr) static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN return virtio_access_is_big_endian(vdev) ? s : bswap16(s); #else return virtio_access_is_big_endian(vdev) ? bswap16(s) : s; @@ -215,7 +215,7 @@ static inline void virtio_tswap16s(VirtIODevice *vdev, uint16_t *s) static inline uint32_t virtio_tswap32(VirtIODevice *vdev, uint32_t s) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN return virtio_access_is_big_endian(vdev) ? s : bswap32(s); #else return virtio_access_is_big_endian(vdev) ? bswap32(s) : s; @@ -229,7 +229,7 @@ static inline void virtio_tswap32s(VirtIODevice *vdev, uint32_t *s) static inline uint64_t virtio_tswap64(VirtIODevice *vdev, uint64_t s) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN return virtio_access_is_big_endian(vdev) ? s : bswap64(s); #else return virtio_access_is_big_endian(vdev) ? bswap64(s) : s; diff --git a/include/hw/virtio/virtio-gpu-bswap.h b/include/hw/virtio/virtio-gpu-bswap.h index 5faac0d8d5..9124108485 100644 --- a/include/hw/virtio/virtio-gpu-bswap.h +++ b/include/hw/virtio/virtio-gpu-bswap.h @@ -29,7 +29,7 @@ virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr) static inline void virtio_gpu_bswap_32(void *ptr, size_t size) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN size_t i; struct virtio_gpu_ctrl_hdr *hdr = (struct virtio_gpu_ctrl_hdr *) ptr; diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index e2339e5b72..84391f8448 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -58,6 +58,7 @@ struct VirtIOIOMMU { GTree *domains; QemuMutex mutex; GTree *endpoints; + bool boot_bypass; }; #endif diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index f095637058..b31c4507f5 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -169,7 +169,7 @@ void virtio_init(VirtIODevice *vdev, const char *name, uint16_t device_id, size_t config_size); void virtio_cleanup(VirtIODevice *vdev); -void virtio_error(VirtIODevice *vdev, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void virtio_error(VirtIODevice *vdev, const char *fmt, ...) G_GNUC_PRINTF(2, 3); /* Set the child bus name. */ void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name); diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h index f945cd6c58..dfa5dfa424 100644 --- a/include/hw/watchdog/wdt_aspeed.h +++ b/include/hw/watchdog/wdt_aspeed.h @@ -19,6 +19,7 @@ OBJECT_DECLARE_TYPE(AspeedWDTState, AspeedWDTClass, ASPEED_WDT) #define TYPE_ASPEED_2400_WDT TYPE_ASPEED_WDT "-ast2400" #define TYPE_ASPEED_2500_WDT TYPE_ASPEED_WDT "-ast2500" #define TYPE_ASPEED_2600_WDT TYPE_ASPEED_WDT "-ast2600" +#define TYPE_ASPEED_1030_WDT TYPE_ASPEED_WDT "-ast1030" #define ASPEED_WDT_REGS_MAX (0x20 / 4) @@ -45,6 +46,8 @@ struct AspeedWDTClass { void (*reset_pulse)(AspeedWDTState *s, uint32_t property); void (*wdt_reload)(AspeedWDTState *s); uint64_t (*sanitize_ctrl)(uint64_t data); + uint32_t default_status; + uint32_t default_reload_value; }; #endif /* WDT_ASPEED_H */ diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h index 4c0f747445..629a904d1a 100644 --- a/include/hw/xen/xen-bus-helper.h +++ b/include/hw/xen/xen-bus-helper.h @@ -22,11 +22,11 @@ void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid, void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, va_list ap) - GCC_FMT_ATTR(6, 0); + G_GNUC_PRINTF(6, 0); void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, ...) - GCC_FMT_ATTR(6, 7); + G_GNUC_PRINTF(6, 7); /* Read from node/key unless node is empty, in which case read from key */ int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid, diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index 6bdbf3ff82..713e763348 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -88,10 +88,10 @@ enum xenbus_state xen_device_backend_get_state(XenDevice *xendev); void xen_device_backend_printf(XenDevice *xendev, const char *key, const char *fmt, ...) - GCC_FMT_ATTR(3, 4); + G_GNUC_PRINTF(3, 4); void xen_device_frontend_printf(XenDevice *xendev, const char *key, const char *fmt, ...) - GCC_FMT_ATTR(3, 4); + G_GNUC_PRINTF(3, 4); int xen_device_frontend_scanf(XenDevice *xendev, const char *key, const char *fmt, ...); diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index a8118b41ac..179741ff79 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -358,7 +358,7 @@ static inline int xen_restrict(domid_t domid) void destroy_hvm_domain(bool reboot); /* shutdown/destroy current domain because of an error */ -void xen_shutdown_fatal_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +void xen_shutdown_fatal_error(const char *fmt, ...) G_GNUC_PRINTF(1, 2); #ifdef HVM_PARAM_VMPORT_REGS_PFN static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index 83e5174d90..7cd4bc2b82 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -76,6 +76,6 @@ void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev); int xen_pv_send_notify(struct XenLegacyDevice *xendev); void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level, - const char *fmt, ...) GCC_FMT_ATTR(3, 4); + const char *fmt, ...) G_GNUC_PRINTF(3, 4); #endif /* QEMU_HW_XEN_PVDEV_H */ diff --git a/include/io/channel-command.h b/include/io/channel-command.h index 27e42bdadc..305ac1d280 100644 --- a/include/io/channel-command.h +++ b/include/io/channel-command.h @@ -45,31 +45,6 @@ struct QIOChannelCommand { }; -/** - * qio_channel_command_new_pid: - * @writefd: the FD connected to the command's stdin - * @readfd: the FD connected to the command's stdout - * @pid: the PID of the running child command - * @errp: pointer to a NULL-initialized error object - * - * Create a channel for performing I/O with the - * previously spawned command identified by @pid. - * The two file descriptors provide the connection - * to command's stdio streams, either one or which - * may be -1 to indicate that stream is not open. - * - * The channel will take ownership of the process - * @pid and will kill it when closing the channel. - * Similarly it will take responsibility for - * closing the file descriptors @writefd and @readfd. - * - * Returns: the command channel object, or NULL on error - */ -QIOChannelCommand * -qio_channel_command_new_pid(int writefd, - int readfd, - pid_t pid); - /** * qio_channel_command_new_spawn: * @argv: the NULL terminated list of command arguments diff --git a/include/libdecnumber/dconfig.h b/include/libdecnumber/dconfig.h index 0f7dccef1f..2bc0ba7f14 100644 --- a/include/libdecnumber/dconfig.h +++ b/include/libdecnumber/dconfig.h @@ -28,7 +28,7 @@ 02110-1301, USA. */ -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define WORDS_BIGENDIAN 1 #else #define WORDS_BIGENDIAN 0 diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 017c03675c..ad24aa1934 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -181,9 +181,7 @@ struct VMStateDescription { int unmigratable; int version_id; int minimum_version_id; - int minimum_version_id_old; MigrationPriority priority; - LoadStateHandler *load_state_old; int (*pre_load)(void *opaque); int (*post_load)(void *opaque, int version_id); int (*pre_save)(void *opaque); diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h index ffdc15a34b..1891a19b21 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -50,5 +50,6 @@ void hmp_mce(Monitor *mon, const QDict *qdict); void hmp_info_local_apic(Monitor *mon, const QDict *qdict); void hmp_info_sev(Monitor *mon, const QDict *qdict); void hmp_info_sgx(Monitor *mon, const QDict *qdict); +void hmp_info_via(Monitor *mon, const QDict *qdict); #endif /* MONITOR_HMP_TARGET_H */ diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 12d395d62d..a4b40e8391 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -32,8 +32,8 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp); int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp); int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); -int monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 0); +int monitor_printf(Monitor *mon, const char *fmt, ...) G_GNUC_PRINTF(2, 3); void monitor_flush(Monitor *mon); int monitor_set_cpu(Monitor *mon, int cpu_index); int monitor_get_cpu_index(Monitor *mon); @@ -56,4 +56,7 @@ void monitor_register_hmp(const char *name, bool info, void monitor_register_hmp_info_hrt(const char *name, HumanReadableText *(*handler)(Error **errp)); +int error_vprintf_unless_qmp(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); +int error_printf_unless_qmp(const char *fmt, ...) G_GNUC_PRINTF(1, 2); + #endif /* MONITOR_H */ diff --git a/include/net/eth.h b/include/net/eth.h index 7767ae880e..6e699b0d7a 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -159,7 +159,7 @@ struct tcp_hdr { u_short th_dport; /* destination port */ uint32_t th_seq; /* sequence number */ uint32_t th_ack; /* acknowledgment number */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN u_char th_off : 4, /* data offset */ th_x2:4; /* (unused) */ #else diff --git a/include/qapi/error.h b/include/qapi/error.h index 4a9260b0cc..d798faeec3 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -320,7 +320,7 @@ ErrorClass error_get_class(const Error *err); void error_setg_internal(Error **errp, const char *src, int line, const char *func, const char *fmt, ...) - GCC_FMT_ATTR(5, 6); + G_GNUC_PRINTF(5, 6); /* * Just like error_setg(), with @os_error info added to the message. @@ -336,7 +336,7 @@ void error_setg_internal(Error **errp, void error_setg_errno_internal(Error **errp, const char *fname, int line, const char *func, int os_error, const char *fmt, ...) - GCC_FMT_ATTR(6, 7); + G_GNUC_PRINTF(6, 7); #ifdef _WIN32 /* @@ -350,7 +350,7 @@ void error_setg_errno_internal(Error **errp, void error_setg_win32_internal(Error **errp, const char *src, int line, const char *func, int win32_err, const char *fmt, ...) - GCC_FMT_ATTR(6, 7); + G_GNUC_PRINTF(6, 7); #endif /* @@ -383,21 +383,21 @@ void error_propagate(Error **dst_errp, Error *local_err); */ void error_propagate_prepend(Error **dst_errp, Error *local_err, const char *fmt, ...) - GCC_FMT_ATTR(3, 4); + G_GNUC_PRINTF(3, 4); /* * Prepend some text to @errp's human-readable error message. * The text is made by formatting @fmt, @ap like vprintf(). */ void error_vprepend(Error *const *errp, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); + G_GNUC_PRINTF(2, 0); /* * Prepend some text to @errp's human-readable error message. * The text is made by formatting @fmt, ... like printf(). */ void error_prepend(Error *const *errp, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /* * Append a printf-style human-readable explanation to an existing error. @@ -414,7 +414,7 @@ void error_prepend(Error *const *errp, const char *fmt, ...) * newline. */ void error_append_hint(Error *const *errp, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /* * Convenience function to report open() failure. @@ -458,13 +458,13 @@ void error_report_err(Error *err); * Convenience function to error_prepend(), warn_report() and free @err. */ void warn_reportf_err(Error *err, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /* * Convenience function to error_prepend(), error_report() and free @err. */ void error_reportf_err(Error *err, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /* * Just like error_setg(), except you get to specify the error class. @@ -477,7 +477,7 @@ void error_reportf_err(Error *err, const char *fmt, ...) void error_set_internal(Error **errp, const char *src, int line, const char *func, ErrorClass err_class, const char *fmt, ...) - GCC_FMT_ATTR(6, 7); + G_GNUC_PRINTF(6, 7); /* * Make @errp parameter easier to use regardless of argument value diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h index 2f888d1057..0d09726939 100644 --- a/include/qapi/qmp/qbool.h +++ b/include/qapi/qmp/qbool.h @@ -21,6 +21,10 @@ struct QBool { bool value; }; +void qbool_unref(QBool *q); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QBool, qbool_unref) + QBool *qbool_from_bool(bool value); bool qbool_get_bool(const QBool *qb); diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index d5b5430e21..82e90fc072 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -30,6 +30,10 @@ struct QDict { QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; }; +void qdict_unref(QDict *q); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QDict, qdict_unref) + /* Object API */ QDict *qdict_new(void); const char *qdict_entry_key(const QDictEntry *entry); @@ -64,7 +68,4 @@ const char *qdict_get_try_str(const QDict *qdict, const char *key); QDict *qdict_clone_shallow(const QDict *src); -QObject *qdict_crumple(const QDict *src, Error **errp); -void qdict_flatten(QDict *qdict); - #endif /* QDICT_H */ diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h index 593b40b4e0..7bd8d2de1b 100644 --- a/include/qapi/qmp/qjson.h +++ b/include/qapi/qmp/qjson.h @@ -17,13 +17,13 @@ QObject *qobject_from_json(const char *string, Error **errp); QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap) - GCC_FMT_ATTR(1, 0); + G_GNUC_PRINTF(1, 0); QObject *qobject_from_jsonf_nofail(const char *string, ...) - GCC_FMT_ATTR(1, 2); + G_GNUC_PRINTF(1, 2); QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap) - GCC_FMT_ATTR(1, 0); + G_GNUC_PRINTF(1, 0); QDict *qdict_from_jsonf_nofail(const char *string, ...) - GCC_FMT_ATTR(1, 2); + G_GNUC_PRINTF(1, 2); GString *qobject_to_json(const QObject *obj); GString *qobject_to_json_pretty(const QObject *obj, bool pretty); diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h index 06e98ad5f4..e4e985d435 100644 --- a/include/qapi/qmp/qlist.h +++ b/include/qapi/qmp/qlist.h @@ -26,6 +26,10 @@ struct QList { QTAILQ_HEAD(,QListEntry) head; }; +void qlist_unref(QList *q); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QList, qlist_unref) + #define qlist_append(qlist, obj) \ qlist_append_obj(qlist, QOBJECT(obj)) diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h index e84ecceedb..7feb7c7d83 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qapi/qmp/qnull.h @@ -26,4 +26,8 @@ static inline QNull *qnull(void) return qobject_ref(&qnull_); } +void qnull_unref(QNull *q); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QNull, qnull_unref) + #endif /* QNULL_H */ diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 7f84e20bfb..e86788dd2e 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -54,6 +54,10 @@ struct QNum { } u; }; +void qnum_unref(QNum *q); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QNum, qnum_unref) + QNum *qnum_from_int(int64_t value); QNum *qnum_from_uint(uint64_t value); QNum *qnum_from_double(double value); diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h index 1d8ba46936..318d815d6a 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qapi/qmp/qstring.h @@ -20,6 +20,10 @@ struct QString { const char *string; }; +void qstring_unref(QString *q); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QString, qstring_unref) + QString *qstring_new(void); QString *qstring_from_str(const char *str); QString *qstring_from_substr(const char *str, size_t start, size_t end); diff --git a/include/qemu-common.h b/include/qemu-common.h deleted file mode 100644 index 68b2e3bc10..0000000000 --- a/include/qemu-common.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is supposed to be included only by .c files. No header file should - * depend on qemu-common.h, as this would easily lead to circular header - * dependencies. - * - * If a header file uses a definition from qemu-common.h, that definition - * must be moved to a separate header file, and the header that uses it - * must include that header. - */ -#ifndef QEMU_COMMON_H -#define QEMU_COMMON_H - -#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) - -/* Copyright string for -version arguments, About dialogs, etc */ -#define QEMU_COPYRIGHT "Copyright (c) 2003-2022 " \ - "Fabrice Bellard and the QEMU Project developers" - -/* Bug reporting information for --help arguments, About dialogs, etc */ -#define QEMU_HELP_BOTTOM \ - "See for how to report bugs.\n" \ - "More information on the QEMU project at ." - -/* main function, renamed */ -#if defined(CONFIG_COCOA) -int qemu_main(int argc, char **argv, char **envp); -#endif - -void *qemu_oom_check(void *ptr); - -ssize_t qemu_write_full(int fd, const void *buf, size_t count) - QEMU_WARN_UNUSED_RESULT; - -#ifndef _WIN32 -int qemu_pipe(int pipefd[2]); -/* like openpty() but also makes it raw; return master fd */ -int qemu_openpty_raw(int *aslave, char *pty_name); -#endif - -#ifdef _WIN32 -/* MinGW needs type casts for the 'buf' and 'optval' arguments. */ -#define qemu_getsockopt(sockfd, level, optname, optval, optlen) \ - getsockopt(sockfd, level, optname, (void *)optval, optlen) -#define qemu_setsockopt(sockfd, level, optname, optval, optlen) \ - setsockopt(sockfd, level, optname, (const void *)optval, optlen) -#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, (void *)buf, len, flags) -#define qemu_sendto(sockfd, buf, len, flags, destaddr, addrlen) \ - sendto(sockfd, (const void *)buf, len, flags, destaddr, addrlen) -#else -#define qemu_getsockopt(sockfd, level, optname, optval, optlen) \ - getsockopt(sockfd, level, optname, optval, optlen) -#define qemu_setsockopt(sockfd, level, optname, optval, optlen) \ - setsockopt(sockfd, level, optname, optval, optlen) -#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, buf, len, flags) -#define qemu_sendto(sockfd, buf, len, flags, destaddr, addrlen) \ - sendto(sockfd, buf, len, flags, destaddr, addrlen) -#endif - -void cpu_exec_init_all(void); -void cpu_exec_step_atomic(CPUState *cpu); - -/** - * set_preferred_target_page_bits: - * @bits: number of bits needed to represent an address within the page - * - * Set the preferred target page size (the actual target page - * size may be smaller than any given CPU's preference). - * Returns true on success, false on failure (which can only happen - * if this is called after the system has already finalized its - * choice of page size and the requested page size is smaller than that). - */ -bool set_preferred_target_page_bits(int bits); - -/** - * finalize_target_page_bits: - * Commit the final value set by set_preferred_target_page_bits. - */ -void finalize_target_page_bits(void); - -/** - * Sends a (part of) iovec down a socket, yielding when the socket is full, or - * Receives data into a (part of) iovec from a socket, - * yielding when there is no data in the socket. - * The same interface as qemu_sendv_recvv(), with added yielding. - * XXX should mark these as coroutine_fn - */ -ssize_t qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt, - size_t offset, size_t bytes, bool do_send); -#define qemu_co_recvv(sockfd, iov, iov_cnt, offset, bytes) \ - qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, false) -#define qemu_co_sendv(sockfd, iov, iov_cnt, offset, bytes) \ - qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, true) - -/** - * The same as above, but with just a single buffer - */ -ssize_t qemu_co_send_recv(int sockfd, void *buf, size_t bytes, bool do_send); -#define qemu_co_recv(sockfd, buf, bytes) \ - qemu_co_send_recv(sockfd, buf, bytes, false) -#define qemu_co_send(sockfd, buf, bytes) \ - qemu_co_send_recv(sockfd, buf, bytes, true) - -void qemu_progress_init(int enabled, float min_skip); -void qemu_progress_end(void); -void qemu_progress_print(float delta, int max); -const char *qemu_get_vm_name(void); - -/* OS specific functions */ -void os_setup_early_signal_handling(void); -int os_parse_cmd_args(int index, const char *optarg); - -/* - * Hexdump a line of a byte buffer into a hexadecimal/ASCII buffer - */ -#define QEMU_HEXDUMP_LINE_BYTES 16 /* Number of bytes to dump */ -#define QEMU_HEXDUMP_LINE_LEN 75 /* Number of characters in line */ -void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, - unsigned int len, bool ascii); - -/* - * Hexdump a buffer to a file. An optional string prefix is added to every line - */ - -void qemu_hexdump(FILE *fp, const char *prefix, - const void *bufptr, size_t size); - -/* - * helper to parse debug environment variables - */ -int parse_debug_env(const char *name, int max, int initial); - -const char *qemu_ether_ntoa(const MACAddr *mac); -void page_size_init(void); - -/* returns non-zero if dump is in progress, otherwise zero is - * returned. */ -bool dump_in_progress(void); - -#endif diff --git a/include/qemu-main.h b/include/qemu-main.h new file mode 100644 index 0000000000..6a3e90d0ad --- /dev/null +++ b/include/qemu-main.h @@ -0,0 +1,10 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_MAIN_H +#define QEMU_MAIN_H + +int qemu_main(int argc, char **argv, char **envp); + +#endif /* QEMU_MAIN_H */ diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 112a29910b..7e8fc8e7cd 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -15,6 +15,8 @@ #ifndef QEMU_ATOMIC_H #define QEMU_ATOMIC_H +#include "compiler.h" + /* Compiler barrier */ #define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) @@ -81,7 +83,7 @@ * no processors except Alpha need a barrier here. Leave it in if * using Thread Sanitizer to avoid warnings, otherwise optimize it away. */ -#if defined(__SANITIZE_THREAD__) +#ifdef QEMU_SANITIZE_THREAD #define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); }) #elif defined(__alpha__) #define smp_read_barrier_depends() asm volatile("mb":::"memory") @@ -146,7 +148,7 @@ /* See above: most compilers currently treat consume and acquire the * same, but this slows down qatomic_rcu_read unnecessarily. */ -#ifdef __SANITIZE_THREAD__ +#ifdef QEMU_SANITIZE_THREAD #define qatomic_rcu_read__nocheck(ptr, valptr) \ __atomic_load(ptr, valptr, __ATOMIC_CONSUME); #else @@ -254,7 +256,7 @@ #define qatomic_mb_read(ptr) \ qatomic_load_acquire(ptr) -#if !defined(__SANITIZE_THREAD__) && \ +#if !defined(QEMU_SANITIZE_THREAD) && \ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) /* This is more efficient than a store plus a fence. */ # define qatomic_mb_set(ptr, i) ((void)qatomic_xchg(ptr, i)) diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 2d3bb8bbed..346d05f2aa 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -19,8 +19,6 @@ extern "C" { #endif -#include "fpu/softfloat-types.h" - #ifdef BSWAP_FROM_BYTESWAP static inline uint16_t bswap16(uint16_t x) { @@ -84,7 +82,7 @@ static inline void bswap64s(uint64_t *s) *s = bswap64(*s); } -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define be_bswap(v, size) (v) #define le_bswap(v, size) glue(bswap, size)(v) #define be_bswaps(v, size) @@ -188,7 +186,7 @@ CPU_CONVERT(le, 64, uint64_t) * a compile-time constant if you pass in a constant. So this can be * used to initialize static variables. */ -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN # define const_le32(_x) \ ((((_x) & 0x000000ffU) << 24) | \ (((_x) & 0x0000ff00U) << 8) | \ @@ -202,64 +200,6 @@ CPU_CONVERT(le, 64, uint64_t) # define const_le16(_x) (_x) #endif -/* Unions for reinterpreting between floats and integers. */ - -typedef union { - float32 f; - uint32_t l; -} CPU_FloatU; - -typedef union { - float64 d; -#if defined(HOST_WORDS_BIGENDIAN) - struct { - uint32_t upper; - uint32_t lower; - } l; -#else - struct { - uint32_t lower; - uint32_t upper; - } l; -#endif - uint64_t ll; -} CPU_DoubleU; - -typedef union { - floatx80 d; - struct { - uint64_t lower; - uint16_t upper; - } l; -} CPU_LDoubleU; - -typedef union { - float128 q; -#if defined(HOST_WORDS_BIGENDIAN) - struct { - uint32_t upmost; - uint32_t upper; - uint32_t lower; - uint32_t lowest; - } l; - struct { - uint64_t upper; - uint64_t lower; - } ll; -#else - struct { - uint32_t lowest; - uint32_t lower; - uint32_t upper; - uint32_t upmost; - } l; - struct { - uint64_t lower; - uint64_t upper; - } ll; -#endif -} CPU_QuadU; - /* unaligned/endian-independent pointer access */ /* diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h index d34d2c857c..e95dfd696c 100644 --- a/include/qemu/buffer.h +++ b/include/qemu/buffer.h @@ -49,7 +49,7 @@ struct Buffer { * to identify in debug traces. */ void buffer_init(Buffer *buffer, const char *name, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /** * buffer_shrink: diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 3baa5e3790..f20a76e4a2 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -7,6 +7,11 @@ #ifndef COMPILER_H #define COMPILER_H +#define HOST_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + +/* HOST_LONG_BITS is the size of a native pointer in bits. */ +#define HOST_LONG_BITS (__SIZEOF_POINTER__ * 8) + #if defined __clang_analyzer__ || defined __COVERITY__ #define QEMU_STATIC_ANALYSIS 1 #endif @@ -17,12 +22,6 @@ #define QEMU_EXTERN_C extern #endif -#define QEMU_NORETURN __attribute__ ((__noreturn__)) - -#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) - -#define QEMU_SENTINEL __attribute__((sentinel)) - #if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) # define QEMU_PACKED __attribute__((gcc_struct, packed)) #else @@ -79,19 +78,12 @@ #define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \ sizeof(QEMU_BUILD_BUG_ON_STRUCT(x))) -#if defined(__clang__) -/* clang doesn't support gnu_printf, so use printf. */ -# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m))) -#else -/* Use gnu_printf (qemu uses standard format strings). */ -# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) -# if defined(_WIN32) +#if !defined(__clang__) && defined(_WIN32) /* * Map __printf__ to __gnu_printf__ because we want standard format strings even * when MinGW or GLib include files use __printf__. */ -# define __printf__ __gnu_printf__ -# endif +# define __printf__ __gnu_printf__ #endif #ifndef __has_warning @@ -114,6 +106,14 @@ #define __has_attribute(x) 0 /* compatibility with older GCC */ #endif +#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) +# define QEMU_SANITIZE_ADDRESS 1 +#endif + +#if defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer) +# define QEMU_SANITIZE_THREAD 1 +#endif + /* * GCC doesn't provide __has_attribute() until GCC 5, but we know all the GCC * versions we support have the "flatten" attribute. Clang may not have the @@ -162,22 +162,6 @@ #define QEMU_ALWAYS_INLINE #endif -/** - * qemu_build_not_reached() - * - * The compiler, during optimization, is expected to prove that a call - * to this function cannot be reached and remove it. If the compiler - * supports QEMU_ERROR, this will be reported at compile time; otherwise - * this will be reported at link time due to the missing symbol. - */ -extern void QEMU_NORETURN QEMU_ERROR("code path is reachable") - qemu_build_not_reached_always(void); -#if defined(__OPTIMIZE__) && !defined(__NO_INLINE__) -#define qemu_build_not_reached() qemu_build_not_reached_always() -#else -#define qemu_build_not_reached() g_assert_not_reached() -#endif - /** * In most cases, normal "fallthrough" comments are good enough for * switch-case statements, but sometimes the compiler has problems diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h index f605423321..321e7c7c03 100644 --- a/include/qemu/config-file.h +++ b/include/qemu/config-file.h @@ -12,7 +12,6 @@ void qemu_add_opts(QemuOptsList *list); void qemu_add_drive_opts(QemuOptsList *list); int qemu_global_option(const char *str); -void qemu_config_write(FILE *fp); int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp); diff --git a/include/qemu/coroutine-tls.h b/include/qemu/coroutine-tls.h new file mode 100644 index 0000000000..1558a826aa --- /dev/null +++ b/include/qemu/coroutine-tls.h @@ -0,0 +1,165 @@ +/* + * QEMU Thread Local Storage for coroutines + * + * Copyright Red Hat + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + * It is forbidden to access Thread Local Storage in coroutines because + * compiler optimizations may cause values to be cached across coroutine + * re-entry. Coroutines can run in more than one thread through the course of + * their life, leading bugs when stale TLS values from the wrong thread are + * used as a result of compiler optimization. + * + * An example is: + * + * ..code-block:: c + * :caption: A coroutine that may see the wrong TLS value + * + * static __thread AioContext *current_aio_context; + * ... + * static void coroutine_fn foo(void) + * { + * aio_notify(current_aio_context); + * qemu_coroutine_yield(); + * aio_notify(current_aio_context); // <-- may be stale after yielding! + * } + * + * This header provides macros for safely defining variables in Thread Local + * Storage: + * + * ..code-block:: c + * :caption: A coroutine that safely uses TLS + * + * QEMU_DEFINE_STATIC_CO_TLS(AioContext *, current_aio_context) + * ... + * static void coroutine_fn foo(void) + * { + * aio_notify(get_current_aio_context()); + * qemu_coroutine_yield(); + * aio_notify(get_current_aio_context()); // <-- safe + * } + */ + +#ifndef QEMU_COROUTINE_TLS_H +#define QEMU_COROUTINE_TLS_H + +/* + * To stop the compiler from caching TLS values we define accessor functions + * with __attribute__((noinline)) plus asm volatile("") to prevent + * optimizations that override noinline. + * + * The compiler can still analyze noinline code and make optimizations based on + * that knowledge, so an inline asm output operand is used to prevent + * optimizations that make assumptions about the address of the TLS variable. + * + * This is fragile and ultimately needs to be solved by a mechanism that is + * guaranteed to work by the compiler (e.g. stackless coroutines), but for now + * we use this approach to prevent issues. + */ + +/** + * QEMU_DECLARE_CO_TLS: + * @type: the variable's C type + * @var: the variable name + * + * Declare an extern variable in Thread Local Storage from a header file: + * + * .. code-block:: c + * :caption: Declaring an extern variable in Thread Local Storage + * + * QEMU_DECLARE_CO_TLS(int, my_count) + * ... + * int c = get_my_count(); + * set_my_count(c + 1); + * *get_ptr_my_count() = 0; + * + * This is a coroutine-safe replacement for the __thread keyword and is + * equivalent to the following code: + * + * .. code-block:: c + * :caption: Declaring a TLS variable using __thread + * + * extern __thread int my_count; + * ... + * int c = my_count; + * my_count = c + 1; + * *(&my_count) = 0; + */ +#define QEMU_DECLARE_CO_TLS(type, var) \ + __attribute__((noinline)) type get_##var(void); \ + __attribute__((noinline)) void set_##var(type v); \ + __attribute__((noinline)) type *get_ptr_##var(void); + +/** + * QEMU_DEFINE_CO_TLS: + * @type: the variable's C type + * @var: the variable name + * + * Define a variable in Thread Local Storage that was previously declared from + * a header file with QEMU_DECLARE_CO_TLS(): + * + * .. code-block:: c + * :caption: Defining a variable in Thread Local Storage + * + * QEMU_DEFINE_CO_TLS(int, my_count) + * + * This is a coroutine-safe replacement for the __thread keyword and is + * equivalent to the following code: + * + * .. code-block:: c + * :caption: Defining a TLS variable using __thread + * + * __thread int my_count; + */ +#define QEMU_DEFINE_CO_TLS(type, var) \ + static __thread type co_tls_##var; \ + type get_##var(void) { asm volatile(""); return co_tls_##var; } \ + void set_##var(type v) { asm volatile(""); co_tls_##var = v; } \ + type *get_ptr_##var(void) \ + { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; } + +/** + * QEMU_DEFINE_STATIC_CO_TLS: + * @type: the variable's C type + * @var: the variable name + * + * Define a static variable in Thread Local Storage: + * + * .. code-block:: c + * :caption: Defining a static variable in Thread Local Storage + * + * QEMU_DEFINE_STATIC_CO_TLS(int, my_count) + * ... + * int c = get_my_count(); + * set_my_count(c + 1); + * *get_ptr_my_count() = 0; + * + * This is a coroutine-safe replacement for the __thread keyword and is + * equivalent to the following code: + * + * .. code-block:: c + * :caption: Defining a static TLS variable using __thread + * + * static __thread int my_count; + * ... + * int c = my_count; + * my_count = c + 1; + * *(&my_count) = 0; + */ +#define QEMU_DEFINE_STATIC_CO_TLS(type, var) \ + static __thread type co_tls_##var; \ + static __attribute__((noinline, unused)) \ + type get_##var(void) \ + { asm volatile(""); return co_tls_##var; } \ + static __attribute__((noinline, unused)) \ + void set_##var(type v) \ + { asm volatile(""); co_tls_##var = v; } \ + static __attribute__((noinline, unused)) \ + type *get_ptr_##var(void) \ + { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; } + +#endif /* QEMU_COROUTINE_TLS_H */ diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index c828a95ee0..284571badb 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -343,4 +343,27 @@ void qemu_coroutine_decrease_pool_batch_size(unsigned int additional_pool_size); #include "qemu/lockable.h" +/** + * Sends a (part of) iovec down a socket, yielding when the socket is full, or + * Receives data into a (part of) iovec from a socket, + * yielding when there is no data in the socket. + * The same interface as qemu_sendv_recvv(), with added yielding. + * XXX should mark these as coroutine_fn + */ +ssize_t qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt, + size_t offset, size_t bytes, bool do_send); +#define qemu_co_recvv(sockfd, iov, iov_cnt, offset, bytes) \ + qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, false) +#define qemu_co_sendv(sockfd, iov, iov_cnt, offset, bytes) \ + qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, true) + +/** + * The same as above, but with just a single buffer + */ +ssize_t qemu_co_send_recv(int sockfd, void *buf, size_t bytes, bool do_send); +#define qemu_co_recv(sockfd, buf, bytes) \ + qemu_co_send_recv(sockfd, buf, bytes, false) +#define qemu_co_send(sockfd, buf, bytes) \ + qemu_co_send_recv(sockfd, buf, bytes, true) + #endif /* QEMU_COROUTINE_H */ diff --git a/include/qemu/cpu-float.h b/include/qemu/cpu-float.h new file mode 100644 index 0000000000..911099499f --- /dev/null +++ b/include/qemu/cpu-float.h @@ -0,0 +1,64 @@ +#ifndef QEMU_CPU_FLOAT_H_ +#define QEMU_CPU_FLOAT_H_ + +#include "fpu/softfloat-types.h" + +/* Unions for reinterpreting between floats and integers. */ + +typedef union { + float32 f; + uint32_t l; +} CPU_FloatU; + +typedef union { + float64 d; +#if HOST_BIG_ENDIAN + struct { + uint32_t upper; + uint32_t lower; + } l; +#else + struct { + uint32_t lower; + uint32_t upper; + } l; +#endif + uint64_t ll; +} CPU_DoubleU; + +typedef union { + floatx80 d; + struct { + uint64_t lower; + uint16_t upper; + } l; +} CPU_LDoubleU; + +typedef union { + float128 q; +#if HOST_BIG_ENDIAN + struct { + uint32_t upmost; + uint32_t upper; + uint32_t lower; + uint32_t lowest; + } l; + struct { + uint64_t upper; + uint64_t lower; + } ll; +#else + struct { + uint32_t lowest; + uint32_t lower; + uint32_t upper; + uint32_t upmost; + } l; + struct { + uint64_t lower; + uint64_t upper; + } ll; +#endif +} CPU_QuadU; + +#endif /* QEMU_CPU_FLOAT_H_ */ diff --git a/include/qemu/cpuid.h b/include/qemu/cpuid.h index 09fc245b91..7adb12d320 100644 --- a/include/qemu/cpuid.h +++ b/include/qemu/cpuid.h @@ -45,12 +45,26 @@ #ifndef bit_AVX2 #define bit_AVX2 (1 << 5) #endif -#ifndef bit_AVX512F -#define bit_AVX512F (1 << 16) -#endif #ifndef bit_BMI2 #define bit_BMI2 (1 << 8) #endif +#ifndef bit_AVX512F +#define bit_AVX512F (1 << 16) +#endif +#ifndef bit_AVX512DQ +#define bit_AVX512DQ (1 << 17) +#endif +#ifndef bit_AVX512BW +#define bit_AVX512BW (1 << 30) +#endif +#ifndef bit_AVX512VL +#define bit_AVX512VL (1u << 31) +#endif + +/* Leaf 7, %ecx */ +#ifndef bit_AVX512VBMI2 +#define bit_AVX512VBMI2 (1 << 6) +#endif /* Leaf 0x80000001, %ecx */ #ifndef bit_LZCNT diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 320543950c..5c6572d444 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -129,9 +129,6 @@ static inline const char *qemu_strchrnul(const char *s, int c) const char *qemu_strchrnul(const char *s, int c); #endif time_t mktimegm(struct tm *tm); -int qemu_fdatasync(int fd); -int qemu_msync(void *addr, size_t length, int fd); -int fcntl_setfl(int fd, int flag); int qemu_parse_fd(const char *param); int qemu_strtoi(const char *nptr, const char **endptr, int base, int *result); @@ -214,4 +211,24 @@ static inline const char *yes_no(bool b) return b ? "yes" : "no"; } +/* + * helper to parse debug environment variables + */ +int parse_debug_env(const char *name, int max, int initial); + +/* + * Hexdump a line of a byte buffer into a hexadecimal/ASCII buffer + */ +#define QEMU_HEXDUMP_LINE_BYTES 16 /* Number of bytes to dump */ +#define QEMU_HEXDUMP_LINE_LEN 75 /* Number of characters in line */ +void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, + unsigned int len, bool ascii); + +/* + * Hexdump a buffer to a file. An optional string prefix is added to every line + */ + +void qemu_hexdump(FILE *fp, const char *prefix, + const void *bufptr, size_t size); + #endif diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h index 9d197daca3..3ae2357fda 100644 --- a/include/qemu/error-report.h +++ b/include/qemu/error-report.h @@ -30,23 +30,21 @@ void loc_set_none(void); void loc_set_cmdline(char **argv, int idx, int cnt); void loc_set_file(const char *fname, int lno); -int error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); -int error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); -int error_vprintf_unless_qmp(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); -int error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +int error_vprintf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); +int error_printf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); -void error_vreport(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); -void warn_vreport(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); -void info_vreport(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); +void error_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); +void warn_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); +void info_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); -void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); -void warn_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); -void info_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +void error_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); +void warn_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); +void info_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); bool error_report_once_cond(bool *printed, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); bool warn_report_once_cond(bool *printed, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); void error_init(const char *argv0); @@ -72,8 +70,6 @@ void error_init(const char *argv0); fmt, ##__VA_ARGS__); \ }) -const char *error_get_progname(void); - extern bool message_with_timestamp; extern bool error_with_guestname; extern const char *error_guest_name; diff --git a/include/qemu/event_notifier.h b/include/qemu/event_notifier.h index b79add035d..8a4ff308e1 100644 --- a/include/qemu/event_notifier.h +++ b/include/qemu/event_notifier.h @@ -38,6 +38,7 @@ int event_notifier_test_and_clear(EventNotifier *); #ifdef CONFIG_POSIX void event_notifier_init_fd(EventNotifier *, int fd); int event_notifier_get_fd(const EventNotifier *); +int event_notifier_get_wfd(const EventNotifier *); #else HANDLE event_notifier_get_handle(EventNotifier *); #endif diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 5e71b6d6f7..5bd986aa44 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -340,6 +340,18 @@ bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, int64_t max_dirty_count, int64_t *dirty_start, int64_t *dirty_count); +/* + * bdrv_dirty_bitmap_status: + * @hb: The HBitmap to operate on + * @start: The bit to start from + * @count: Number of bits to proceed + * @pnum: Out-parameter. How many bits has same value starting from @start + * + * Returns true if bitmap is dirty at @start, false otherwise. + */ +bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count, + int64_t *pnum); + /** * hbitmap_iter_next: * @hbi: HBitmapIter to operate on. diff --git a/include/qemu/help-texts.h b/include/qemu/help-texts.h new file mode 100644 index 0000000000..ba32cc8b1f --- /dev/null +++ b/include/qemu/help-texts.h @@ -0,0 +1,13 @@ +#ifndef QEMU_COMMON_H +#define QEMU_COMMON_H + +/* Copyright string for -version arguments, About dialogs, etc */ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2022 " \ + "Fabrice Bellard and the QEMU Project developers" + +/* Bug reporting information for --help arguments, About dialogs, etc */ +#define QEMU_HELP_BOTTOM \ + "See for how to report bugs.\n" \ + "More information on the QEMU project at ." + +#endif diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index ca979dc6cc..f19bd29105 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -88,7 +88,7 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) union { uint64_t ll; struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint32_t high, low; #else uint32_t low, high; diff --git a/include/qemu/int128.h b/include/qemu/int128.h index 2c4064256c..ef71f56e3f 100644 --- a/include/qemu/int128.h +++ b/include/qemu/int128.h @@ -83,6 +83,11 @@ static inline Int128 int128_rshift(Int128 a, int n) return a >> n; } +static inline Int128 int128_urshift(Int128 a, int n) +{ + return (__uint128_t)a >> n; +} + static inline Int128 int128_lshift(Int128 a, int n) { return a << n; @@ -205,7 +210,7 @@ typedef struct Int128 Int128; * a union with other integer types). */ struct Int128 { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN int64_t hi; uint64_t lo; #else @@ -299,6 +304,20 @@ static inline Int128 int128_rshift(Int128 a, int n) } } +static inline Int128 int128_urshift(Int128 a, int n) +{ + uint64_t h = a.hi; + if (!n) { + return a; + } + h = h >> (n & 63); + if (n >= 64) { + return int128_make64(h); + } else { + return int128_make128((a.lo >> n) | ((uint64_t)a.hi << (64 - n)), h); + } +} + static inline Int128 int128_lshift(Int128 a, int n) { uint64_t l = a.lo << (n & 63); @@ -412,5 +431,7 @@ static inline void bswap128s(Int128 *s) } #define UINT128_MAX int128_make128(~0LL, ~0LL) +#define INT128_MAX int128_make128(UINT64_MAX, INT64_MAX) +#define INT128_MIN int128_make128(0, INT64_MIN) #endif /* INT128_H */ diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index 8249edd764..c938fb0793 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -29,6 +29,7 @@ #define IOVA_OK (0) #define IOVA_ERR_INVALID (-1) /* Invalid parameters */ #define IOVA_ERR_OVERLAP (-2) /* IOVA range overlapped */ +#define IOVA_ERR_NOMEM (-3) /* Cannot allocate */ typedef struct IOVATree IOVATree; typedef struct DMAMap { @@ -82,7 +83,7 @@ int iova_tree_remove(IOVATree *tree, const DMAMap *map); * @tree: the iova tree to search from * @map: the mapping to search * - * Search for a mapping in the iova tree that overlaps with the + * Search for a mapping in the iova tree that iova overlaps with the * mapping range specified. Only the first found mapping will be * returned. * @@ -94,6 +95,24 @@ int iova_tree_remove(IOVATree *tree, const DMAMap *map); */ const DMAMap *iova_tree_find(const IOVATree *tree, const DMAMap *map); +/** + * iova_tree_find_iova: + * + * @tree: the iova tree to search from + * @map: the mapping to search + * + * Search for a mapping in the iova tree that translated_addr overlaps with the + * mapping range specified. Only the first found mapping will be + * returned. + * + * Return: DMAMap pointer if found, or NULL if not found. Note that + * the returned DMAMap pointer is maintained internally. User should + * only read the content but never modify or free the content. Also, + * user is responsible to make sure the pointer is valid (say, no + * concurrent deletion in progress). + */ +const DMAMap *iova_tree_find_iova(const IOVATree *tree, const DMAMap *map); + /** * iova_tree_find_address: * @@ -119,6 +138,23 @@ const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova); */ void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator); +/** + * iova_tree_alloc_map: + * + * @tree: the iova tree to allocate from + * @map: the new map (as translated addr & size) to allocate in the iova region + * @iova_begin: the minimum address of the allocation + * @iova_end: the maximum addressable direction of the allocation + * + * Allocates a new region of a given size, between iova_min and iova_max. + * + * Return: Same as iova_tree_insert, but cannot overlap and can return error if + * iova tree is out of free contiguous range. The caller gets the assigned iova + * in map->iova. + */ +int iova_tree_alloc_map(IOVATree *tree, DMAMap *map, hwaddr iova_begin, + hwaddr iova_end); + /** * iova_tree_destroy: * diff --git a/include/qemu/job.h b/include/qemu/job.h index 6e67b6977f..c105b31076 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -169,6 +169,12 @@ typedef struct Job { * Callbacks and other information about a Job driver. */ struct JobDriver { + + /* + * These fields are initialized when this object is created, + * and are never changed afterwards + */ + /** Derived Job struct size */ size_t instance_size; @@ -184,9 +190,18 @@ struct JobDriver { * aborted. If it returns zero, the job moves into the WAITING state. If it * is the last job to complete in its transaction, all jobs in the * transaction move from WAITING to PENDING. + * + * This callback must be run in the job's context. */ int coroutine_fn (*run)(Job *job, Error **errp); + /* + * Functions run without regard to the BQL that may run in any + * arbitrary thread. These functions do not need to be thread-safe + * because the caller ensures that they are invoked from one + * thread at time. + */ + /** * If the callback is not NULL, it will be invoked when the job transitions * into the paused state. Paused jobs must not perform any asynchronous @@ -201,6 +216,13 @@ struct JobDriver { */ void coroutine_fn (*resume)(Job *job); + /* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + /** * Called when the job is resumed by the user (i.e. user_paused becomes * false). .user_resume is called before .resume. diff --git a/include/qemu/keyval.h b/include/qemu/keyval.h new file mode 100644 index 0000000000..2d263286d7 --- /dev/null +++ b/include/qemu/keyval.h @@ -0,0 +1,14 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef KEYVAL_H_ +#define KEYVAL_H_ + +QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key, + bool *p_help, Error **errp); +QDict *keyval_parse(const char *params, const char *implied_key, + bool *help, Error **errp); +void keyval_merge(QDict *old, const QDict *new, Error **errp); + +#endif /* KEYVAL_H_ */ diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h index 2f0a5b080e..d47c9cd446 100644 --- a/include/qemu/log-for-trace.h +++ b/include/qemu/log-for-trace.h @@ -30,6 +30,6 @@ static inline bool qemu_loglevel_mask(int mask) } /* main logging function */ -int GCC_FMT_ATTR(1, 2) qemu_log(const char *fmt, ...); +void G_GNUC_PRINTF(1, 2) qemu_log(const char *fmt, ...); #endif diff --git a/include/qemu/log.h b/include/qemu/log.h index 9b80660207..c5643d8dd5 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -3,46 +3,16 @@ /* A small part of this API is split into its own header */ #include "qemu/log-for-trace.h" -#include "qemu/rcu.h" - -typedef struct QemuLogFile { - struct rcu_head rcu; - FILE *fd; -} QemuLogFile; - -/* Private global variable, don't use */ -extern QemuLogFile *qemu_logfile; - /* * The new API: - * */ -/* Log settings checking macros: */ +/* Returns true if qemu_log() will really write somewhere. */ +bool qemu_log_enabled(void); -/* Returns true if qemu_log() will really write somewhere - */ -static inline bool qemu_log_enabled(void) -{ - return qemu_logfile != NULL; -} - -/* Returns true if qemu_log() will write somewhere else than stderr - */ -static inline bool qemu_log_separate(void) -{ - QemuLogFile *logfile; - bool res = false; - - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile && logfile->fd != stderr) { - res = true; - } - rcu_read_unlock(); - return res; -} +/* Returns true if qemu_log() will write somewhere other than stderr. */ +bool qemu_log_separate(void); #define CPU_LOG_TB_OUT_ASM (1 << 0) #define CPU_LOG_TB_IN_ASM (1 << 1) @@ -64,51 +34,15 @@ static inline bool qemu_log_separate(void) #define CPU_LOG_PLUGIN (1 << 18) /* LOG_STRACE is used for user-mode strace logging. */ #define LOG_STRACE (1 << 19) +#define LOG_PER_THREAD (1 << 20) -/* Lock output for a series of related logs. Since this is not needed - * for a single qemu_log / qemu_log_mask / qemu_log_mask_and_addr, we - * assume that qemu_loglevel_mask has already been tested, and that - * qemu_loglevel is never set when qemu_logfile is unset. - */ +/* Lock/unlock output. */ -static inline FILE *qemu_log_lock(void) -{ - QemuLogFile *logfile; - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - qemu_flockfile(logfile->fd); - return logfile->fd; - } else { - return NULL; - } -} - -static inline void qemu_log_unlock(FILE *fd) -{ - if (fd) { - qemu_funlockfile(fd); - } - rcu_read_unlock(); -} +FILE *qemu_log_trylock(void) G_GNUC_WARN_UNUSED_RESULT; +void qemu_log_unlock(FILE *fd); /* Logging functions: */ -/* vfprintf-like logging function - */ -static inline void GCC_FMT_ATTR(1, 0) -qemu_log_vprintf(const char *fmt, va_list va) -{ - QemuLogFile *logfile; - - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - vfprintf(logfile->fd, fmt, va); - } - rcu_read_unlock(); -} - /* log only if a bit is set on the current loglevel mask: * @mask: bit to check in the mask * @fmt: printf-style format string @@ -147,9 +81,9 @@ typedef struct QEMULogItem { extern const QEMULogItem qemu_log_items[]; -void qemu_set_log(int log_flags); -void qemu_log_needs_buffers(void); -void qemu_set_log_filename(const char *filename, Error **errp); +bool qemu_set_log(int log_flags, Error **errp); +bool qemu_set_log_filename(const char *filename, Error **errp); +bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp); void qemu_set_dfilter_ranges(const char *ranges, Error **errp); bool qemu_log_in_addr_range(uint64_t addr); int qemu_str_to_log_mask(const char *str); @@ -159,9 +93,4 @@ int qemu_str_to_log_mask(const char *str); */ void qemu_print_log_usage(FILE *f); -/* fflush() the log file */ -void qemu_log_flush(void); -/* Close the log file */ -void qemu_log_close(void); - #endif diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 8dbc6fcb89..d3750c8e76 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -242,9 +242,65 @@ AioContext *iohandler_get_aio_context(void); * must always be taken outside other locks. This function helps * functions take different paths depending on whether the current * thread is running within the main loop mutex. + * + * This function should never be used in the block layer, because + * unit tests, block layer tools and qemu-storage-daemon do not + * have a BQL. + * Please instead refer to qemu_in_main_thread(). */ bool qemu_mutex_iothread_locked(void); +/** + * qemu_in_main_thread: return whether it's possible to safely access + * the global state of the block layer. + * + * Global state of the block layer is not accessible from I/O threads + * or worker threads; only from threads that "own" the default + * AioContext that qemu_get_aio_context() returns. For tests, block + * layer tools and qemu-storage-daemon there is a designated thread that + * runs the event loop for qemu_get_aio_context(), and that is the + * main thread. + * + * For emulators, however, any thread that holds the BQL can act + * as the block layer main thread; this will be any of the actual + * main thread, the vCPU threads or the RCU thread. + * + * For clarity, do not use this function outside the block layer. + */ +bool qemu_in_main_thread(void); + +/* Mark and check that the function is part of the global state API. */ +#ifdef CONFIG_COCOA +/* + * When using the Cocoa UI, addRemovableDevicesMenuItems() is called from + * a thread different from the QEMU main thread and can not take the BQL, + * triggering this assertions in the block layer (commit 0439c5a462). + * As the Cocoa fix is not trivial, disable this assertion for the v7.0.0 + * release (when using Cocoa); we will restore it immediately after the + * release. + * This issue is tracked as https://gitlab.com/qemu-project/qemu/-/issues/926 + */ +#define GLOBAL_STATE_CODE() +#else +#define GLOBAL_STATE_CODE() \ + do { \ + /* FIXME: Re-enable after 7.0 release */ \ + /* assert(qemu_in_main_thread()); */ \ + } while (0) +#endif /* CONFIG_COCOA */ + +/* Mark and check that the function is part of the I/O API. */ +#define IO_CODE() \ + do { \ + /* nop */ \ + } while (0) + +/* Mark and check that the function is part of the "I/O OR GS" API. */ +#define IO_OR_GS_CODE() \ + do { \ + /* nop */ \ + } while (0) + /** * qemu_mutex_lock_iothread: Lock the main loop mutex. * diff --git a/include/qemu/memalign.h b/include/qemu/memalign.h new file mode 100644 index 0000000000..fa299f3bf6 --- /dev/null +++ b/include/qemu/memalign.h @@ -0,0 +1,61 @@ +/* + * Allocation and free functions for aligned memory + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_MEMALIGN_H +#define QEMU_MEMALIGN_H + +/** + * qemu_try_memalign: Allocate aligned memory + * @alignment: required alignment, in bytes + * @size: size of allocation, in bytes + * + * Allocate memory on an aligned boundary (i.e. the returned + * address will be an exact multiple of @alignment). + * @alignment must be a power of 2, or the function will assert(). + * On success, returns allocated memory; on failure, returns NULL. + * + * The memory allocated through this function must be freed via + * qemu_vfree() (and not via free()). + */ +void *qemu_try_memalign(size_t alignment, size_t size); +/** + * qemu_memalign: Allocate aligned memory, without failing + * @alignment: required alignment, in bytes + * @size: size of allocation, in bytes + * + * Allocate memory in the same way as qemu_try_memalign(), but + * abort() with an error message if the memory allocation fails. + * + * The memory allocated through this function must be freed via + * qemu_vfree() (and not via free()). + */ +void *qemu_memalign(size_t alignment, size_t size); +/** + * qemu_vfree: Free memory allocated through qemu_memalign + * @ptr: memory to free + * + * This function must be used to free memory allocated via qemu_memalign() + * or qemu_try_memalign(). (Using the wrong free function will cause + * subtle bugs on Windows hosts.) + */ +void qemu_vfree(void *ptr); +/* + * It's an analog of GLIB's g_autoptr_cleanup_generic_gfree(), used to define + * g_autofree macro. + */ +static inline void qemu_cleanup_generic_vfree(void *p) +{ + void **pp = (void **)p; + qemu_vfree(*pp); +} + +/* + * Analog of g_autofree, but qemu_vfree is called on cleanup instead of g_free. + */ +#define QEMU_AUTO_VFREE __attribute__((cleanup(qemu_cleanup_generic_vfree))) + +#endif diff --git a/include/qemu/option.h b/include/qemu/option.h index bbd86e1c4e..b349828782 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -144,12 +144,6 @@ void qemu_opts_print_help(QemuOptsList *list, bool print_caption); void qemu_opts_free(QemuOptsList *list); QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list); -QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key, - bool *p_help, Error **errp); -QDict *keyval_parse(const char *params, const char *implied_key, - bool *help, Error **errp); -void keyval_merge(QDict *old, const QDict *new, Error **errp); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuOpts, qemu_opts_del) #endif diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 7bcce3bceb..1c1e7eca98 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -34,6 +34,18 @@ #include "exec/poison.h" #endif +/* + * HOST_WORDS_BIGENDIAN was replaced with HOST_BIG_ENDIAN. Prevent it from + * creeping back in. + */ +#pragma GCC poison HOST_WORDS_BIGENDIAN + +/* + * TARGET_WORDS_BIGENDIAN was replaced with TARGET_BIG_ENDIAN. Prevent it from + * creeping back in. + */ +#pragma GCC poison TARGET_WORDS_BIGENDIAN + #include "qemu/compiler.h" /* Older versions of C++ don't get definitions of various macros from @@ -157,6 +169,23 @@ extern "C" { #define assert(x) g_assert(x) #endif +/** + * qemu_build_not_reached() + * + * The compiler, during optimization, is expected to prove that a call + * to this function cannot be reached and remove it. If the compiler + * supports QEMU_ERROR, this will be reported at compile time; otherwise + * this will be reported at link time due to the missing symbol. + */ +extern G_NORETURN +void QEMU_ERROR("code path is reachable") + qemu_build_not_reached_always(void); +#if defined(__OPTIMIZE__) && !defined(__NO_INLINE__) +#define qemu_build_not_reached() qemu_build_not_reached_always() +#else +#define qemu_build_not_reached() g_assert_not_reached() +#endif + /* * According to waitpid man page: * WCOREDUMP @@ -214,6 +243,8 @@ extern "C" { #define ESHUTDOWN 4099 #endif +#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) + /* time_t may be either 32 or 64 bits depending on the host OS, and * can be either signed or unsigned, so we can't just hardcode a * specific maximum value. This is not a C preprocessor constant, @@ -244,15 +275,6 @@ extern "C" { #define TIME_MAX TYPE_MAXIMUM(time_t) #endif -/* HOST_LONG_BITS is the size of a native pointer in bits. */ -#if UINTPTR_MAX == UINT32_MAX -# define HOST_LONG_BITS 32 -#elif UINTPTR_MAX == UINT64_MAX -# define HOST_LONG_BITS 64 -#else -# error Unknown pointer size -#endif - /* Mac OSX has a bug that incorrectly defines SIZE_MAX with * the wrong type. Our replacement isn't usable in preprocessor * expressions, but it is sufficient for our needs. */ @@ -379,28 +401,10 @@ extern "C" { #endif int qemu_daemon(int nochdir, int noclose); -void *qemu_try_memalign(size_t alignment, size_t size); -void *qemu_memalign(size_t alignment, size_t size); void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, bool noreserve); -void qemu_vfree(void *ptr); void qemu_anon_ram_free(void *ptr, size_t size); -/* - * It's an analog of GLIB's g_autoptr_cleanup_generic_gfree(), used to define - * g_autofree macro. - */ -static inline void qemu_cleanup_generic_vfree(void *p) -{ - void **pp = (void **)p; - qemu_vfree(*pp); -} - -/* - * Analog of g_autofree, but qemu_vfree is called on cleanup instead of g_free. - */ -#define QEMU_AUTO_VFREE __attribute__((cleanup(qemu_cleanup_generic_vfree))) - #ifdef _WIN32 #define HAVE_CHARDEV_SERIAL 1 #elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ @@ -418,6 +422,14 @@ static inline void qemu_cleanup_generic_vfree(void *p) #define SIGIO SIGPOLL #endif +#ifdef HAVE_MADVISE_WITHOUT_PROTOTYPE +/* + * See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for discussion + * about Solaris missing the madvise() prototype. + */ +extern int madvise(char *, size_t, int); +#endif + #if defined(CONFIG_LINUX) #ifndef BUS_MCEERR_AR #define BUS_MCEERR_AR 4 @@ -438,9 +450,9 @@ static inline void qemu_cleanup_generic_vfree(void *p) /* Use 1 MiB (segment size) alignment so gmap can be used by KVM. */ # define QEMU_VMALLOC_ALIGN (256 * 4096) #elif defined(__linux__) && defined(__sparc__) -# define QEMU_VMALLOC_ALIGN MAX(qemu_real_host_page_size, SHMLBA) +# define QEMU_VMALLOC_ALIGN MAX(qemu_real_host_page_size(), SHMLBA) #else -# define QEMU_VMALLOC_ALIGN qemu_real_host_page_size +# define QEMU_VMALLOC_ALIGN qemu_real_host_page_size() #endif #ifdef CONFIG_POSIX @@ -532,21 +544,18 @@ static inline void qemu_timersub(const struct timeval *val1, #define qemu_timersub timersub #endif +ssize_t qemu_write_full(int fd, const void *buf, size_t count) + G_GNUC_WARN_UNUSED_RESULT; + void qemu_set_cloexec(int fd); -void fips_set_state(bool requested); -bool fips_get_state(void); - -/* Return a dynamically allocated pathname denoting a file or directory that is - * appropriate for storing local state. - * - * @relative_pathname need not start with a directory separator; one will be - * added automatically. +/* Return a dynamically allocated directory path that is appropriate for storing + * local state. * * The caller is responsible for releasing the value returned with g_free() * after use. */ -char *qemu_get_local_state_pathname(const char *relative_pathname); +char *qemu_get_local_state_dir(void); /* Find program directory, and save it for later usage with * qemu_get_exec_dir(). @@ -597,8 +606,15 @@ pid_t qemu_fork(Error **errp); /* Using intptr_t ensures that qemu_*_page_mask is sign-extended even * when intptr_t is 32-bit and we are aligning a long long. */ -extern uintptr_t qemu_real_host_page_size; -extern intptr_t qemu_real_host_page_mask; +static inline uintptr_t qemu_real_host_page_size(void) +{ + return getpagesize(); +} + +static inline intptr_t qemu_real_host_page_mask(void) +{ + return -(intptr_t)qemu_real_host_page_size(); +} /* * After using getopt or getopt_long, if you need to parse another set @@ -615,15 +631,20 @@ static inline void qemu_reset_optind(void) #endif } +int qemu_fdatasync(int fd); + /** - * qemu_get_host_name: - * @errp: Error object + * Sync changes made to the memory mapped file back to the backing + * storage. For POSIX compliant systems this will fallback + * to regular msync call. Otherwise it will trigger whole file sync + * (including the metadata case there is no support to skip that otherwise) * - * Operating system agnostic way of querying host name. - * - * Returns allocated hostname (caller should free), NULL on failure. + * @addr - start of the memory area to be synced + * @length - length of the are to be synced + * @fd - file descriptor for the file to be synced + * (mandatory only for POSIX non-compliant systems) */ -char *qemu_get_host_name(Error **errp); +int qemu_msync(void *addr, size_t length, int fd); /** * qemu_get_host_physmem: @@ -642,19 +663,15 @@ size_t qemu_get_host_physmem(void); * for the current thread. */ #if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 static inline void qemu_thread_jit_execute(void) { - if (__builtin_available(macOS 11.0, *)) { - pthread_jit_write_protect_np(true); - } + pthread_jit_write_protect_np(true); } static inline void qemu_thread_jit_write(void) { - if (__builtin_available(macOS 11.0, *)) { - pthread_jit_write_protect_np(false); - } + pthread_jit_write_protect_np(false); } #else static inline void qemu_thread_jit_write(void) {} @@ -673,19 +690,6 @@ static inline int platform_does_not_support_system(const char *command) } #endif /* !HAVE_SYSTEM_FUNCTION */ -/** - * Duplicate directory entry @dent. - * - * It is highly recommended to use this function instead of open coding - * duplication of @c dirent objects, because the actual @c struct @c dirent - * size may be bigger or shorter than @c sizeof(struct dirent) and correct - * handling is platform specific (see gitlab issue #841). - * - * @dent - original directory entry to be duplicated - * @returns duplicated directory entry which should be freed with g_free() - */ -struct dirent *qemu_dirent_dup(struct dirent *dent); - #ifdef __cplusplus } #endif diff --git a/include/qemu/qemu-print.h b/include/qemu/qemu-print.h index 40b596262f..1b70920648 100644 --- a/include/qemu/qemu-print.h +++ b/include/qemu/qemu-print.h @@ -13,11 +13,11 @@ #ifndef QEMU_PRINT_H #define QEMU_PRINT_H -int qemu_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); -int qemu_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +int qemu_vprintf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); +int qemu_printf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); int qemu_vfprintf(FILE *stream, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); -int qemu_fprintf(FILE *stream, const char *fmt, ...) GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 0); +int qemu_fprintf(FILE *stream, const char *fmt, ...) G_GNUC_PRINTF(2, 3); #endif diff --git a/include/qemu/qemu-progress.h b/include/qemu/qemu-progress.h new file mode 100644 index 0000000000..137e1c316f --- /dev/null +++ b/include/qemu/qemu-progress.h @@ -0,0 +1,8 @@ +#ifndef QEMU_PROGRESS_H +#define QEMU_PROGRESS_H + +void qemu_progress_init(int enabled, float min_skip); +void qemu_progress_end(void); +void qemu_progress_print(float delta, int max); + +#endif /* QEMU_PROGRESS_H */ diff --git a/include/qemu/range.h b/include/qemu/range.h index f62b363e0d..7e2b1cc447 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -114,8 +114,8 @@ static inline uint64_t range_upb(Range *range) * @size may be 0. If the range would overflow, returns -ERANGE, otherwise * 0. */ -static inline int QEMU_WARN_UNUSED_RESULT range_init(Range *range, uint64_t lob, - uint64_t size) +G_GNUC_WARN_UNUSED_RESULT +static inline int range_init(Range *range, uint64_t lob, uint64_t size) { if (lob + size < lob) { return -ERANGE; diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h index e69efbd47f..b063c6fde8 100644 --- a/include/qemu/rcu.h +++ b/include/qemu/rcu.h @@ -29,6 +29,7 @@ #include "qemu/atomic.h" #include "qemu/notify.h" #include "qemu/sys_membarrier.h" +#include "qemu/coroutine-tls.h" #ifdef __cplusplus extern "C" { @@ -76,11 +77,11 @@ struct rcu_reader_data { NotifierList force_rcu; }; -extern __thread struct rcu_reader_data rcu_reader; +QEMU_DECLARE_CO_TLS(struct rcu_reader_data, rcu_reader) static inline void rcu_read_lock(void) { - struct rcu_reader_data *p_rcu_reader = &rcu_reader; + struct rcu_reader_data *p_rcu_reader = get_ptr_rcu_reader(); unsigned ctr; if (p_rcu_reader->depth++ > 0) { @@ -96,7 +97,7 @@ static inline void rcu_read_lock(void) static inline void rcu_read_unlock(void) { - struct rcu_reader_data *p_rcu_reader = &rcu_reader; + struct rcu_reader_data *p_rcu_reader = get_ptr_rcu_reader(); assert(p_rcu_reader->depth != 0); if (--p_rcu_reader->depth > 0) { diff --git a/include/qemu/readline.h b/include/qemu/readline.h index e81258322b..622aa4564f 100644 --- a/include/qemu/readline.h +++ b/include/qemu/readline.h @@ -5,7 +5,7 @@ #define READLINE_MAX_CMDS 64 #define READLINE_MAX_COMPLETIONS 256 -typedef void GCC_FMT_ATTR(2, 3) ReadLinePrintfFunc(void *opaque, +typedef void G_GNUC_PRINTF(2, 3) ReadLinePrintfFunc(void *opaque, const char *fmt, ...); typedef void ReadLineFlushFunc(void *opaque); typedef void ReadLineFunc(void *opaque, const char *str, diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 0c34bf2398..038faa157f 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -17,9 +17,9 @@ int qemu_socket(int domain, int type, int protocol); int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); int socket_set_cork(int fd, int v); int socket_set_nodelay(int fd); -void qemu_set_block(int fd); -int qemu_try_set_nonblock(int fd); -void qemu_set_nonblock(int fd); +void qemu_socket_set_block(int fd); +int qemu_socket_try_set_nonblock(int fd); +void qemu_socket_set_nonblock(int fd); int socket_set_fast_reuse(int fd); #ifdef WIN32 diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index b792e6ef37..5f2f3d1386 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -27,14 +27,9 @@ struct QemuCond { }; struct QemuSemaphore { -#ifndef CONFIG_SEM_TIMEDWAIT - pthread_mutex_t lock; - pthread_cond_t cond; + QemuMutex mutex; + QemuCond cond; unsigned int count; -#else - sem_t sem; -#endif - bool initialized; }; struct QemuEvent { diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 460568d67d..af19f2b3fc 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -188,7 +188,7 @@ void qemu_thread_create(QemuThread *thread, const char *name, void *qemu_thread_join(QemuThread *thread); void qemu_thread_get_self(QemuThread *thread); bool qemu_thread_is_self(QemuThread *thread); -void qemu_thread_exit(void *retval) QEMU_NORETURN; +G_NORETURN void qemu_thread_exit(void *retval); void qemu_thread_naming(bool enable); struct Notifier; diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 88ef114689..ee071e07d1 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -520,7 +520,7 @@ static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, int scale, int attributes, QEMUTimerCB *cb, void *opaque) { - QEMUTimer *ts = g_malloc0(sizeof(QEMUTimer)); + QEMUTimer *ts = g_new0(QEMUTimer, 1); timer_init_full(ts, timer_list_group, type, scale, attributes, cb, opaque); return ts; } diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index ee60eb3de4..42f4ceb701 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -26,6 +26,7 @@ typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; typedef struct Aml Aml; typedef struct AnnounceTimer AnnounceTimer; +typedef struct ArchCPU ArchCPU; typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; typedef struct BlockBackend BlockBackend; @@ -39,6 +40,7 @@ typedef struct CompatProperty CompatProperty; typedef struct CoMutex CoMutex; typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; typedef struct CPUAddressSpace CPUAddressSpace; +typedef struct CPUArchState CPUArchState; typedef struct CPUState CPUState; typedef struct DeviceListener DeviceListener; typedef struct DeviceState DeviceState; @@ -86,6 +88,8 @@ typedef struct PCIDevice PCIDevice; typedef struct PCIEAERErr PCIEAERErr; typedef struct PCIEAERLog PCIEAERLog; typedef struct PCIEAERMsg PCIEAERMsg; +typedef struct PCIESriovPF PCIESriovPF; +typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; typedef struct PCIExpressDevice PCIExpressDevice; diff --git a/include/qemu/win_dump_defs.h b/include/qemu/win_dump_defs.h index 145096e8ee..73a44e2408 100644 --- a/include/qemu/win_dump_defs.h +++ b/include/qemu/win_dump_defs.h @@ -11,11 +11,22 @@ #ifndef QEMU_WIN_DUMP_DEFS_H #define QEMU_WIN_DUMP_DEFS_H +typedef struct WinDumpPhyMemRun32 { + uint32_t BasePage; + uint32_t PageCount; +} QEMU_PACKED WinDumpPhyMemRun32; + typedef struct WinDumpPhyMemRun64 { uint64_t BasePage; uint64_t PageCount; } QEMU_PACKED WinDumpPhyMemRun64; +typedef struct WinDumpPhyMemDesc32 { + uint32_t NumberOfRuns; + uint32_t NumberOfPages; + WinDumpPhyMemRun32 Run[86]; +} QEMU_PACKED WinDumpPhyMemDesc32; + typedef struct WinDumpPhyMemDesc64 { uint32_t NumberOfRuns; uint32_t unused; @@ -33,6 +44,39 @@ typedef struct WinDumpExceptionRecord { uint64_t ExceptionInformation[15]; } QEMU_PACKED WinDumpExceptionRecord; +typedef struct WinDumpHeader32 { + char Signature[4]; + char ValidDump[4]; + uint32_t MajorVersion; + uint32_t MinorVersion; + uint32_t DirectoryTableBase; + uint32_t PfnDatabase; + uint32_t PsLoadedModuleList; + uint32_t PsActiveProcessHead; + uint32_t MachineImageType; + uint32_t NumberProcessors; + union { + struct { + uint32_t BugcheckCode; + uint32_t BugcheckParameter1; + uint32_t BugcheckParameter2; + uint32_t BugcheckParameter3; + uint32_t BugcheckParameter4; + }; + uint8_t BugcheckData[20]; + }; + uint8_t VersionUser[32]; + uint32_t reserved0; + uint32_t KdDebuggerDataBlock; + union { + WinDumpPhyMemDesc32 PhysicalMemoryBlock; + uint8_t PhysicalMemoryBlockBuffer[700]; + }; + uint8_t reserved1[3200]; + uint32_t RequiredDumpSpace; + uint8_t reserved2[92]; +} QEMU_PACKED WinDumpHeader32; + typedef struct WinDumpHeader64 { char Signature[4]; char ValidDump[4]; @@ -81,24 +125,48 @@ typedef struct WinDumpHeader64 { uint8_t reserved[4018]; } QEMU_PACKED WinDumpHeader64; +typedef union WinDumpHeader { + struct { + char Signature[4]; + char ValidDump[4]; + }; + WinDumpHeader32 x32; + WinDumpHeader64 x64; +} WinDumpHeader; + #define KDBG_OWNER_TAG_OFFSET64 0x10 #define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0 #define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88 #define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218 #define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338 +#define KDBG_OWNER_TAG_OFFSET KDBG_OWNER_TAG_OFFSET64 +#define KDBG_MM_PFN_DATABASE_OFFSET KDBG_MM_PFN_DATABASE_OFFSET64 +#define KDBG_KI_BUGCHECK_DATA_OFFSET KDBG_KI_BUGCHECK_DATA_OFFSET64 +#define KDBG_KI_PROCESSOR_BLOCK_OFFSET KDBG_KI_PROCESSOR_BLOCK_OFFSET64 +#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 + #define VMCOREINFO_ELF_NOTE_HDR_SIZE 24 +#define VMCOREINFO_WIN_DUMP_NOTE_SIZE64 (sizeof(WinDumpHeader64) + \ + VMCOREINFO_ELF_NOTE_HDR_SIZE) +#define VMCOREINFO_WIN_DUMP_NOTE_SIZE32 (sizeof(WinDumpHeader32) + \ + VMCOREINFO_ELF_NOTE_HDR_SIZE) #define WIN_CTX_X64 0x00100000L +#define WIN_CTX_X86 0x00010000L #define WIN_CTX_CTL 0x00000001L #define WIN_CTX_INT 0x00000002L #define WIN_CTX_SEG 0x00000004L #define WIN_CTX_FP 0x00000008L #define WIN_CTX_DBG 0x00000010L +#define WIN_CTX_EXT 0x00000020L -#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP) -#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG) +#define WIN_CTX64_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP) +#define WIN_CTX64_ALL (WIN_CTX64_FULL | WIN_CTX_SEG | WIN_CTX_DBG) + +#define WIN_CTX32_FULL (WIN_CTX_X86 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_SEG) +#define WIN_CTX32_ALL (WIN_CTX32_FULL | WIN_CTX_FP | WIN_CTX_DBG | WIN_CTX_EXT) #define LIVE_SYSTEM_DUMP 0x00000161 @@ -107,7 +175,41 @@ typedef struct WinM128A { int64_t high; } QEMU_ALIGNED(16) WinM128A; -typedef struct WinContext { +typedef struct WinContext32 { + uint32_t ContextFlags; + + uint32_t Dr0; + uint32_t Dr1; + uint32_t Dr2; + uint32_t Dr3; + uint32_t Dr6; + uint32_t Dr7; + + uint8_t FloatSave[112]; + + uint32_t SegGs; + uint32_t SegFs; + uint32_t SegEs; + uint32_t SegDs; + + uint32_t Edi; + uint32_t Esi; + uint32_t Ebx; + uint32_t Edx; + uint32_t Ecx; + uint32_t Eax; + + uint32_t Ebp; + uint32_t Eip; + uint32_t SegCs; + uint32_t EFlags; + uint32_t Esp; + uint32_t SegSs; + + uint8_t ExtendedRegisters[512]; +} QEMU_ALIGNED(16) WinContext32; + +typedef struct WinContext64 { uint64_t PHome[6]; uint32_t ContextFlags; @@ -174,6 +276,11 @@ typedef struct WinContext { uint64_t LastBranchFromRip; uint64_t LastExceptionToRip; uint64_t LastExceptionFromRip; -} QEMU_ALIGNED(16) WinContext; +} QEMU_ALIGNED(16) WinContext64; + +typedef union WinContext { + WinContext32 x32; + WinContext64 x64; +} WinContext; #endif /* QEMU_WIN_DUMP_DEFS_H */ diff --git a/include/qemu/xattr.h b/include/qemu/xattr.h index a83fe8e749..f1d0f7be74 100644 --- a/include/qemu/xattr.h +++ b/include/qemu/xattr.h @@ -22,7 +22,9 @@ #ifdef CONFIG_LIBATTR # include #else -# define ENOATTR ENODATA +# if !defined(ENOATTR) +# define ENOATTR ENODATA +# endif # include #endif diff --git a/include/qom/object.h b/include/qom/object.h index fae096f51c..5f3d5b5bf5 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -616,7 +616,7 @@ Object *object_new_with_props(const char *typename, Object *parent, const char *id, Error **errp, - ...) QEMU_SENTINEL; + ...) G_GNUC_NULL_TERMINATED; /** * object_new_with_propv: @@ -676,7 +676,7 @@ void object_apply_compat_props(Object *obj); * * Returns: %true on success, %false on error. */ -bool object_set_props(Object *obj, Error **errp, ...) QEMU_SENTINEL; +bool object_set_props(Object *obj, Error **errp, ...) G_GNUC_NULL_TERMINATED; /** * object_set_propv: @@ -728,7 +728,7 @@ void object_initialize(void *obj, size_t size, const char *typename); bool object_initialize_child_with_props(Object *parentobj, const char *propname, void *childobj, size_t size, const char *type, - Error **errp, ...) QEMU_SENTINEL; + Error **errp, ...) G_GNUC_NULL_TERMINATED; /** * object_initialize_child_with_propsv: diff --git a/include/standard-headers/linux/pvpanic.h b/include/standard-headers/linux/pvpanic.h new file mode 100644 index 0000000000..54b7485390 --- /dev/null +++ b/include/standard-headers/linux/pvpanic.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#ifndef __PVPANIC_H__ +#define __PVPANIC_H__ + +#define PVPANIC_PANICKED (1 << 0) +#define PVPANIC_CRASH_LOADED (1 << 1) + +#endif /* __PVPANIC_H__ */ diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h index 032f6979d7..a0572ea87a 100644 --- a/include/sysemu/accel-ops.h +++ b/include/sysemu/accel-ops.h @@ -28,13 +28,17 @@ struct AccelOpsClass { /* initialization function called when accel is chosen */ void (*ops_init)(AccelOpsClass *ops); + bool (*cpus_are_resettable)(void); + void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY NON-NULL */ void (*kick_vcpu_thread)(CPUState *cpu); + bool (*cpu_thread_is_idle)(CPUState *cpu); void (*synchronize_post_reset)(CPUState *cpu); void (*synchronize_post_init)(CPUState *cpu); void (*synchronize_state)(CPUState *cpu); void (*synchronize_pre_loadvm)(CPUState *cpu); + void (*synchronize_pre_resume)(bool step_pending); void (*handle_interrupt)(CPUState *cpu, int mask); diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 70c579560a..79c2591425 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -28,4 +28,6 @@ enum { extern const uint32_t arch_type; +void qemu_init_arch_modules(void); + #endif diff --git a/include/sysemu/block-backend-common.h b/include/sysemu/block-backend-common.h new file mode 100644 index 0000000000..2391679c56 --- /dev/null +++ b/include/sysemu/block-backend-common.h @@ -0,0 +1,102 @@ +/* + * QEMU Block backends + * + * Copyright (C) 2014-2016 Red Hat, Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 + * or later. See the COPYING.LIB file in the top-level directory. + */ + +#ifndef BLOCK_BACKEND_COMMON_H +#define BLOCK_BACKEND_COMMON_H + +#include "qemu/iov.h" +#include "block/throttle-groups.h" + +/* + * TODO Have to include block/block.h for a bunch of block layer + * types. Unfortunately, this pulls in the whole BlockDriverState + * API, which we don't want used by many BlockBackend users. Some of + * the types belong here, and the rest should be split into a common + * header and one for the BlockDriverState API. + */ +#include "block/block.h" + +/* Callbacks for block device models */ +typedef struct BlockDevOps { + + /* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + + /* + * Runs when virtual media changed (monitor commands eject, change) + * Argument load is true on load and false on eject. + * Beware: doesn't run when a host device's physical media + * changes. Sure would be useful if it did. + * Device models with removable media must implement this callback. + */ + void (*change_media_cb)(void *opaque, bool load, Error **errp); + /* + * Runs when an eject request is issued from the monitor, the tray + * is closed, and the medium is locked. + * Device models that do not implement is_medium_locked will not need + * this callback. Device models that can lock the medium or tray might + * want to implement the callback and unlock the tray when "force" is + * true, even if they do not support eject requests. + */ + void (*eject_request_cb)(void *opaque, bool force); + + /* + * Is the virtual medium locked into the device? + * Device models implement this only when device has such a lock. + */ + bool (*is_medium_locked)(void *opaque); + + /* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + + /* + * Is the virtual tray open? + * Device models implement this only when the device has a tray. + */ + bool (*is_tray_open)(void *opaque); + + /* + * Runs when the size changed (e.g. monitor command block_resize) + */ + void (*resize_cb)(void *opaque); + /* + * Runs when the backend receives a drain request. + */ + void (*drained_begin)(void *opaque); + /* + * Runs when the backend's last drain request ends. + */ + void (*drained_end)(void *opaque); + /* + * Is the device still busy? + */ + bool (*drained_poll)(void *opaque); +} BlockDevOps; + +/* + * This struct is embedded in (the private) BlockBackend struct and contains + * fields that must be public. This is in particular for QLIST_ENTRY() and + * friends so that BlockBackends can be kept in lists outside block-backend.c + */ +typedef struct BlockBackendPublic { + ThrottleGroupMember throttle_group_member; +} BlockBackendPublic; + +#endif /* BLOCK_BACKEND_COMMON_H */ diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h new file mode 100644 index 0000000000..2e93a74679 --- /dev/null +++ b/include/sysemu/block-backend-global-state.h @@ -0,0 +1,116 @@ +/* + * QEMU Block backends + * + * Copyright (C) 2014-2016 Red Hat, Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 + * or later. See the COPYING.LIB file in the top-level directory. + */ + +#ifndef BLOCK_BACKEND_GS_H +#define BLOCK_BACKEND_GS_H + +#include "block-backend-common.h" + +/* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + +BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm); +BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, + uint64_t shared_perm, Error **errp); +BlockBackend *blk_new_open(const char *filename, const char *reference, + QDict *options, int flags, Error **errp); +int blk_get_refcnt(BlockBackend *blk); +void blk_ref(BlockBackend *blk); +void blk_unref(BlockBackend *blk); +void blk_remove_all_bs(void); +BlockBackend *blk_by_name(const char *name); +BlockBackend *blk_next(BlockBackend *blk); +BlockBackend *blk_all_next(BlockBackend *blk); +bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); +void monitor_remove_blk(BlockBackend *blk); + +BlockBackendPublic *blk_get_public(BlockBackend *blk); +BlockBackend *blk_by_public(BlockBackendPublic *public); + +void blk_remove_bs(BlockBackend *blk); +int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); +int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); +bool bdrv_has_blk(BlockDriverState *bs); +bool bdrv_is_root_node(BlockDriverState *bs); +int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, + Error **errp); +void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm); + +void blk_iostatus_enable(BlockBackend *blk); +BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk); +void blk_iostatus_disable(BlockBackend *blk); +void blk_iostatus_reset(BlockBackend *blk); +int blk_attach_dev(BlockBackend *blk, DeviceState *dev); +void blk_detach_dev(BlockBackend *blk, DeviceState *dev); +DeviceState *blk_get_attached_dev(BlockBackend *blk); +BlockBackend *blk_by_dev(void *dev); +BlockBackend *blk_by_qdev_id(const char *id, Error **errp); +void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque); + +void blk_activate(BlockBackend *blk, Error **errp); + +int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); +void blk_aio_cancel(BlockAIOCB *acb); +int blk_commit_all(void); +void blk_drain(BlockBackend *blk); +void blk_drain_all(void); +void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, + BlockdevOnError on_write_error); +bool blk_supports_write_perm(BlockBackend *blk); +bool blk_is_sg(BlockBackend *blk); +void blk_set_enable_write_cache(BlockBackend *blk, bool wce); +int blk_get_flags(BlockBackend *blk); +bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); +void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason); +void blk_op_block_all(BlockBackend *blk, Error *reason); +void blk_op_unblock_all(BlockBackend *blk, Error *reason); +int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, + Error **errp); +void blk_add_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *new_context, void *opaque), + void (*detach_aio_context)(void *opaque), void *opaque); +void blk_remove_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *, + void *), + void (*detach_aio_context)(void *), + void *opaque); +void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify); +void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify); +BlockBackendRootState *blk_get_root_state(BlockBackend *blk); +void blk_update_root_state(BlockBackend *blk); +bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk); +int blk_get_open_flags_from_root_state(BlockBackend *blk); + +int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, + int64_t pos, int size); +int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); + +void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); +void blk_io_limits_disable(BlockBackend *blk); +void blk_io_limits_enable(BlockBackend *blk, const char *group); +void blk_io_limits_update_group(BlockBackend *blk, const char *group); +void blk_set_force_allow_inactivate(BlockBackend *blk); + +void blk_register_buf(BlockBackend *blk, void *host, size_t size); +void blk_unregister_buf(BlockBackend *blk, void *host); + +const BdrvChild *blk_root(BlockBackend *blk); + +int blk_make_empty(BlockBackend *blk, Error **errp); + +#endif /* BLOCK_BACKEND_GS_H */ diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h new file mode 100644 index 0000000000..6517c39295 --- /dev/null +++ b/include/sysemu/block-backend-io.h @@ -0,0 +1,161 @@ +/* + * QEMU Block backends + * + * Copyright (C) 2014-2016 Red Hat, Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 + * or later. See the COPYING.LIB file in the top-level directory. + */ + +#ifndef BLOCK_BACKEND_IO_H +#define BLOCK_BACKEND_IO_H + +#include "block-backend-common.h" + +/* + * I/O API functions. These functions are thread-safe. + * + * See include/block/block-io.h for more information about + * the I/O API. + */ + +const char *blk_name(const BlockBackend *blk); + +BlockDriverState *blk_bs(BlockBackend *blk); + +void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow); +void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow); +void blk_set_disable_request_queuing(BlockBackend *blk, bool disable); +bool blk_iostatus_is_enabled(const BlockBackend *blk); + +char *blk_get_attached_dev_id(BlockBackend *blk); + +BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque); + +BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, + QEMUIOVector *qiov, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, + QEMUIOVector *qiov, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *blk_aio_flush(BlockBackend *blk, + BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, + BlockCompletionFunc *cb, void *opaque); +void blk_aio_cancel_async(BlockAIOCB *acb); +BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, + BlockCompletionFunc *cb, void *opaque); + +void blk_inc_in_flight(BlockBackend *blk); +void blk_dec_in_flight(BlockBackend *blk); +bool blk_is_inserted(BlockBackend *blk); +bool blk_is_available(BlockBackend *blk); +void blk_lock_medium(BlockBackend *blk, bool locked); +void blk_eject(BlockBackend *blk, bool eject_flag); +int64_t blk_getlength(BlockBackend *blk); +void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); +int64_t blk_nb_sectors(BlockBackend *blk); +void *blk_try_blockalign(BlockBackend *blk, size_t size); +void *blk_blockalign(BlockBackend *blk, size_t size); +bool blk_is_writable(BlockBackend *blk); +bool blk_enable_write_cache(BlockBackend *blk); +BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); +BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, + int error); +void blk_error_action(BlockBackend *blk, BlockErrorAction action, + bool is_read, int error); +void blk_iostatus_set_err(BlockBackend *blk, int error); +int blk_get_max_iov(BlockBackend *blk); +int blk_get_max_hw_iov(BlockBackend *blk); +void blk_set_guest_block_size(BlockBackend *blk, int align); + +void blk_io_plug(BlockBackend *blk); +void blk_io_unplug(BlockBackend *blk); +AioContext *blk_get_aio_context(BlockBackend *blk); +BlockAcctStats *blk_get_stats(BlockBackend *blk); +void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, + BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, + BlockCompletionFunc *cb, + void *opaque, int ret); + +uint32_t blk_get_request_alignment(BlockBackend *blk); +uint32_t blk_get_max_transfer(BlockBackend *blk); +uint64_t blk_get_max_hw_transfer(BlockBackend *blk); + +int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + BlockBackend *blk_out, int64_t off_out, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + + +/* + * "I/O or GS" API functions. These functions can run without + * the BQL, but only in one specific iothread/main loop. + * + * See include/block/block-io.h for more information about + * the "I/O or GS" API. + */ + +int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes); +int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, + BdrvRequestFlags flags); +int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, + int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); + +static inline int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, + int64_t bytes, void *buf, + BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); + + assert(bytes <= SIZE_MAX); + + return blk_co_preadv(blk, offset, bytes, &qiov, flags); +} + +static inline int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, + int64_t bytes, void *buf, + BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); + + assert(bytes <= SIZE_MAX); + + return blk_co_pwritev(blk, offset, bytes, &qiov, flags); +} + +int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, + int64_t bytes); + +int coroutine_fn blk_co_flush(BlockBackend *blk); +int blk_flush(BlockBackend *blk); + +int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); + +int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, + int64_t bytes); +int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); +int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, BdrvRequestFlags flags); +int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, BdrvRequestFlags flags); +int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); + +#endif /* BLOCK_BACKEND_IO_H */ diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index e5e1524f06..038be9fc40 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -13,272 +13,9 @@ #ifndef BLOCK_BACKEND_H #define BLOCK_BACKEND_H -#include "qemu/iov.h" -#include "block/throttle-groups.h" +#include "block-backend-global-state.h" +#include "block-backend-io.h" -/* - * TODO Have to include block/block.h for a bunch of block layer - * types. Unfortunately, this pulls in the whole BlockDriverState - * API, which we don't want used by many BlockBackend users. Some of - * the types belong here, and the rest should be split into a common - * header and one for the BlockDriverState API. - */ -#include "block/block.h" - -/* Callbacks for block device models */ -typedef struct BlockDevOps { - /* - * Runs when virtual media changed (monitor commands eject, change) - * Argument load is true on load and false on eject. - * Beware: doesn't run when a host device's physical media - * changes. Sure would be useful if it did. - * Device models with removable media must implement this callback. - */ - void (*change_media_cb)(void *opaque, bool load, Error **errp); - /* - * Runs when an eject request is issued from the monitor, the tray - * is closed, and the medium is locked. - * Device models that do not implement is_medium_locked will not need - * this callback. Device models that can lock the medium or tray might - * want to implement the callback and unlock the tray when "force" is - * true, even if they do not support eject requests. - */ - void (*eject_request_cb)(void *opaque, bool force); - /* - * Is the virtual tray open? - * Device models implement this only when the device has a tray. - */ - bool (*is_tray_open)(void *opaque); - /* - * Is the virtual medium locked into the device? - * Device models implement this only when device has such a lock. - */ - bool (*is_medium_locked)(void *opaque); - /* - * Runs when the size changed (e.g. monitor command block_resize) - */ - void (*resize_cb)(void *opaque); - /* - * Runs when the backend receives a drain request. - */ - void (*drained_begin)(void *opaque); - /* - * Runs when the backend's last drain request ends. - */ - void (*drained_end)(void *opaque); - /* - * Is the device still busy? - */ - bool (*drained_poll)(void *opaque); -} BlockDevOps; - -/* This struct is embedded in (the private) BlockBackend struct and contains - * fields that must be public. This is in particular for QLIST_ENTRY() and - * friends so that BlockBackends can be kept in lists outside block-backend.c - * */ -typedef struct BlockBackendPublic { - ThrottleGroupMember throttle_group_member; -} BlockBackendPublic; - -BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm); -BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, - uint64_t shared_perm, Error **errp); -BlockBackend *blk_new_open(const char *filename, const char *reference, - QDict *options, int flags, Error **errp); -int blk_get_refcnt(BlockBackend *blk); -void blk_ref(BlockBackend *blk); -void blk_unref(BlockBackend *blk); -void blk_remove_all_bs(void); -const char *blk_name(const BlockBackend *blk); -BlockBackend *blk_by_name(const char *name); -BlockBackend *blk_next(BlockBackend *blk); -BlockBackend *blk_all_next(BlockBackend *blk); -bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); -void monitor_remove_blk(BlockBackend *blk); - -BlockBackendPublic *blk_get_public(BlockBackend *blk); -BlockBackend *blk_by_public(BlockBackendPublic *public); - -BlockDriverState *blk_bs(BlockBackend *blk); -void blk_remove_bs(BlockBackend *blk); -int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); -int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); -bool bdrv_has_blk(BlockDriverState *bs); -bool bdrv_is_root_node(BlockDriverState *bs); -int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, - Error **errp); -void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm); - -void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow); -void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow); -void blk_set_disable_request_queuing(BlockBackend *blk, bool disable); -void blk_iostatus_enable(BlockBackend *blk); -bool blk_iostatus_is_enabled(const BlockBackend *blk); -BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk); -void blk_iostatus_disable(BlockBackend *blk); -void blk_iostatus_reset(BlockBackend *blk); -void blk_iostatus_set_err(BlockBackend *blk, int error); -int blk_attach_dev(BlockBackend *blk, DeviceState *dev); -void blk_detach_dev(BlockBackend *blk, DeviceState *dev); -DeviceState *blk_get_attached_dev(BlockBackend *blk); -char *blk_get_attached_dev_id(BlockBackend *blk); -BlockBackend *blk_by_dev(void *dev); -BlockBackend *blk_by_qdev_id(const char *id, Error **errp); -void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque); -int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); -int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags); -int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); - -static inline int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, - int64_t bytes, void *buf, - BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - - assert(bytes <= SIZE_MAX); - - return blk_co_preadv(blk, offset, bytes, &qiov, flags); -} - -static inline int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, - int64_t bytes, void *buf, - BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - - assert(bytes <= SIZE_MAX); - - return blk_co_pwritev(blk, offset, bytes, &qiov, flags); -} - -int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags, - BlockCompletionFunc *cb, void *opaque); -int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); -int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes); -int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, - BdrvRequestFlags flags); -int64_t blk_getlength(BlockBackend *blk); -void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); -int64_t blk_nb_sectors(BlockBackend *blk); -BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, - QEMUIOVector *qiov, BdrvRequestFlags flags, - BlockCompletionFunc *cb, void *opaque); -BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, - QEMUIOVector *qiov, BdrvRequestFlags flags, - BlockCompletionFunc *cb, void *opaque); -BlockAIOCB *blk_aio_flush(BlockBackend *blk, - BlockCompletionFunc *cb, void *opaque); -BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, - BlockCompletionFunc *cb, void *opaque); -void blk_aio_cancel(BlockAIOCB *acb); -void blk_aio_cancel_async(BlockAIOCB *acb); -int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); -BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, - BlockCompletionFunc *cb, void *opaque); -int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, - int64_t bytes); -int coroutine_fn blk_co_flush(BlockBackend *blk); -int blk_flush(BlockBackend *blk); -int blk_commit_all(void); -void blk_inc_in_flight(BlockBackend *blk); -void blk_dec_in_flight(BlockBackend *blk); -void blk_drain(BlockBackend *blk); -void blk_drain_all(void); -void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, - BlockdevOnError on_write_error); -BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); -BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, - int error); -void blk_error_action(BlockBackend *blk, BlockErrorAction action, - bool is_read, int error); -bool blk_supports_write_perm(BlockBackend *blk); -bool blk_is_writable(BlockBackend *blk); -bool blk_is_sg(BlockBackend *blk); -bool blk_enable_write_cache(BlockBackend *blk); -void blk_set_enable_write_cache(BlockBackend *blk, bool wce); -void blk_invalidate_cache(BlockBackend *blk, Error **errp); -bool blk_is_inserted(BlockBackend *blk); -bool blk_is_available(BlockBackend *blk); -void blk_lock_medium(BlockBackend *blk, bool locked); -void blk_eject(BlockBackend *blk, bool eject_flag); -int blk_get_flags(BlockBackend *blk); -uint32_t blk_get_request_alignment(BlockBackend *blk); -uint32_t blk_get_max_transfer(BlockBackend *blk); -uint64_t blk_get_max_hw_transfer(BlockBackend *blk); -int blk_get_max_iov(BlockBackend *blk); -int blk_get_max_hw_iov(BlockBackend *blk); -void blk_set_guest_block_size(BlockBackend *blk, int align); -void *blk_try_blockalign(BlockBackend *blk, size_t size); -void *blk_blockalign(BlockBackend *blk, size_t size); -bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); -void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason); -void blk_op_block_all(BlockBackend *blk, Error *reason); -void blk_op_unblock_all(BlockBackend *blk, Error *reason); -AioContext *blk_get_aio_context(BlockBackend *blk); -int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, - Error **errp); -void blk_add_aio_context_notifier(BlockBackend *blk, - void (*attached_aio_context)(AioContext *new_context, void *opaque), - void (*detach_aio_context)(void *opaque), void *opaque); -void blk_remove_aio_context_notifier(BlockBackend *blk, - void (*attached_aio_context)(AioContext *, - void *), - void (*detach_aio_context)(void *), - void *opaque); -void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify); -void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify); -void blk_io_plug(BlockBackend *blk); -void blk_io_unplug(BlockBackend *blk); -BlockAcctStats *blk_get_stats(BlockBackend *blk); -BlockBackendRootState *blk_get_root_state(BlockBackend *blk); -void blk_update_root_state(BlockBackend *blk); -bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk); -int blk_get_open_flags_from_root_state(BlockBackend *blk); - -void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, - BlockCompletionFunc *cb, void *opaque); -int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, - int64_t bytes); -int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); -int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); -int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, - int64_t pos, int size); -int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); -int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); -int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); -BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, - BlockCompletionFunc *cb, - void *opaque, int ret); - -void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); -void blk_io_limits_disable(BlockBackend *blk); -void blk_io_limits_enable(BlockBackend *blk, const char *group); -void blk_io_limits_update_group(BlockBackend *blk, const char *group); -void blk_set_force_allow_inactivate(BlockBackend *blk); - -void blk_register_buf(BlockBackend *blk, void *host, size_t size); -void blk_unregister_buf(BlockBackend *blk, void *host); - -int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, - BlockBackend *blk_out, int64_t off_out, - int64_t bytes, BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); - -const BdrvChild *blk_root(BlockBackend *blk); - -int blk_make_empty(BlockBackend *blk, Error **errp); +/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ #endif diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index f9fb54d437..3211b16513 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -13,9 +13,6 @@ #include "block/block.h" #include "qemu/queue.h" -void blockdev_mark_auto_del(BlockBackend *blk); -void blockdev_auto_del(BlockBackend *blk); - typedef enum { IF_DEFAULT = -1, /* for use with drive_add() only */ /* @@ -38,6 +35,16 @@ struct DriveInfo { QTAILQ_ENTRY(DriveInfo) next; }; +/* + * Global state (GS) API. These functions run under the BQL. + * + * See include/block/block-global-state.h for more information about + * the GS API. + */ + +void blockdev_mark_auto_del(BlockBackend *blk); +void blockdev_auto_del(BlockBackend *blk); + DriveInfo *blk_legacy_dinfo(BlockBackend *blk); DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo); BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo); diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index 868f1192de..b5c87d48b3 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -55,6 +55,4 @@ extern int smp_cores; extern int smp_threads; #endif -void list_cpus(const char *optarg); - #endif diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 250143cb5a..ffc2ea1072 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -154,12 +154,15 @@ typedef struct DumpState { GuestPhysBlockList guest_phys_blocks; ArchDumpInfo dump_info; MemoryMappingList list; - uint16_t phdr_num; - uint32_t sh_info; - bool have_section; + uint32_t phdr_num; + uint32_t shdr_num; bool resume; bool detached; ssize_t note_size; + hwaddr shdr_offset; + hwaddr phdr_offset; + hwaddr section_offset; + hwaddr note_offset; hwaddr memory_offset; int fd; diff --git a/include/sysemu/hax.h b/include/sysemu/hax.h index 247f0661d1..bf8f99a824 100644 --- a/include/sysemu/hax.h +++ b/include/sysemu/hax.h @@ -25,17 +25,23 @@ int hax_sync_vcpus(void); #ifdef NEED_CPU_H +# ifdef CONFIG_HAX +# define CONFIG_HAX_IS_POSSIBLE +# endif +#else /* !NEED_CPU_H */ +# define CONFIG_HAX_IS_POSSIBLE +#endif -#ifdef CONFIG_HAX +#ifdef CONFIG_HAX_IS_POSSIBLE -int hax_enabled(void); +extern bool hax_allowed; -#else /* CONFIG_HAX */ +#define hax_enabled() (hax_allowed) -#define hax_enabled() (0) +#else /* !CONFIG_HAX_IS_POSSIBLE */ -#endif /* CONFIG_HAX */ +#define hax_enabled() (0) -#endif /* NEED_CPU_H */ +#endif /* CONFIG_HAX_IS_POSSIBLE */ #endif /* QEMU_HAX_H */ diff --git a/include/sysemu/hw_accel.h b/include/sysemu/hw_accel.h index 01b5ebf442..22903a55f7 100644 --- a/include/sysemu/hw_accel.h +++ b/include/sysemu/hw_accel.h @@ -23,9 +23,4 @@ void cpu_synchronize_post_reset(CPUState *cpu); void cpu_synchronize_post_init(CPUState *cpu); void cpu_synchronize_pre_loadvm(CPUState *cpu); -static inline bool cpu_check_are_resettable(void) -{ - return kvm_enabled() ? kvm_cpu_check_are_resettable() : true; -} - #endif /* QEMU_HW_ACCEL_H */ diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 6eb39a088b..a783c78868 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -224,6 +224,11 @@ DECLARE_INSTANCE_CHECKER(KVMState, KVM_STATE, extern KVMState *kvm_state; typedef struct Notifier Notifier; +typedef struct KVMRouteChange { + KVMState *s; + int changes; +} KVMRouteChange; + /* external API */ bool kvm_has_free_slot(MachineState *ms); @@ -249,6 +254,9 @@ int kvm_has_intx_set_mask(void); bool kvm_arm_supports_user_irq(void); +int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); +int kvm_on_sigbus(int code, void *addr); + #ifdef NEED_CPU_H #include "cpu.h" @@ -261,9 +269,6 @@ int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, void kvm_remove_all_breakpoints(CPUState *cpu); int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); -int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); -int kvm_on_sigbus(int code, void *addr); - /* internal API */ int kvm_ioctl(KVMState *s, int type, ...); @@ -481,7 +486,7 @@ void kvm_init_cpu_signals(CPUState *cpu); /** * kvm_irqchip_add_msi_route - Add MSI route for specific vector - * @s: KVM state + * @c: KVMRouteChange instance. * @vector: which vector to add. This can be either MSI/MSIX * vector. The function will automatically detect whether * MSI/MSIX is enabled, and fetch corresponding MSI @@ -490,10 +495,24 @@ void kvm_init_cpu_signals(CPUState *cpu); * as @NULL, an empty MSI message will be inited. * @return: virq (>=0) when success, errno (<0) when failed. */ -int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev); +int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev); int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, PCIDevice *dev); void kvm_irqchip_commit_routes(KVMState *s); + +static inline KVMRouteChange kvm_irqchip_begin_route_changes(KVMState *s) +{ + return (KVMRouteChange) { .s = s, .changes = 0 }; +} + +static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c) +{ + if (c->changes) { + kvm_irqchip_commit_routes(c->s); + c->changes = 0; + } +} + void kvm_irqchip_release_virq(KVMState *s, int virq); int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter); diff --git a/include/sysemu/memory_mapping.h b/include/sysemu/memory_mapping.h index 4b20f1a639..3bbeb1bcb4 100644 --- a/include/sysemu/memory_mapping.h +++ b/include/sysemu/memory_mapping.h @@ -15,8 +15,7 @@ #define MEMORY_MAPPING_H #include "qemu/queue.h" -#include "exec/cpu-defs.h" -#include "exec/memory.h" +#include "exec/cpu-common.h" typedef struct GuestPhysBlock { /* visible to guest, reflects PCI hole, etc */ @@ -43,7 +42,7 @@ typedef struct GuestPhysBlockList { /* The physical and virtual address in the memory mapping are contiguous. */ typedef struct MemoryMapping { hwaddr phys_addr; - target_ulong virt_addr; + vaddr virt_addr; ram_addr_t length; QTAILQ_ENTRY(MemoryMapping) next; } MemoryMapping; diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h index 2edf33658a..58de7c994d 100644 --- a/include/sysemu/os-posix.h +++ b/include/sysemu/os-posix.h @@ -42,7 +42,9 @@ extern "C" { #endif +int os_parse_cmd_args(int index, const char *optarg); void os_set_line_buffering(void); +void os_setup_early_signal_handling(void); void os_set_proc_name(const char *s); void os_setup_signal_handling(void); void os_daemonize(void); @@ -52,9 +54,7 @@ int os_mlock(void); #define closesocket(s) close(s) #define ioctlsocket(s, r, v) ioctl(s, r, v) -typedef struct timeval qemu_timeval; -#define qemu_gettimeofday(tp) gettimeofday(tp, NULL) - +int os_set_daemonize(bool d); bool is_daemonized(void); /** diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index 43f569b5c2..edc3b38a57 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -62,8 +62,10 @@ struct tm *localtime_r(const time_t *timep, struct tm *result); static inline void os_setup_signal_handling(void) {} static inline void os_daemonize(void) {} static inline void os_setup_post(void) {} -void os_set_line_buffering(void); static inline void os_set_proc_name(const char *dummy) {} +static inline int os_parse_cmd_args(int index, const char *optarg) { return -1; } +void os_set_line_buffering(void); +void os_setup_early_signal_handling(void); int getpagesize(void); @@ -71,11 +73,13 @@ int getpagesize(void); # define EPROTONOSUPPORT EINVAL #endif -typedef struct { - long tv_sec; - long tv_usec; -} qemu_timeval; -int qemu_gettimeofday(qemu_timeval *tp); +static inline int os_set_daemonize(bool d) +{ + if (d) { + return -ENOTSUP; + } + return 0; +} static inline bool is_daemonized(void) { @@ -105,20 +109,22 @@ static inline char *realpath(const char *path, char *resolved_path) return resolved_path; } -/* ??? Mingw appears to export _lock_file and _unlock_file as the functions - * with which to lock a stdio handle. But something is wrong in the markup, - * either in the header or the library, such that we get undefined references - * to "_imp___lock_file" etc when linking. Since we seem to have no other - * alternative, and the usage within the logging functions isn't critical, - * ignore FILE locking. +/* + * Older versions of MinGW do not import _lock_file and _unlock_file properly. + * This was fixed for v6.0.0 with commit b48e3ac8969d. */ - static inline void qemu_flockfile(FILE *f) { +#ifdef HAVE__LOCK_FILE + _lock_file(f); +#endif } static inline void qemu_funlockfile(FILE *f) { +#ifdef HAVE__LOCK_FILE + _unlock_file(f); +#endif } /* We wrap all the sockets functions so that we can diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h index a535691573..f3ed52548e 100644 --- a/include/sysemu/runstate.h +++ b/include/sysemu/runstate.h @@ -34,7 +34,13 @@ static inline bool shutdown_caused_by_guest(ShutdownCause cause) } void vm_start(void); -int vm_prepare_start(void); + +/** + * vm_prepare_start: Prepare for starting/resuming the VM + * + * @step_pending: whether any of the CPUs is about to be single-stepped by gdb + */ +int vm_prepare_start(bool step_pending); int vm_stop(RunState state); int vm_stop_force_state(RunState state); int vm_shutdown(void); @@ -69,6 +75,7 @@ void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(ShutdownCause reason); void qemu_system_guest_panicked(GuestPanicInformation *info); void qemu_system_guest_crashloaded(GuestPanicInformation *info); +bool qemu_system_dump_in_progress(void); #endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index b9421e03ff..10e283c170 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -13,6 +13,8 @@ extern const char *qemu_name; extern QemuUUID qemu_uuid; extern bool qemu_uuid_set; +const char *qemu_get_vm_name(void); + void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); diff --git a/include/tcg/tcg-ldst.h b/include/tcg/tcg-ldst.h index bf40942de4..121a156933 100644 --- a/include/tcg/tcg-ldst.h +++ b/include/tcg/tcg-ldst.h @@ -72,8 +72,8 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, #else -void QEMU_NORETURN helper_unaligned_ld(CPUArchState *env, target_ulong addr); -void QEMU_NORETURN helper_unaligned_st(CPUArchState *env, target_ulong addr); +G_NORETURN void helper_unaligned_ld(CPUArchState *env, target_ulong addr); +G_NORETURN void helper_unaligned_st(CPUArchState *env, target_ulong addr); #endif /* CONFIG_SOFTMMU */ #endif /* TCG_LDST_H */ diff --git a/include/tcg/tcg-op-gvec.h b/include/tcg/tcg-op-gvec.h index da55fed870..28cafbcc5c 100644 --- a/include/tcg/tcg-op-gvec.h +++ b/include/tcg/tcg-op-gvec.h @@ -218,6 +218,25 @@ typedef struct { bool write_aofs; } GVecGen4; +typedef struct { + /* + * Expand inline as a 64-bit or 32-bit integer. Only one of these will be + * non-NULL. + */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, int64_t); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, int32_t); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); + /* Expand out-of-line helper w/descriptor, data in descriptor. */ + gen_helper_gvec_4 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; +} GVecGen4i; + void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz, const GVecGen2 *); void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, @@ -231,6 +250,9 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, const GVecGen3i *); void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t oprsz, uint32_t maxsz, const GVecGen4 *); +void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz, int64_t c, + const GVecGen4i *); /* Expand a specific vector operation. */ diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index caa0a63612..b09b8b4a05 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -332,6 +332,7 @@ void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags); void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); @@ -531,6 +532,8 @@ void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); @@ -1077,6 +1080,8 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_bswap32_tl tcg_gen_bswap32_i64 #define tcg_gen_bswap64_tl tcg_gen_bswap64_i64 #define tcg_gen_bswap_tl tcg_gen_bswap64_i64 +#define tcg_gen_hswap_tl tcg_gen_hswap_i64 +#define tcg_gen_wswap_tl tcg_gen_wswap_i64 #define tcg_gen_concat_tl_i64 tcg_gen_concat32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr32_i64 #define tcg_gen_andc_tl tcg_gen_andc_i64 @@ -1192,6 +1197,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_bswap16_tl tcg_gen_bswap16_i32 #define tcg_gen_bswap32_tl(D, S, F) tcg_gen_bswap32_i32(D, S) #define tcg_gen_bswap_tl tcg_gen_bswap32_i32 +#define tcg_gen_hswap_tl tcg_gen_hswap_i32 #define tcg_gen_concat_tl_i64 tcg_gen_concat_i32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr_i64_i32 #define tcg_gen_andc_tl tcg_gen_andc_i32 diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 675873e200..dd444734d9 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -245,6 +245,9 @@ DEF(or_vec, 1, 2, 0, IMPLVEC) DEF(xor_vec, 1, 2, 0, IMPLVEC) DEF(andc_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_andc_vec)) DEF(orc_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_orc_vec)) +DEF(nand_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_nand_vec)) +DEF(nor_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_nor_vec)) +DEF(eqv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_eqv_vec)) DEF(not_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_not_vec)) DEF(shli_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_shi_vec)) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 42f5b500ed..26a70526f1 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -43,7 +43,7 @@ #else #define MAX_OPC_PARAM_PER_ARG 1 #endif -#define MAX_OPC_PARAM_IARGS 6 +#define MAX_OPC_PARAM_IARGS 7 #define MAX_OPC_PARAM_OARGS 1 #define MAX_OPC_PARAM_ARGS (MAX_OPC_PARAM_IARGS + MAX_OPC_PARAM_OARGS) @@ -183,6 +183,9 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_not_vec 0 #define TCG_TARGET_HAS_andc_vec 0 #define TCG_TARGET_HAS_orc_vec 0 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 #define TCG_TARGET_HAS_roti_vec 0 #define TCG_TARGET_HAS_rots_vec 0 #define TCG_TARGET_HAS_rotv_vec 0 @@ -395,7 +398,7 @@ typedef TCGv_ptr TCGv_env; #define TCG_CALL_NO_WRITE_GLOBALS 0x0002 /* Helper can be safely suppressed if the return value is not used. */ #define TCG_CALL_NO_SIDE_EFFECTS 0x0004 -/* Helper is QEMU_NORETURN. */ +/* Helper is G_NORETURN. */ #define TCG_CALL_NO_RETURN 0x0008 /* convenience version of most used call flags */ @@ -430,6 +433,8 @@ typedef enum TCGTempVal { typedef enum TCGTempKind { /* Temp is dead at the end of all basic blocks. */ TEMP_NORMAL, + /* Temp is live across conditional branch, but dead otherwise. */ + TEMP_EBB, /* Temp is saved across basic blocks but dead at the end of TBs. */ TEMP_LOCAL, /* Temp is saved across both basic blocks and translation blocks. */ @@ -1051,9 +1056,11 @@ TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); #if UINTPTR_MAX == UINT32_MAX # define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i32((intptr_t)(x))) # define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i32((intptr_t)(x))) +# define tcg_constant_ptr(x) ((TCGv_ptr)tcg_constant_i32((intptr_t)(x))) #else # define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i64((intptr_t)(x))) # define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i64((intptr_t)(x))) +# define tcg_constant_ptr(x) ((TCGv_ptr)tcg_constant_i64((intptr_t)(x))) #endif TCGLabel *gen_new_label(void); diff --git a/include/ui/console.h b/include/ui/console.h index f590819880..c44b28a972 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -282,23 +282,28 @@ struct DisplayChangeListener { }; typedef struct DisplayGLCtxOps { - /* - * We only check if the GLCtx is compatible with a DCL via ops. A natural - * evolution of this would be a callback to check some runtime requirements - * and allow various DCL kinds. - */ - const DisplayChangeListenerOps *compatible_dcl; - + bool (*dpy_gl_ctx_is_compatible_dcl)(DisplayGLCtx *dgc, + DisplayChangeListener *dcl); QEMUGLContext (*dpy_gl_ctx_create)(DisplayGLCtx *dgc, QEMUGLParams *params); void (*dpy_gl_ctx_destroy)(DisplayGLCtx *dgc, QEMUGLContext ctx); int (*dpy_gl_ctx_make_current)(DisplayGLCtx *dgc, QEMUGLContext ctx); + void (*dpy_gl_ctx_create_texture)(DisplayGLCtx *dgc, + DisplaySurface *surface); + void (*dpy_gl_ctx_destroy_texture)(DisplayGLCtx *dgc, + DisplaySurface *surface); + void (*dpy_gl_ctx_update_texture)(DisplayGLCtx *dgc, + DisplaySurface *surface, + int x, int y, int w, int h); } DisplayGLCtxOps; struct DisplayGLCtx { const DisplayGLCtxOps *ops; +#ifdef CONFIG_OPENGL + QemuGLShader *gls; /* optional shared shader */ +#endif }; DisplayState *init_displaystate(void); @@ -513,6 +518,7 @@ int vnc_display_pw_expire(const char *id, time_t expires); void vnc_parse(const char *str); int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp); bool vnc_display_reload_certs(const char *id, Error **errp); +bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp); /* input.c */ int index_from_key(const char *key, size_t key_length); diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 806ddcd7cd..0c775604d1 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -19,7 +19,7 @@ * feeding libjpeg / libpng and writing screenshots. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN # define PIXMAN_BE_r8g8b8 PIXMAN_r8g8b8 # define PIXMAN_BE_x8r8g8b8 PIXMAN_x8r8g8b8 # define PIXMAN_BE_a8r8g8b8 PIXMAN_a8r8g8b8 diff --git a/io/channel-command.c b/io/channel-command.c index 338da73ade..4a1f969aaa 100644 --- a/io/channel-command.c +++ b/io/channel-command.c @@ -26,8 +26,28 @@ #include "qemu/sockets.h" #include "trace.h" - -QIOChannelCommand * +#ifndef WIN32 +/** + * qio_channel_command_new_pid: + * @writefd: the FD connected to the command's stdin + * @readfd: the FD connected to the command's stdout + * @pid: the PID of the running child command + * @errp: pointer to a NULL-initialized error object + * + * Create a channel for performing I/O with the + * previously spawned command identified by @pid. + * The two file descriptors provide the connection + * to command's stdio streams, either one or which + * may be -1 to indicate that stream is not open. + * + * The channel will take ownership of the process + * @pid and will kill it when closing the channel. + * Similarly it will take responsibility for + * closing the file descriptors @writefd and @readfd. + * + * Returns: the command channel object, or NULL on error + */ +static QIOChannelCommand * qio_channel_command_new_pid(int writefd, int readfd, pid_t pid) @@ -44,8 +64,6 @@ qio_channel_command_new_pid(int writefd, return ioc; } - -#ifndef WIN32 QIOChannelCommand * qio_channel_command_new_spawn(const char *const argv[], int flags, @@ -76,8 +94,8 @@ qio_channel_command_new_spawn(const char *const argv[], } } - if ((!stdinnull && pipe(stdinfd) < 0) || - (!stdoutnull && pipe(stdoutfd) < 0)) { + if ((!stdinnull && !g_unix_open_pipe(stdinfd, FD_CLOEXEC, NULL)) || + (!stdoutnull && !g_unix_open_pipe(stdoutfd, FD_CLOEXEC, NULL))) { error_setg_errno(errp, errno, "Unable to open pipe"); goto error; @@ -283,16 +301,18 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { +#ifdef WIN32 + /* command spawn is not supported on win32 */ + g_assert_not_reached(); +#else QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); - if (enabled) { - qemu_set_block(cioc->writefd); - qemu_set_block(cioc->readfd); - } else { - qemu_set_nonblock(cioc->writefd); - qemu_set_nonblock(cioc->readfd); + if (!g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL) || + !g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return -1; } - +#endif return 0; } diff --git a/io/channel-file.c b/io/channel-file.c index d7cf6d278f..d146ace7db 100644 --- a/io/channel-file.c +++ b/io/channel-file.c @@ -139,14 +139,19 @@ static int qio_channel_file_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { +#ifdef WIN32 + /* not implemented */ + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return -1; +#else QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); - if (enabled) { - qemu_set_block(fioc->fd); - } else { - qemu_set_nonblock(fioc->fd); + if (!g_unix_set_fd_nonblocking(fioc->fd, !enabled, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return -1; } return 0; +#endif } diff --git a/io/channel-socket.c b/io/channel-socket.c index 459922c874..e531d7bd2a 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qapi/qapi-visit-sockets.h" #include "qemu/module.h" @@ -461,7 +460,7 @@ static void qio_channel_socket_copy_fds(struct msghdr *msg, } /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_set_block(fd); + qemu_socket_set_block(fd); #ifndef MSG_CMSG_CLOEXEC qemu_set_cloexec(fd); @@ -666,9 +665,9 @@ qio_channel_socket_set_blocking(QIOChannel *ioc, QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); if (enabled) { - qemu_set_block(sioc->fd); + qemu_socket_set_block(sioc->fd); } else { - qemu_set_nonblock(sioc->fd); + qemu_socket_set_nonblock(sioc->fd); } return 0; } @@ -681,9 +680,9 @@ qio_channel_socket_set_delay(QIOChannel *ioc, QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); int v = enabled ? 0 : 1; - qemu_setsockopt(sioc->fd, - IPPROTO_TCP, TCP_NODELAY, - &v, sizeof(v)); + setsockopt(sioc->fd, + IPPROTO_TCP, TCP_NODELAY, + &v, sizeof(v)); } diff --git a/io/channel-websock.c b/io/channel-websock.c index 70889bb54d..55145a6a8c 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -157,7 +157,7 @@ enum { QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA }; -static void GCC_FMT_ATTR(2, 3) +static void G_GNUC_PRINTF(2, 3) qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc, const char *resmsg, ...) diff --git a/job.c b/job.c index 54db80df66..075c6f3a20 100644 --- a/job.c +++ b/job.c @@ -381,6 +381,8 @@ void job_ref(Job *job) void job_unref(Job *job) { + GLOBAL_STATE_CODE(); + if (--job->refcnt == 0) { assert(job->status == JOB_STATUS_NULL); assert(!timer_pending(&job->sleep_timer)); @@ -602,6 +604,7 @@ bool job_user_paused(Job *job) void job_user_resume(Job *job, Error **errp) { assert(job); + GLOBAL_STATE_CODE(); if (!job->user_paused || job->pause_count <= 0) { error_setg(errp, "Can't resume a job that was not paused"); return; @@ -672,6 +675,7 @@ static void job_update_rc(Job *job) static void job_commit(Job *job) { assert(!job->ret); + GLOBAL_STATE_CODE(); if (job->driver->commit) { job->driver->commit(job); } @@ -680,6 +684,7 @@ static void job_commit(Job *job) static void job_abort(Job *job) { assert(job->ret); + GLOBAL_STATE_CODE(); if (job->driver->abort) { job->driver->abort(job); } @@ -687,6 +692,7 @@ static void job_abort(Job *job) static void job_clean(Job *job) { + GLOBAL_STATE_CODE(); if (job->driver->clean) { job->driver->clean(job); } @@ -726,6 +732,7 @@ static int job_finalize_single(Job *job) static void job_cancel_async(Job *job, bool force) { + GLOBAL_STATE_CODE(); if (job->driver->cancel) { force = job->driver->cancel(job, force); } else { @@ -825,6 +832,7 @@ static void job_completed_txn_abort(Job *job) static int job_prepare(Job *job) { + GLOBAL_STATE_CODE(); if (job->ret == 0 && job->driver->prepare) { job->ret = job->driver->prepare(job); job_update_rc(job); @@ -952,6 +960,7 @@ static void coroutine_fn job_co_entry(void *opaque) Job *job = opaque; assert(job && job->driver && job->driver->run); + assert(job->aio_context == qemu_get_current_aio_context()); job_pause_point(job); job->ret = job->driver->run(job, &job->err); job->deferred_to_main_loop = true; @@ -1054,6 +1063,7 @@ void job_complete(Job *job, Error **errp) { /* Should not be reachable via external interface for internal jobs */ assert(job->id); + GLOBAL_STATE_CODE(); if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) { return; } diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 2da3316bb5..bf6e96011d 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -452,6 +452,9 @@ struct kvm_sync_regs { #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 +/* attributes for system fd (group 0) */ +#define KVM_X86_XCOMP_GUEST_SUPP 0 + struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; __u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 00af3bc333..d232feaae9 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -1133,6 +1133,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206 #define KVM_CAP_VM_GPA_BITS 207 #define KVM_CAP_XSAVE2 208 +#define KVM_CAP_SYS_ATTRIBUTES 209 #ifdef KVM_CAP_IRQ_ROUTING @@ -2047,4 +2048,7 @@ struct kvm_stats_desc { #define KVM_GET_STATS_FD _IO(KVMIO, 0xce) +/* Available with KVM_CAP_XSAVE2 */ +#define KVM_GET_XSAVE2 _IOR(KVMIO, 0xcf, struct kvm_xsave) + #endif /* __LINUX_KVM_H */ diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index aa948420b6..f3f19fb090 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" @@ -212,7 +211,7 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) } env->pc = regs->pc; env->xregs[31] = regs->sp; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN env->cp15.sctlr_el[1] |= SCTLR_E0E; for (i = 1; i < 4; ++i) { env->cp15.sctlr_el[i] |= SCTLR_EE; diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index df9e39a4ba..7de4c96eb9 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -147,7 +147,7 @@ static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, for (i = 0; i < 32; i++) { uint64_t *q = aa64_vfp_qreg(env, i); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN __put_user(q[0], &fpsimd->vregs[i * 2 + 1]); __put_user(q[1], &fpsimd->vregs[i * 2]); #else @@ -233,7 +233,7 @@ static void target_restore_fpsimd_record(CPUARMState *env, for (i = 0; i < 32; i++) { uint64_t *q = aa64_vfp_qreg(env, i); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN __get_user(q[0], &fpsimd->vregs[i * 2 + 1]); __get_user(q[1], &fpsimd->vregs[i * 2]); #else diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h index a98f568ab4..c055133725 100644 --- a/linux-user/aarch64/target_syscall.h +++ b/linux-user/aarch64/target_syscall.h @@ -8,7 +8,7 @@ struct target_pt_regs { uint64_t pstate; }; -#if defined(TARGET_WORDS_BIGENDIAN) +#if TARGET_BIG_ENDIAN #define UNAME_MACHINE "aarch64_be" #else #define UNAME_MACHINE "aarch64" diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index de6e0c901c..2ea039aa71 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 060d8cd297..a1e9005b6e 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "elf.h" @@ -75,10 +74,70 @@ put_user_u16(__x, (gaddr)); \ }) -/* Commpage handling -- there is no commpage for AArch64 */ +/* + * Similar to code in accel/tcg/user-exec.c, but outside the execution loop. + * Must be called with mmap_lock. + * We get the PC of the entry address - which is as good as anything, + * on a real kernel what you get depends on which mode it uses. + */ +static void *atomic_mmu_lookup(CPUArchState *env, uint32_t addr, int size) +{ + int need_flags = PAGE_READ | PAGE_WRITE_ORG | PAGE_VALID; + int page_flags; + + /* Enforce guest required alignment. */ + if (unlikely(addr & (size - 1))) { + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, addr); + return NULL; + } + + page_flags = page_get_flags(addr); + if (unlikely((page_flags & need_flags) != need_flags)) { + force_sig_fault(TARGET_SIGSEGV, + page_flags & PAGE_VALID ? + TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR, addr); + return NULL; + } + + return g2h(env_cpu(env), addr); +} /* - * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt + * See the Linux kernel's Documentation/arm/kernel_user_helpers.rst + * Input: + * r0 = oldval + * r1 = newval + * r2 = pointer to target value + * + * Output: + * r0 = 0 if *ptr was changed, non-0 if no exchange happened + * C set if *ptr was changed, clear if no exchange happened + */ +static void arm_kernel_cmpxchg32_helper(CPUARMState *env) +{ + uint32_t oldval, newval, val, addr, cpsr, *host_addr; + + oldval = env->regs[0]; + newval = env->regs[1]; + addr = env->regs[2]; + + mmap_lock(); + host_addr = atomic_mmu_lookup(env, addr, 4); + if (!host_addr) { + mmap_unlock(); + return; + } + + val = qatomic_cmpxchg__nocheck(host_addr, oldval, newval); + mmap_unlock(); + + cpsr = (val == oldval) * CPSR_C; + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); + env->regs[0] = cpsr ? 0 : -1; +} + +/* + * See the Linux kernel's Documentation/arm/kernel_user_helpers.rst * Input: * r0 = pointer to oldval * r1 = pointer to newval @@ -95,57 +154,54 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) { uint64_t oldval, newval, val; uint32_t addr, cpsr; + uint64_t *host_addr; - /* Based on the 32 bit code in do_kernel_trap */ + addr = env->regs[0]; + if (get_user_u64(oldval, addr)) { + goto segv; + } - /* XXX: This only works between threads, not between processes. - It's probably possible to implement this with native host - operations. However things like ldrex/strex are much harder so - there's not much point trying. */ - start_exclusive(); - cpsr = cpsr_read(env); + addr = env->regs[1]; + if (get_user_u64(newval, addr)) { + goto segv; + } + + mmap_lock(); addr = env->regs[2]; - - if (get_user_u64(oldval, env->regs[0])) { - env->exception.vaddress = env->regs[0]; - goto segv; - }; - - if (get_user_u64(newval, env->regs[1])) { - env->exception.vaddress = env->regs[1]; - goto segv; - }; - - if (get_user_u64(val, addr)) { - env->exception.vaddress = addr; - goto segv; + host_addr = atomic_mmu_lookup(env, addr, 8); + if (!host_addr) { + mmap_unlock(); + return; } +#ifdef CONFIG_ATOMIC64 + val = qatomic_cmpxchg__nocheck(host_addr, oldval, newval); + cpsr = (val == oldval) * CPSR_C; +#else + /* + * This only works between threads, not between processes, but since + * the host has no 64-bit cmpxchg, it is the best that we can do. + */ + start_exclusive(); + val = *host_addr; if (val == oldval) { - val = newval; - - if (put_user_u64(val, addr)) { - env->exception.vaddress = addr; - goto segv; - }; - - env->regs[0] = 0; - cpsr |= CPSR_C; + *host_addr = newval; + cpsr = CPSR_C; } else { - env->regs[0] = -1; - cpsr &= ~CPSR_C; + cpsr = 0; } - cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); end_exclusive(); +#endif + mmap_unlock(); + + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); + env->regs[0] = cpsr ? 0 : -1; return; -segv: - end_exclusive(); - /* We get the PC of the entry address - which is as good as anything, - on a real kernel what you get depends on which mode it uses. */ - /* XXX: check env->error_code */ - force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, - env->exception.vaddress); + segv: + force_sig_fault(TARGET_SIGSEGV, + page_get_flags(addr) & PAGE_VALID ? + TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR, addr); } /* Handle a jump to the kernel code page. */ @@ -153,36 +209,13 @@ static int do_kernel_trap(CPUARMState *env) { uint32_t addr; - uint32_t cpsr; - uint32_t val; switch (env->regs[15]) { case 0xffff0fa0: /* __kernel_memory_barrier */ - /* ??? No-op. Will need to do better for SMP. */ + smp_mb(); break; case 0xffff0fc0: /* __kernel_cmpxchg */ - /* XXX: This only works between threads, not between processes. - It's probably possible to implement this with native host - operations. However things like ldrex/strex are much harder so - there's not much point trying. */ - start_exclusive(); - cpsr = cpsr_read(env); - addr = env->regs[2]; - /* FIXME: This should SEGV if the access fails. */ - if (get_user_u32(val, addr)) - val = ~env->regs[0]; - if (val == env->regs[0]) { - val = env->regs[1]; - /* FIXME: Check for segfaults. */ - put_user_u32(val, addr); - env->regs[0] = 0; - cpsr |= CPSR_C; - } else { - env->regs[0] = -1; - cpsr &= ~CPSR_C; - } - cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); - end_exclusive(); + arm_kernel_cmpxchg32_helper(env); break; case 0xffff0fe0: /* __kernel_get_tls */ env->regs[0] = cpu_get_tls(env); @@ -197,7 +230,7 @@ do_kernel_trap(CPUARMState *env) /* Jump back to the caller. */ addr = env->regs[14]; if (addr & 1) { - env->thumb = 1; + env->thumb = true; addr &= ~1; } env->regs[15] = addr; @@ -495,7 +528,7 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) for(i = 0; i < 16; i++) { env->regs[i] = regs->uregs[i]; } -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN /* Enable BE8. */ if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 && (info->elf_flags & EF_ARM_BE8)) { diff --git a/linux-user/arm/nwfpe/double_cpdo.c b/linux-user/arm/nwfpe/double_cpdo.c index 1cef380852..d45ece2e2f 100644 --- a/linux-user/arm/nwfpe/double_cpdo.c +++ b/linux-user/arm/nwfpe/double_cpdo.c @@ -150,7 +150,7 @@ unsigned int DoubleCPDO(const unsigned int opcode) case MNF_CODE: { unsigned int *p = (unsigned int*)&rFm; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN p[0] ^= 0x80000000; #else p[1] ^= 0x80000000; @@ -162,7 +162,7 @@ unsigned int DoubleCPDO(const unsigned int opcode) case ABS_CODE: { unsigned int *p = (unsigned int*)&rFm; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN p[0] &= 0x7fffffff; #else p[1] &= 0x7fffffff; diff --git a/linux-user/arm/nwfpe/fpa11_cpdt.c b/linux-user/arm/nwfpe/fpa11_cpdt.c index c32b0c2faa..fee525937c 100644 --- a/linux-user/arm/nwfpe/fpa11_cpdt.c +++ b/linux-user/arm/nwfpe/fpa11_cpdt.c @@ -44,7 +44,7 @@ void loadDouble(const unsigned int Fn, target_ulong addr) unsigned int *p; p = (unsigned int*)&fpa11->fpreg[Fn].fDouble; fpa11->fType[Fn] = typeDouble; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN /* FIXME - handle failure of get_user() */ get_user_u32(p[0], addr); /* sign & exponent */ get_user_u32(p[1], addr + 4); @@ -147,7 +147,7 @@ void storeDouble(const unsigned int Fn, target_ulong addr) default: val = fpa11->fpreg[Fn].fDouble; } /* FIXME - handle put_user() failures */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN put_user_u32(p[0], addr); /* msw */ put_user_u32(p[1], addr + 4); /* lsw */ #else diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index f04f9c9e3d..412ad434cf 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -18,7 +18,7 @@ struct target_pt_regs { #define ARM_NR_set_tls (ARM_NR_BASE + 5) #define ARM_NR_get_tls (ARM_NR_BASE + 6) -#if defined(TARGET_WORDS_BIGENDIAN) +#if TARGET_BIG_ENDIAN #define UNAME_MACHINE "armv5teb" #else #define UNAME_MACHINE "armv5tel" diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c index 0f46b3c1a8..01e6ff16fc 100644 --- a/linux-user/cris/cpu_loop.c +++ b/linux-user/cris/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 9628a38361..61063fd974 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -105,7 +105,7 @@ int info_is_fdpic(struct image_info *info) #define ELIBBAD 80 #endif -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN #define ELF_DATA ELFDATA2MSB #else #define ELF_DATA ELFDATA2LSB @@ -483,7 +483,7 @@ static const char *get_elf_platform(void) { CPUARMState *env = thread_cpu->env_ptr; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN # define END "b" #else # define END "l" @@ -514,7 +514,7 @@ static const char *get_elf_platform(void) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN # define ELF_PLATFORM "aarch64_be" #else # define ELF_PLATFORM "aarch64" @@ -1096,7 +1096,6 @@ static void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->ea = infop->entry; regs->sp = infop->start_stack; - regs->estatus = 0x3; } #define LO_COMMPAGE TARGET_PAGE_SIZE @@ -1170,7 +1169,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, (*regs)[30] = -1; /* R_SSTATUS */ (*regs)[31] = tswapreg(env->regs[R_RA]); - (*regs)[32] = tswapreg(env->regs[R_PC]); + (*regs)[32] = tswapreg(env->pc); (*regs)[33] = -1; /* R_STATUS */ (*regs)[34] = tswapreg(env->regs[CR_ESTATUS]); @@ -1916,8 +1915,8 @@ static abi_ulong setup_arg_pages(struct linux_binprm *bprm, size = STACK_LOWER_LIMIT; } guard = TARGET_PAGE_SIZE; - if (guard < qemu_real_host_page_size) { - guard = qemu_real_host_page_size; + if (guard < qemu_real_host_page_size()) { + guard = qemu_real_host_page_size(); } error = target_mmap(0, size + guard, PROT_READ | PROT_WRITE, @@ -2504,7 +2503,7 @@ static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr, addr = mmap(test, reserved_va, PROT_NONE, flags, -1, 0); if (addr == MAP_FAILED || addr != test) { error_report("Unable to reserve 0x%lx bytes of virtual address " - "space at %p (%s) for use as guest address space (check your" + "space at %p (%s) for use as guest address space (check your " "virtual memory ulimit setting, min_mmap_addr or reserve less " "using -R option)", reserved_va, test, strerror(errno)); exit(EXIT_FAILURE); diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 0d73934d31..b84e25bf71 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -19,7 +19,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 3765acf9ee..2e39fc2f9f 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "qemu/timer.h" #include "user-internals.h" diff --git a/linux-user/include/host/ppc/host-signal.h b/linux-user/include/host/ppc/host-signal.h deleted file mode 100644 index b80384d135..0000000000 --- a/linux-user/include/host/ppc/host-signal.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * host-signal.h: signal info dependent on the host architecture - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2021 Linaro Limited - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef PPC_HOST_SIGNAL_H -#define PPC_HOST_SIGNAL_H - -/* The third argument to a SA_SIGINFO handler is ucontext_t. */ -typedef ucontext_t host_sigcontext; - -static inline uintptr_t host_signal_pc(host_sigcontext *uc) -{ - return uc->uc_mcontext.regs->nip; -} - -static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) -{ - uc->uc_mcontext.regs->nip = pc; -} - -static inline void *host_signal_mask(host_sigcontext *uc) -{ - return &uc->uc_sigmask; -} - -static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) -{ - return uc->uc_mcontext.regs->trap != 0x400 - && (uc->uc_mcontext.regs->dsisr & 0x02000000); -} - -#endif diff --git a/linux-user/include/host/ppc64/host-signal.h b/linux-user/include/host/ppc64/host-signal.h index a353c22a90..c4ea866472 100644 --- a/linux-user/include/host/ppc64/host-signal.h +++ b/linux-user/include/host/ppc64/host-signal.h @@ -1 +1,41 @@ -#include "../ppc/host-signal.h" +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef PPC_HOST_SIGNAL_H +#define PPC_HOST_SIGNAL_H + +/* Needed for PT_* constants */ +#include + +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) +{ + return uc->uc_mcontext.gp_regs[PT_NIP]; +} + +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) +{ + uc->uc_mcontext.gp_regs[PT_NIP] = pc; +} + +static inline void *host_signal_mask(host_sigcontext *uc) +{ + return &uc->uc_sigmask; +} + +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) +{ + return uc->uc_mcontext.gp_regs[PT_TRAP] != 0x400 + && (uc->uc_mcontext.gp_regs[PT_DSISR] & 0x02000000); +} + +#endif diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 928a18e3cf..d1bf8548b7 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/main.c b/linux-user/main.c index a9c50c310d..aa95527665 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -18,10 +18,9 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qemu/units.h" #include "qemu/accel.h" -#include "sysemu/tcg.h" #include "qemu-version.h" #include #include @@ -85,6 +84,7 @@ static bool enable_strace; * Used to support command line arguments overriding environment variables. */ static int last_log_mask; +static const char *last_log_filename; /* * When running 32-on-64 we should make sure we can fit all of the possible @@ -257,7 +257,7 @@ static void handle_arg_dfilter(const char *arg) static void handle_arg_log_filename(const char *arg) { - qemu_set_log_filename(arg, &error_fatal); + last_log_filename = arg; } static void handle_arg_set_env(const char *arg) @@ -688,7 +688,6 @@ int main(int argc, char **argv, char **envp) int i; int ret; int execfd; - int log_mask; unsigned long max_reserved_va; bool preserve_argv0; @@ -722,11 +721,9 @@ int main(int argc, char **argv, char **envp) optind = parse_args(argc, argv); - log_mask = last_log_mask | (enable_strace ? LOG_STRACE : 0); - if (log_mask) { - qemu_log_needs_buffers(); - qemu_set_log(log_mask); - } + qemu_set_log_filename_flags(last_log_filename, + last_log_mask | (enable_strace * LOG_STRACE), + &error_fatal); if (!trace_init_backends()) { exit(1); @@ -903,21 +900,36 @@ int main(int argc, char **argv, char **envp) g_free(target_environ); if (qemu_loglevel_mask(CPU_LOG_PAGE)) { - qemu_log("guest_base %p\n", (void *)guest_base); - log_page_dump("binary load"); + FILE *f = qemu_log_trylock(); + if (f) { + fprintf(f, "guest_base %p\n", (void *)guest_base); + fprintf(f, "page layout changed following binary load\n"); + page_dump(f); - qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); - qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); - qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n", info->start_code); - qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n", info->start_data); - qemu_log("end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data); - qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n", info->start_stack); - qemu_log("brk 0x" TARGET_ABI_FMT_lx "\n", info->brk); - qemu_log("entry 0x" TARGET_ABI_FMT_lx "\n", info->entry); - qemu_log("argv_start 0x" TARGET_ABI_FMT_lx "\n", info->arg_start); - qemu_log("env_start 0x" TARGET_ABI_FMT_lx "\n", - info->arg_end + (abi_ulong)sizeof(abi_ulong)); - qemu_log("auxv_start 0x" TARGET_ABI_FMT_lx "\n", info->saved_auxv); + fprintf(f, "start_brk 0x" TARGET_ABI_FMT_lx "\n", + info->start_brk); + fprintf(f, "end_code 0x" TARGET_ABI_FMT_lx "\n", + info->end_code); + fprintf(f, "start_code 0x" TARGET_ABI_FMT_lx "\n", + info->start_code); + fprintf(f, "start_data 0x" TARGET_ABI_FMT_lx "\n", + info->start_data); + fprintf(f, "end_data 0x" TARGET_ABI_FMT_lx "\n", + info->end_data); + fprintf(f, "start_stack 0x" TARGET_ABI_FMT_lx "\n", + info->start_stack); + fprintf(f, "brk 0x" TARGET_ABI_FMT_lx "\n", + info->brk); + fprintf(f, "entry 0x" TARGET_ABI_FMT_lx "\n", + info->entry); + fprintf(f, "argv_start 0x" TARGET_ABI_FMT_lx "\n", + info->arg_start); + fprintf(f, "env_start 0x" TARGET_ABI_FMT_lx "\n", + info->arg_end + (abi_ulong)sizeof(abi_ulong)); + fprintf(f, "auxv_start 0x" TARGET_ABI_FMT_lx "\n", + info->saved_auxv); + qemu_log_unlock(f); + } } target_set_brk(info->brk); diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 1a2556be2c..5ccf9e942e 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 9bb12a07ba..d5c1c7941d 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/mmap.c b/linux-user/mmap.c index c125031b90..48e1373796 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -494,7 +494,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, may need to truncate file maps at EOF and add extra anonymous pages up to the targets page boundary. */ - if ((qemu_real_host_page_size < qemu_host_page_size) && + if ((qemu_real_host_page_size() < qemu_host_page_size) && !(flags & MAP_ANONYMOUS)) { struct stat sb; @@ -630,7 +630,12 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, the_end: trace_target_mmap_complete(start); if (qemu_loglevel_mask(CPU_LOG_PAGE)) { - log_page_dump(__func__); + FILE *f = qemu_log_trylock(); + if (f) { + fprintf(f, "page layout changed following mmap\n"); + page_dump(f); + qemu_log_unlock(f); + } } tb_invalidate_phys_range(start, start + len); mmap_unlock(); diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c index 1e93ef34e6..da77ede76b 100644 --- a/linux-user/nios2/cpu_loop.c +++ b/linux-user/nios2/cpu_loop.c @@ -26,7 +26,6 @@ void cpu_loop(CPUNios2State *env) { CPUState *cs = env_cpu(env); - target_siginfo_t info; int trapnr, ret; for (;;) { @@ -39,6 +38,30 @@ void cpu_loop(CPUNios2State *env) /* just indicate that signals should be handled asap */ break; + case EXCP_DIV: + /* Match kernel's handle_diverror_c(). */ + env->pc -= 4; + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); + break; + + case EXCP_UNALIGN: + case EXCP_UNALIGND: + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, + env->ctrl[CR_BADADDR]); + break; + + case EXCP_ILLEGAL: + case EXCP_UNIMPL: + /* Match kernel's handle_illegal_c(). */ + env->pc -= 4; + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); + break; + case EXCP_SUPERI: + /* Match kernel's handle_supervisor_instr(). */ + env->pc -= 4; + force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc); + break; + case EXCP_TRAP: switch (env->error_code) { case 0: @@ -49,32 +72,41 @@ void cpu_loop(CPUNios2State *env) env->regs[7], env->regs[8], env->regs[9], 0, 0); - if (env->regs[2] == 0) { /* FIXME: syscall 0 workaround */ - ret = 0; + if (ret == -QEMU_ESIGRETURN) { + /* rt_sigreturn has set all state. */ + break; } - + if (ret == -QEMU_ERESTARTSYS) { + env->pc -= 4; + break; + } + /* + * See the code after translate_rc_and_ret: all negative + * values are errors (aided by userspace restricted to 2G), + * errno is returned positive in r2, and error indication + * is a boolean in r7. + */ env->regs[2] = abs(ret); - /* Return value is 0..4096 */ - env->regs[7] = ret > 0xfffff000u; - env->regs[R_PC] += 4; + env->regs[7] = ret < 0; break; case 1: qemu_log_mask(CPU_LOG_INT, "\nTrap 1\n"); - force_sig_fault(TARGET_SIGUSR1, 0, env->regs[R_PC]); + force_sig_fault(TARGET_SIGUSR1, 0, env->pc); break; case 2: qemu_log_mask(CPU_LOG_INT, "\nTrap 2\n"); - force_sig_fault(TARGET_SIGUSR2, 0, env->regs[R_PC]); + force_sig_fault(TARGET_SIGUSR2, 0, env->pc); break; case 31: qemu_log_mask(CPU_LOG_INT, "\nTrap 31\n"); - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[R_PC]); + /* Match kernel's breakpoint_c(). */ + env->pc -= 4; + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; default: qemu_log_mask(CPU_LOG_INT, "\nTrap %d\n", env->error_code); - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP, - env->regs[R_PC]); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP, env->pc); break; case 16: /* QEMU specific, for __kuser_cmpxchg */ @@ -99,27 +131,13 @@ void cpu_loop(CPUNios2State *env) o = env->regs[5]; n = env->regs[6]; env->regs[2] = qatomic_cmpxchg(h, o, n) - o; - env->regs[R_PC] += 4; } break; } break; case EXCP_DEBUG: - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case 0xaa: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* TODO: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->regs[R_PC]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; default: EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", @@ -133,28 +151,6 @@ void cpu_loop(CPUNios2State *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { - env->regs[0] = 0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - /* TODO: unsigned long orig_r2; */ - env->regs[R_RA] = regs->ra; - env->regs[R_FP] = regs->fp; env->regs[R_SP] = regs->sp; - env->regs[R_GP] = regs->gp; - env->regs[CR_ESTATUS] = regs->estatus; - env->regs[R_PC] = regs->ea; - /* TODO: unsigned long orig_r7; */ + env->pc = regs->ea; } diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c index 517cd39270..32b3dc99c6 100644 --- a/linux-user/nios2/signal.c +++ b/linux-user/nios2/signal.c @@ -73,12 +73,11 @@ static void rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) __put_user(env->regs[R_RA], &gregs[23]); __put_user(env->regs[R_FP], &gregs[24]); __put_user(env->regs[R_GP], &gregs[25]); - __put_user(env->regs[R_PC], &gregs[27]); + __put_user(env->pc, &gregs[27]); __put_user(env->regs[R_SP], &gregs[28]); } -static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc, - int *pr2) +static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc) { int temp; unsigned long *gregs = uc->tuc_mcontext.gregs; @@ -122,14 +121,12 @@ static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc, __get_user(env->regs[R_GP], &gregs[25]); /* Not really necessary no user settable bits */ __get_user(temp, &gregs[26]); - __get_user(env->regs[R_PC], &gregs[27]); + __get_user(env->pc, &gregs[27]); __get_user(env->regs[R_RA], &gregs[23]); __get_user(env->regs[R_SP], &gregs[28]); target_restore_altstack(&uc->tuc_stack, env); - - *pr2 = env->regs[2]; return 0; } @@ -180,25 +177,17 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->regs[4] = sig; env->regs[5] = frame_addr + offsetof(struct target_rt_sigframe, info); env->regs[6] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->regs[R_PC] = ka->_sa_handler; + env->pc = ka->_sa_handler; unlock_user_struct(frame, frame_addr, 1); } -long do_sigreturn(CPUNios2State *env) -{ - trace_user_do_sigreturn(env, 0); - qemu_log_mask(LOG_UNIMP, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - long do_rt_sigreturn(CPUNios2State *env) { /* Verify, can we follow the stack back */ abi_ulong frame_addr = env->regs[R_SP]; struct target_rt_sigframe *frame; sigset_t set; - int rval; if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { goto badframe; @@ -207,15 +196,15 @@ long do_rt_sigreturn(CPUNios2State *env) target_to_host_sigset(&set, &frame->uc.tuc_sigmask); set_sigmask(&set); - if (rt_restore_ucontext(env, &frame->uc, &rval)) { + if (rt_restore_ucontext(env, &frame->uc)) { goto badframe; } unlock_user_struct(frame, frame_addr, 0); - return rval; + return -QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -QEMU_ESIGRETURN; } diff --git a/linux-user/nios2/target_cpu.h b/linux-user/nios2/target_cpu.h index 2d2008f002..830b4c0741 100644 --- a/linux-user/nios2/target_cpu.h +++ b/linux-user/nios2/target_cpu.h @@ -27,6 +27,7 @@ static inline void cpu_clone_regs_child(CPUNios2State *env, target_ulong newsp, env->regs[R_SP] = newsp; } env->regs[R_RET0] = 0; + env->regs[7] = 0; } static inline void cpu_clone_regs_parent(CPUNios2State *env, unsigned flags) diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 7683bea064..a7aa586c8f 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index c5d809916f..02204ad8be 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "qemu/timer.h" #include "user-internals.h" @@ -181,7 +180,8 @@ void cpu_loop(CPUPPCState *env) } break; case POWERPC_EXCP_TRAP: - cpu_abort(cs, "Tried to call a TRAP\n"); + si_signo = TARGET_SIGTRAP; + si_code = TARGET_TRAP_BRKPT; break; default: /* Should not happen ! */ diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index ec0b9c0df3..07729c1653 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -215,8 +215,7 @@ static target_ulong get_sigframe(struct target_sigaction *ka, return (oldsp - frame_size) & ~0xFUL; } -#if ((defined(TARGET_WORDS_BIGENDIAN) && defined(HOST_WORDS_BIGENDIAN)) || \ - (!defined(HOST_WORDS_BIGENDIAN) && !defined(TARGET_WORDS_BIGENDIAN))) +#if TARGET_BIG_ENDIAN == HOST_BIG_ENDIAN #define PPC_VEC_HI 0 #define PPC_VEC_LO 1 #else @@ -229,7 +228,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) { target_ulong msr = env->msr; int i; - target_ulong ccr = 0; + uint32_t ccr = 0; /* In general, the kernel attempts to be intelligent about what it needs to save for Altivec/FP/SPE registers. We don't care that @@ -543,7 +542,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->nip = (target_ulong) ka->_sa_handler; #endif -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN /* Signal handlers are entered in big-endian mode. */ ppc_store_msr(env, env->msr & ~(1ull << MSR_LE)); #else diff --git a/linux-user/ppc/target_syscall.h b/linux-user/ppc/target_syscall.h index 7df9118937..77b36d0b46 100644 --- a/linux-user/ppc/target_syscall.h +++ b/linux-user/ppc/target_syscall.h @@ -59,7 +59,7 @@ struct target_revectored_struct { */ #if defined(TARGET_PPC64) -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN #define UNAME_MACHINE "ppc64" #else #define UNAME_MACHINE "ppc64le" diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 98dfbf2096..46550f5e21 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -236,7 +236,7 @@ static inline bool access_ok(CPUState *cpu, int type, } while (0) -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN # define __put_user(x, hptr) __put_user_e(x, hptr, be) # define __get_user(x, hptr) __get_user_e(x, hptr, be) #else diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 26d446f323..29084c1421 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu.h" #include "user-internals.h" diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 7901dfe6f5..285bc60071 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index 1bd313cb19..c805f9db11 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/sh4/termbits.h b/linux-user/sh4/termbits.h index f91b5c51cf..eeabd2d7a9 100644 --- a/linux-user/sh4/termbits.h +++ b/linux-user/sh4/termbits.h @@ -273,7 +273,7 @@ ebugging only */ #define TARGET_TIOCSERGETLSR TARGET_IOR('T', 89, unsigned int) /* 0x5459 */ /* Get line sta tus register */ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ -# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ +# define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ #define TARGET_TIOCSERGETMULTI TARGET_IOR('T', 90, int) /* 0x545A */ /* Get multiport config */ #define TARGET_TIOCSERSETMULTI TARGET_IOW('T', 91, int) /* 0x545B diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 2113165a75..6a7e4a93fc 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -92,4 +92,30 @@ abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, */ int block_signals(void); /* Returns non zero if signal pending */ +/** + * process_sigsuspend_mask: read and apply syscall-local signal mask + * + * Read the guest signal mask from @sigset, length @sigsize. + * Convert that to a host signal mask and save it to sigpending_mask. + * + * Return value: negative target errno, or zero; + * store &sigpending_mask into *pset on success. + */ +int process_sigsuspend_mask(sigset_t **pset, target_ulong sigset, + target_ulong sigsize); + +/** + * finish_sigsuspend_mask: finish a sigsuspend-like syscall + * + * Set in_sigsuspend if we need to use the modified sigset + * during process_pending_signals. + */ +static inline void finish_sigsuspend_mask(int ret) +{ + if (ret != -QEMU_ERESTARTSYS) { + TaskState *ts = (TaskState *)thread_cpu->opaque; + ts->in_sigsuspend = 1; + } +} + #endif diff --git a/linux-user/signal.c b/linux-user/signal.c index 27a0ff30e9..8d29bfaa6b 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -725,7 +725,8 @@ void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, } /* abort execution with signal */ -static void QEMU_NORETURN dump_core_and_abort(int target_sig) +static G_NORETURN +void dump_core_and_abort(int target_sig) { CPUState *cpu = thread_cpu; CPUArchState *env = cpu->env_ptr; @@ -999,7 +1000,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, oact->sa_mask = k->sa_mask; } if (act) { - /* FIXME: This is not threadsafe. */ __get_user(k->_sa_handler, &act->_sa_handler); __get_user(k->sa_flags, &act->sa_flags); #ifdef TARGET_ARCH_HAS_SA_RESTORER @@ -1149,7 +1149,6 @@ void process_pending_signals(CPUArchState *cpu_env) sigset_t *blocked_set; while (qatomic_read(&ts->signal_pending)) { - /* FIXME: This is not threadsafe. */ sigfillset(&set); sigprocmask(SIG_SETMASK, &set, 0); @@ -1201,3 +1200,26 @@ void process_pending_signals(CPUArchState *cpu_env) } ts->in_sigsuspend = 0; } + +int process_sigsuspend_mask(sigset_t **pset, target_ulong sigset, + target_ulong sigsize) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + sigset_t *host_set = &ts->sigsuspend_mask; + target_sigset_t *target_sigset; + + if (sigsize != sizeof(*target_sigset)) { + /* Like the kernel, we enforce correct size sigsets */ + return -TARGET_EINVAL; + } + + target_sigset = lock_user(VERIFY_READ, sigset, sigsize, 1); + if (!target_sigset) { + return -TARGET_EFAULT; + } + target_to_host_sigset(host_set, target_sigset); + unlock_user(target_sigset, sigset, 0); + + *pset = host_set; + return 0; +} diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index baf3d9ae01..434c90a55f 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu.h" #include "user-internals.h" #include "cpu_loop-common.h" diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 505840bf5b..2d850ca23b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1408,14 +1408,12 @@ static abi_long do_pselect6(abi_long arg1, abi_long arg2, abi_long arg3, * The 6th arg is actually two args smashed together, * so we cannot use the C library. */ - sigset_t set; struct { sigset_t *set; size_t size; } sig, *sig_ptr; abi_ulong arg_sigset, arg_sigsize, *arg7; - target_sigset_t *target_sigset; n = arg1; rfd_addr = arg2; @@ -1456,10 +1454,8 @@ static abi_long do_pselect6(abi_long arg1, abi_long arg2, abi_long arg3, } /* Extract the two packed args for the sigset */ + sig_ptr = NULL; if (arg6) { - sig_ptr = &sig; - sig.size = SIGSET_T_SIZE; - arg7 = lock_user(VERIFY_READ, arg6, sizeof(*arg7) * 2, 1); if (!arg7) { return -TARGET_EFAULT; @@ -1469,28 +1465,22 @@ static abi_long do_pselect6(abi_long arg1, abi_long arg2, abi_long arg3, unlock_user(arg7, arg6, 0); if (arg_sigset) { - sig.set = &set; - if (arg_sigsize != sizeof(*target_sigset)) { - /* Like the kernel, we enforce correct size sigsets */ - return -TARGET_EINVAL; + ret = process_sigsuspend_mask(&sig.set, arg_sigset, arg_sigsize); + if (ret != 0) { + return ret; } - target_sigset = lock_user(VERIFY_READ, arg_sigset, - sizeof(*target_sigset), 1); - if (!target_sigset) { - return -TARGET_EFAULT; - } - target_to_host_sigset(&set, target_sigset); - unlock_user(target_sigset, arg_sigset, 0); - } else { - sig.set = NULL; + sig_ptr = &sig; + sig.size = SIGSET_T_SIZE; } - } else { - sig_ptr = NULL; } ret = get_errno(safe_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, ts_ptr, sig_ptr)); + if (sig_ptr) { + finish_sigsuspend_mask(ret); + } + if (!is_error(ret)) { if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) { return -TARGET_EFAULT; @@ -1546,8 +1536,7 @@ static abi_long do_ppoll(abi_long arg1, abi_long arg2, abi_long arg3, } if (ppoll) { struct timespec _timeout_ts, *timeout_ts = &_timeout_ts; - target_sigset_t *target_set; - sigset_t _set, *set = &_set; + sigset_t *set = NULL; if (arg3) { if (time64) { @@ -1566,25 +1555,19 @@ static abi_long do_ppoll(abi_long arg1, abi_long arg2, abi_long arg3, } if (arg4) { - if (arg5 != sizeof(target_sigset_t)) { + ret = process_sigsuspend_mask(&set, arg4, arg5); + if (ret != 0) { unlock_user(target_pfd, arg1, 0); - return -TARGET_EINVAL; + return ret; } - - target_set = lock_user(VERIFY_READ, arg4, - sizeof(target_sigset_t), 1); - if (!target_set) { - unlock_user(target_pfd, arg1, 0); - return -TARGET_EFAULT; - } - target_to_host_sigset(set, target_set); - } else { - set = NULL; } ret = get_errno(safe_ppoll(pfd, nfds, timeout_ts, set, SIGSET_T_SIZE)); + if (set) { + finish_sigsuspend_mask(ret); + } if (!is_error(ret) && arg3) { if (time64) { if (host_to_target_timespec64(arg3, timeout_ts)) { @@ -1596,9 +1579,6 @@ static abi_long do_ppoll(abi_long arg1, abi_long arg2, abi_long arg3, } } } - if (arg4) { - unlock_user(target_set, arg4, 0); - } } else { struct timespec ts, *pts; @@ -5093,7 +5073,7 @@ do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp, target_size = thunk_type_size(arg_type, THUNK_TARGET); /* construct host copy of urb and metadata */ - lurb = g_try_malloc0(sizeof(struct live_urb)); + lurb = g_try_new0(struct live_urb, 1); if (!lurb) { return -TARGET_ENOMEM; } @@ -8188,7 +8168,7 @@ static int is_proc_myself(const char *filename, const char *entry) return 0; } -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) || \ +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN || \ defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA) static int is_proc(const char *filename, const char *entry) { @@ -8196,7 +8176,7 @@ static int is_proc(const char *filename, const char *entry) } #endif -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static int open_net_route(void *cpu_env, int fd) { FILE *fp; @@ -8282,7 +8262,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, { "stat", open_self_stat, is_proc_myself }, { "auxv", open_self_auxv, is_proc_myself }, { "cmdline", open_self_cmdline, is_proc_myself }, -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN { "/proc/net/route", open_net_route, is_proc }, #endif #if defined(TARGET_SPARC) || defined(TARGET_HPPA) @@ -9593,40 +9573,35 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_sigsuspend case TARGET_NR_sigsuspend: { - TaskState *ts = cpu->opaque; + sigset_t *set; + #if defined(TARGET_ALPHA) - abi_ulong mask = arg1; - target_to_host_old_sigset(&ts->sigsuspend_mask, &mask); + TaskState *ts = cpu->opaque; + /* target_to_host_old_sigset will bswap back */ + abi_ulong mask = tswapal(arg1); + set = &ts->sigsuspend_mask; + target_to_host_old_sigset(set, &mask); #else - if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) - return -TARGET_EFAULT; - target_to_host_old_sigset(&ts->sigsuspend_mask, p); - unlock_user(p, arg1, 0); -#endif - ret = get_errno(safe_rt_sigsuspend(&ts->sigsuspend_mask, - SIGSET_T_SIZE)); - if (ret != -QEMU_ERESTARTSYS) { - ts->in_sigsuspend = 1; + ret = process_sigsuspend_mask(&set, arg1, sizeof(target_sigset_t)); + if (ret != 0) { + return ret; } +#endif + ret = get_errno(safe_rt_sigsuspend(set, SIGSET_T_SIZE)); + finish_sigsuspend_mask(ret); } return ret; #endif case TARGET_NR_rt_sigsuspend: { - TaskState *ts = cpu->opaque; + sigset_t *set; - if (arg2 != sizeof(target_sigset_t)) { - return -TARGET_EINVAL; - } - if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) - return -TARGET_EFAULT; - target_to_host_sigset(&ts->sigsuspend_mask, p); - unlock_user(p, arg1, 0); - ret = get_errno(safe_rt_sigsuspend(&ts->sigsuspend_mask, - SIGSET_T_SIZE)); - if (ret != -QEMU_ERESTARTSYS) { - ts->in_sigsuspend = 1; + ret = process_sigsuspend_mask(&set, arg1, arg2); + if (ret != 0) { + return ret; } + ret = get_errno(safe_rt_sigsuspend(set, SIGSET_T_SIZE)); + finish_sigsuspend_mask(ret); } return ret; #ifdef TARGET_NR_rt_sigtimedwait @@ -12745,29 +12720,21 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_epoll_pwait) case TARGET_NR_epoll_pwait: { - target_sigset_t *target_set; - sigset_t _set, *set = &_set; + sigset_t *set = NULL; if (arg5) { - if (arg6 != sizeof(target_sigset_t)) { - ret = -TARGET_EINVAL; + ret = process_sigsuspend_mask(&set, arg5, arg6); + if (ret != 0) { break; } - - target_set = lock_user(VERIFY_READ, arg5, - sizeof(target_sigset_t), 1); - if (!target_set) { - ret = -TARGET_EFAULT; - break; - } - target_to_host_sigset(set, target_set); - unlock_user(target_set, arg5, 0); - } else { - set = NULL; } ret = get_errno(safe_epoll_pwait(epfd, ep, maxevents, timeout, set, SIGSET_T_SIZE)); + + if (set) { + finish_sigsuspend_mask(ret); + } break; } #endif diff --git a/linux-user/uname.c b/linux-user/uname.c index 1d82608c10..415fdf31b6 100644 --- a/linux-user/uname.c +++ b/linux-user/uname.c @@ -21,7 +21,6 @@ #include "qemu.h" #include "user-internals.h" -//#include "qemu-common.h" #include "uname.h" /* return highest utsname machine name for emulated instruction set @@ -41,7 +40,7 @@ const char *cpu_to_uname_machine(void *cpu_env) /* in theory, endianness is configurable on some ARM CPUs, but this isn't * used in user mode emulation */ -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN #define utsname_suffix "b" #else #define utsname_suffix "l" diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index f71f372829..96445e3633 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -112,16 +112,16 @@ static inline int is_error(abi_long ret) return (abi_ulong)ret >= (abi_ulong)(-4096); } -#if TARGET_ABI_BITS == 32 +#if (TARGET_ABI_BITS == 32) && !defined(TARGET_ABI_MIPSN32) static inline uint64_t target_offset64(uint32_t word0, uint32_t word1) { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN return ((uint64_t)word0 << 32) | word1; #else return ((uint64_t)word1 << 32) | word0; #endif } -#else /* TARGET_ABI_BITS == 32 */ +#else /* TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32) */ static inline uint64_t target_offset64(uint64_t word0, uint64_t word1) { return word0; @@ -136,7 +136,7 @@ static inline int regpairs_aligned(void *cpu_env, int num) { return ((((CPUARMState *)cpu_env)->eabi) == 1) ; } -#elif defined(TARGET_MIPS) && (TARGET_ABI_BITS == 32) +#elif defined(TARGET_MIPS) && defined(TARGET_ABI_MIPSO32) static inline int regpairs_aligned(void *cpu_env, int num) { return 1; } #elif defined(TARGET_PPC) && !defined(TARGET_PPC64) /* diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index 06d91a37ec..f5fb8b5cbe 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -130,7 +130,7 @@ static int setup_sigcontext(struct target_rt_sigframe *frame, static void install_sigtramp(uint8_t *tramp) { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ __put_user(0x22, &tramp[0]); __put_user(0x0a, &tramp[1]); diff --git a/linux-user/xtensa/target_structs.h b/linux-user/xtensa/target_structs.h index 9cde6844b8..cb1b3411cf 100644 --- a/linux-user/xtensa/target_structs.h +++ b/linux-user/xtensa/target_structs.h @@ -15,7 +15,7 @@ struct target_ipc_perm { struct target_semid64_ds { struct target_ipc_perm sem_perm; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN abi_ulong __unused1; abi_ulong sem_otime; abi_ulong __unused2; diff --git a/meson.build b/meson.build index bc3d499f56..91b021ab38 100644 --- a/meson.build +++ b/meson.build @@ -3,9 +3,9 @@ project('qemu', ['c'], meson_version: '>=0.59.3', 'b_staticpic=false', 'stdsplit=false'], version: files('VERSION')) -add_test_setup('quick', exclude_suites: ['block', 'slow', 'thorough'], is_default: true) -add_test_setup('slow', exclude_suites: ['block', 'thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) -add_test_setup('thorough', exclude_suites: ['block'], env: ['G_TEST_SLOW=1', 'SPEED=thorough']) +add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) +add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) +add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough']) not_found = dependency('', required: false) keyval = import('keyval') @@ -173,11 +173,13 @@ endif qemu_cflags = config_host['QEMU_CFLAGS'].split() qemu_cxxflags = config_host['QEMU_CXXFLAGS'].split() +qemu_objcflags = config_host['QEMU_OBJCFLAGS'].split() qemu_ldflags = config_host['QEMU_LDFLAGS'].split() if get_option('gprof') qemu_cflags += ['-p'] qemu_cxxflags += ['-p'] + qemu_objcflags += ['-p'] qemu_ldflags += ['-p'] endif @@ -216,8 +218,9 @@ if get_option('fuzzing') endif endif -add_global_arguments(qemu_cflags, native: false, language: ['c', 'objc']) +add_global_arguments(qemu_cflags, native: false, language: ['c']) add_global_arguments(qemu_cxxflags, native: false, language: ['cpp']) +add_global_arguments(qemu_objcflags, native: false, language: ['objc']) add_global_link_arguments(qemu_ldflags, native: false, language: ['c', 'cpp', 'objc']) if targetos == 'linux' @@ -445,15 +448,36 @@ glib = declare_dependency(compile_args: config_host['GLIB_CFLAGS'].split(), meson.override_dependency('glib-2.0', glib) gio = not_found -if 'CONFIG_GIO' in config_host - gio = declare_dependency(compile_args: config_host['GIO_CFLAGS'].split(), - link_args: config_host['GIO_LIBS'].split(), - version: config_host['GLIB_VERSION']) +gdbus_codegen = not_found +if not get_option('gio').auto() or have_system + gio = dependency('gio-2.0', required: get_option('gio'), + method: 'pkg-config', kwargs: static_kwargs) + if gio.found() and not cc.links(''' + #include + int main(void) + { + g_dbus_proxy_new_sync(0, 0, 0, 0, 0, 0, 0, 0); + return 0; + }''', dependencies: [glib, gio]) + if get_option('gio').enabled() + error('The installed libgio is broken for static linking') + endif + gio = not_found + endif + if gio.found() + gdbus_codegen = find_program(gio.get_variable('gdbus_codegen'), + required: get_option('gio')) + gio_unix = dependency('gio-unix-2.0', required: get_option('gio'), + method: 'pkg-config', kwargs: static_kwargs) + gio = declare_dependency(dependencies: [gio, gio_unix], + version: gio.version()) + endif endif + lttng = not_found if 'ust' in get_option('trace_backends') - lttng = dependency('lttng-ust', required: true, method: 'pkg-config', - kwargs: static_kwargs) + lttng = dependency('lttng-ust', required: true, version: '>= 2.1', + method: 'pkg-config', kwargs: static_kwargs) endif pixman = not_found if have_system or have_tools @@ -997,9 +1021,14 @@ if not get_option('coreaudio').auto() or (targetos == 'darwin' and have_system) endif opengl = not_found -if 'CONFIG_OPENGL' in config_host - opengl = declare_dependency(compile_args: config_host['OPENGL_CFLAGS'].split(), - link_args: config_host['OPENGL_LIBS'].split()) +if not get_option('opengl').auto() or have_system or have_vhost_user_gpu + epoxy = dependency('epoxy', method: 'pkg-config', + required: get_option('opengl'), kwargs: static_kwargs) + if cc.has_header('epoxy/egl.h', dependencies: epoxy) + opengl = epoxy + elif get_option('opengl').enabled() + error('epoxy/egl.h not found') + endif endif gbm = not_found if (have_system or have_tools) and (virgl.found() or opengl.found()) @@ -1112,14 +1141,16 @@ if gtkx11.found() x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(), kwargs: static_kwargs) endif -vnc = not_found png = not_found +if get_option('png').allowed() and have_system + png = dependency('libpng', required: get_option('png'), + method: 'pkg-config', kwargs: static_kwargs) +endif +vnc = not_found jpeg = not_found sasl = not_found if get_option('vnc').allowed() and have_system vnc = declare_dependency() # dummy dependency - png = dependency('libpng', required: get_option('vnc_png'), - method: 'pkg-config', kwargs: static_kwargs) jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'), method: 'pkg-config', kwargs: static_kwargs) sasl = cc.find_library('sasl2', has_headers: ['sasl/sasl.h'], @@ -1209,9 +1240,22 @@ if numa.found() and not cc.links(''' endif rdma = not_found -if 'CONFIG_RDMA' in config_host - rdma = declare_dependency(link_args: config_host['RDMA_LIBS'].split()) +if not get_option('rdma').auto() or have_system + libumad = cc.find_library('ibumad', required: get_option('rdma')) + rdma_libs = [cc.find_library('rdmacm', has_headers: ['rdma/rdma_cma.h'], + required: get_option('rdma'), + kwargs: static_kwargs), + cc.find_library('ibverbs', required: get_option('rdma'), + kwargs: static_kwargs), + libumad] + rdma = declare_dependency(dependencies: rdma_libs) + foreach lib: rdma_libs + if not lib.found() + rdma = not_found + endif + endforeach endif + xen = not_found if 'CONFIG_XEN_BACKEND' in config_host xen = declare_dependency(compile_args: config_host['XEN_CFLAGS'].split(), @@ -1306,6 +1350,18 @@ statx_test = gnu_source_prefix + ''' has_statx = cc.links(statx_test) +# Check whether statx() provides mount ID information + +statx_mnt_id_test = gnu_source_prefix + ''' + #include + int main(void) { + struct statx statxbuf; + statx(0, "", 0, STATX_BASIC_STATS | STATX_MNT_ID, &statxbuf); + return statxbuf.stx_mnt_id; + }''' + +has_statx_mnt_id = cc.links(statx_mnt_id_test) + have_vhost_user_blk_server = get_option('vhost_user_blk_server') \ .require(targetos == 'linux', error_message: 'vhost_user_blk_server requires linux') \ @@ -1445,19 +1501,21 @@ dbus_display = get_option('dbus_display') \ error_message: '-display dbus requires glib>=2.64') \ .require(enable_modules, error_message: '-display dbus requires --enable-modules') \ - .require(config_host.has_key('GDBUS_CODEGEN'), + .require(gdbus_codegen.found(), error_message: '-display dbus requires gdbus-codegen') \ .allowed() have_virtfs = get_option('virtfs') \ - .require(targetos == 'linux', - error_message: 'virtio-9p (virtfs) requires Linux') \ - .require(libattr.found() and libcap_ng.found(), - error_message: 'virtio-9p (virtfs) requires libcap-ng-devel and libattr-devel') \ + .require(targetos == 'linux' or targetos == 'darwin', + error_message: 'virtio-9p (virtfs) requires Linux or macOS') \ + .require(targetos == 'linux' or cc.has_function('pthread_fchdir_np'), + error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \ + .require(targetos == 'darwin' or (libattr.found() and libcap_ng.found()), + error_message: 'virtio-9p (virtfs) on Linux requires libcap-ng-devel and libattr-devel') \ .disable_auto_if(not have_tools and not have_system) \ .allowed() -have_virtfs_proxy_helper = have_virtfs and have_tools +have_virtfs_proxy_helper = targetos != 'darwin' and have_virtfs and have_tools foreach k : get_option('trace_backends') config_host_data.set('CONFIG_TRACE_' + k.to_upper(), true) @@ -1492,6 +1550,11 @@ endif config_host_data.set('HOST_' + host_arch.to_upper(), 1) +if get_option('module_upgrades') and not enable_modules + error('Cannot enable module-upgrades as modules are not enabled') +endif +config_host_data.set('CONFIG_MODULE_UPGRADES', get_option('module_upgrades')) + config_host_data.set('CONFIG_ATTR', libattr.found()) config_host_data.set('CONFIG_BDRV_WHITELIST_TOOLS', get_option('block_drv_whitelist_in_tools')) config_host_data.set('CONFIG_BRLAPI', brlapi.found()) @@ -1505,6 +1568,7 @@ config_host_data.set('CONFIG_MPATH_NEW_API', mpathpersist_new_api) config_host_data.set('CONFIG_CURL', curl.found()) config_host_data.set('CONFIG_CURSES', curses.found()) config_host_data.set('CONFIG_GBM', gbm.found()) +config_host_data.set('CONFIG_GIO', gio.found()) config_host_data.set('CONFIG_GLUSTERFS', glusterfs.found()) if glusterfs.found() config_host_data.set('CONFIG_GLUSTERFS_XLATOR_OPT', glusterfs.version().version_compare('>=4')) @@ -1527,8 +1591,10 @@ config_host_data.set('CONFIG_LINUX_AIO', libaio.found()) config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found()) config_host_data.set('CONFIG_LIBPMEM', libpmem.found()) config_host_data.set('CONFIG_NUMA', numa.found()) +config_host_data.set('CONFIG_OPENGL', opengl.found()) config_host_data.set('CONFIG_PROFILER', get_option('profiler')) config_host_data.set('CONFIG_RBD', rbd.found()) +config_host_data.set('CONFIG_RDMA', rdma.found()) config_host_data.set('CONFIG_SDL', sdl.found()) config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) config_host_data.set('CONFIG_SECCOMP', seccomp.found()) @@ -1537,9 +1603,9 @@ config_host_data.set('CONFIG_TPM', have_tpm) config_host_data.set('CONFIG_USB_LIBUSB', libusb.found()) config_host_data.set('CONFIG_VDE', vde.found()) config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server) +config_host_data.set('CONFIG_PNG', png.found()) config_host_data.set('CONFIG_VNC', vnc.found()) config_host_data.set('CONFIG_VNC_JPEG', jpeg.found()) -config_host_data.set('CONFIG_VNC_PNG', png.found()) config_host_data.set('CONFIG_VNC_SASL', sasl.found()) config_host_data.set('CONFIG_VIRTFS', have_virtfs) config_host_data.set('CONFIG_VTE', vte.found()) @@ -1553,6 +1619,7 @@ config_host_data.set('CONFIG_NETTLE', nettle.found()) config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private') config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim) config_host_data.set('CONFIG_STATX', has_statx) +config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id) config_host_data.set('CONFIG_ZSTD', zstd.found()) config_host_data.set('CONFIG_FUSE', fuse.found()) config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found()) @@ -1574,7 +1641,6 @@ config_host_data.set('QEMU_VERSION_MICRO', meson.project_version().split('.')[2] config_host_data.set_quoted('CONFIG_HOST_DSOSUF', host_dsosuf) config_host_data.set('HAVE_HOST_BLOCK_DEVICE', have_host_block_device) -config_host_data.set('HOST_WORDS_BIGENDIAN', host_machine.endian() == 'big') have_coroutine_pool = get_option('coroutine_pool') if get_option('debug_stack_usage') and have_coroutine_pool @@ -1606,23 +1672,29 @@ config_host_data.set('CONFIG_CLOCK_ADJTIME', cc.has_function('clock_adjtime')) config_host_data.set('CONFIG_DUP3', cc.has_function('dup3')) config_host_data.set('CONFIG_FALLOCATE', cc.has_function('fallocate')) config_host_data.set('CONFIG_POSIX_FALLOCATE', cc.has_function('posix_fallocate')) -config_host_data.set('CONFIG_POSIX_MEMALIGN', cc.has_function('posix_memalign')) +# Note that we need to specify prefix: here to avoid incorrectly +# thinking that Windows has posix_memalign() +config_host_data.set('CONFIG_POSIX_MEMALIGN', cc.has_function('posix_memalign', prefix: '#include ')) +config_host_data.set('CONFIG_ALIGNED_MALLOC', cc.has_function('_aligned_malloc')) +config_host_data.set('CONFIG_VALLOC', cc.has_function('valloc')) +config_host_data.set('CONFIG_MEMALIGN', cc.has_function('memalign')) config_host_data.set('CONFIG_PPOLL', cc.has_function('ppoll')) config_host_data.set('CONFIG_PREADV', cc.has_function('preadv', prefix: '#include ')) -config_host_data.set('CONFIG_SEM_TIMEDWAIT', cc.has_function('sem_timedwait', dependencies: threads)) +config_host_data.set('CONFIG_PTHREAD_FCHDIR_NP', cc.has_function('pthread_fchdir_np')) config_host_data.set('CONFIG_SENDFILE', cc.has_function('sendfile')) config_host_data.set('CONFIG_SETNS', cc.has_function('setns') and cc.has_function('unshare')) config_host_data.set('CONFIG_SYNCFS', cc.has_function('syncfs')) config_host_data.set('CONFIG_SYNC_FILE_RANGE', cc.has_function('sync_file_range')) config_host_data.set('CONFIG_TIMERFD', cc.has_function('timerfd_create')) config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range')) +config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs')) config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util)) config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul')) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) if rdma.found() config_host_data.set('HAVE_IBV_ADVISE_MR', cc.has_function('ibv_advise_mr', - args: config_host['RDMA_LIBS'].split(), + dependencies: rdma, prefix: '#include ')) endif @@ -1631,8 +1703,6 @@ config_host_data.set('CONFIG_BYTESWAP_H', cc.has_header_symbol('byteswap.h', 'bswap_32')) config_host_data.set('CONFIG_EPOLL_CREATE1', cc.has_header_symbol('sys/epoll.h', 'epoll_create1')) -config_host_data.set('CONFIG_HAS_ENVIRON', - cc.has_header_symbol('unistd.h', 'environ', prefix: gnu_source_prefix)) config_host_data.set('CONFIG_FALLOCATE_PUNCH_HOLE', cc.has_header_symbol('linux/falloc.h', 'FALLOC_FL_PUNCH_HOLE') and cc.has_header_symbol('linux/falloc.h', 'FALLOC_FL_KEEP_SIZE')) @@ -1691,11 +1761,30 @@ config_host_data.set('CONFIG_FDATASYNC', cc.links(gnu_source_prefix + ''' #error Not supported #endif }''')) -config_host_data.set('CONFIG_MADVISE', cc.links(gnu_source_prefix + ''' + +has_madvise = cc.links(gnu_source_prefix + ''' #include #include #include - int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }''')) + int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }''') +missing_madvise_proto = false +if has_madvise + # Some platforms (illumos and Solaris before Solaris 11) provide madvise() + # but forget to prototype it. In this case, has_madvise will be true (the + # test program links despite a compile warning). To detect the + # missing-prototype case, we try again with a definitely-bogus prototype. + # This will only compile if the system headers don't provide the prototype; + # otherwise the conflicting prototypes will cause a compiler error. + missing_madvise_proto = cc.links(gnu_source_prefix + ''' + #include + #include + #include + extern int madvise(int); + int main(void) { return madvise(0); }''') +endif +config_host_data.set('CONFIG_MADVISE', has_madvise) +config_host_data.set('HAVE_MADVISE_WITHOUT_PROTOTYPE', missing_madvise_proto) + config_host_data.set('CONFIG_MEMFD', cc.links(gnu_source_prefix + ''' #include int main(void) { return memfd_create("foo", MFD_ALLOW_SEALING); }''')) @@ -1741,6 +1830,17 @@ config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(gnu_source_pre pthread_create(&thread, 0, f, 0); return 0; }''', dependencies: threads)) +config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_prefix + ''' + #include + #include + + int main(void) + { + pthread_condattr_t attr + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + return 0; + }''', dependencies: threads)) config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' #include @@ -1810,21 +1910,57 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' return printf("%zu", SIZE_MAX); }''', args: ['-Werror'])) -# See if 64-bit atomic operations are supported. -# Note that without __atomic builtins, we can only -# assume atomic loads/stores max at pointer size. -config_host_data.set('CONFIG_ATOMIC64', cc.links(''' +atomic_test = ''' #include int main(void) { - uint64_t x = 0, y = 0; + @0@ x = 0, y = 0; y = __atomic_load_n(&x, __ATOMIC_RELAXED); __atomic_store_n(&x, y, __ATOMIC_RELAXED); __atomic_compare_exchange_n(&x, &y, x, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); __atomic_exchange_n(&x, y, __ATOMIC_RELAXED); __atomic_fetch_add(&x, y, __ATOMIC_RELAXED); return 0; - }''')) + }''' + +# See if 64-bit atomic operations are supported. +# Note that without __atomic builtins, we can only +# assume atomic loads/stores max at pointer size. +config_host_data.set('CONFIG_ATOMIC64', cc.links(atomic_test.format('uint64_t'))) + +has_int128 = cc.links(''' + __int128_t a; + __uint128_t b; + int main (void) { + a = a + b; + b = a * b; + a = a * a; + return 0; + }''') + +config_host_data.set('CONFIG_INT128', has_int128) + +if has_int128 + # "do we have 128-bit atomics which are handled inline and specifically not + # via libatomic". The reason we can't use libatomic is documented in the + # comment starting "GCC is a house divided" in include/qemu/atomic128.h. + has_atomic128 = cc.links(atomic_test.format('unsigned __int128')) + + config_host_data.set('CONFIG_ATOMIC128', has_atomic128) + + if not has_atomic128 + has_cmpxchg128 = cc.links(''' + int main(void) + { + unsigned __int128 x = 0, y = 0; + __sync_val_compare_and_swap_16(&x, y, x); + return 0; + } + ''') + + config_host_data.set('CONFIG_CMPXCHG128', has_cmpxchg128) + endif +endif config_host_data.set('CONFIG_GETAUXVAL', cc.links(gnu_source_prefix + ''' #include @@ -1832,6 +1968,32 @@ config_host_data.set('CONFIG_GETAUXVAL', cc.links(gnu_source_prefix + ''' return getauxval(AT_HWCAP) == 0; }''')) +config_host_data.set('CONFIG_USBFS', have_linux_user and cc.compiles(''' + #include + + #ifndef USBDEVFS_GET_CAPABILITIES + #error "USBDEVFS_GET_CAPABILITIES undefined" + #endif + + #ifndef USBDEVFS_DISCONNECT_CLAIM + #error "USBDEVFS_DISCONNECT_CLAIM undefined" + #endif + + int main(void) { return 0; }''')) + +have_keyring = get_option('keyring') \ + .require(targetos == 'linux', error_message: 'keyring is only available on Linux') \ + .require(cc.compiles(''' + #include + #include + #include + #include + #include + int main(void) { + return syscall(__NR_keyctl, KEYCTL_READ, 0, NULL, NULL, 0); + }'''), error_message: 'keyctl syscall not available on this system').allowed() +config_host_data.set('CONFIG_SECRET_KEYRING', have_keyring) + have_cpuid_h = cc.links(''' #include int main(void) { @@ -1878,6 +2040,36 @@ config_host_data.set('CONFIG_AVX512F_OPT', get_option('avx512f') \ int main(int argc, char *argv[]) { return bar(argv[0]); } '''), error_message: 'AVX512F not available').allowed()) +have_pvrdma = get_option('pvrdma') \ + .require(rdma.found(), error_message: 'PVRDMA requires OpenFabrics libraries') \ + .require(cc.compiles(''' + int main(void) + { + char buf = 0; + void *addr = &buf; + addr = mremap(addr, 0, 1, MREMAP_MAYMOVE | MREMAP_FIXED); + + return 0; + }'''), error_message: 'PVRDMA requires mremap').allowed() + +if have_pvrdma + config_host_data.set('LEGACY_RDMA_REG_MR', not cc.compiles(''' + #include + int main(void) + { + struct ibv_mr *mr; + struct ibv_pd *pd = NULL; + size_t length = 10; + uint64_t iova = 0; + int access = 0; + void *addr = NULL; + + mr = ibv_reg_mr_iova(pd, addr, length, iova, access); + ibv_dereg_mr(mr); + return 0; + }''')) +endif + if get_option('membarrier').disabled() have_membarrier = false elif targetos == 'windows' @@ -1912,49 +2104,21 @@ have_afalg = get_option('crypto_afalg') \ '''), error_message: 'AF_ALG requested but could not be detected').allowed() config_host_data.set('CONFIG_AF_ALG', have_afalg) -config_host_data.set('CONFIG_AF_VSOCK', cc.compiles(gnu_source_prefix + ''' - #include - #include - #include - #if !defined(AF_VSOCK) - # error missing AF_VSOCK flag - #endif - #include - int main(void) { - int sock, ret; - struct sockaddr_vm svm; - socklen_t len = sizeof(svm); - sock = socket(AF_VSOCK, SOCK_STREAM, 0); - ret = getpeername(sock, (struct sockaddr *)&svm, &len); - if ((ret == -1) && (errno == ENOTCONN)) { - return 0; - } - return -1; - }''')) +config_host_data.set('CONFIG_AF_VSOCK', cc.has_header_symbol( + 'linux/vm_sockets.h', 'AF_VSOCK', + prefix: '#include ', +)) have_vss = false +have_vss_sdk = false # old xp/2003 SDK if targetos == 'windows' and link_language == 'cpp' have_vss = cxx.compiles(''' #define __MIDL_user_allocate_free_DEFINED__ - #include + #include int main(void) { return VSS_CTX_BACKUP; }''') + have_vss_sdk = cxx.has_header('vscoordint.h') endif - -have_ntddscsi = false -if targetos == 'windows' - have_ntddscsi = cc.compiles(''' - #include - #include - int main(void) { - #if !defined(IOCTL_SCSI_GET_ADDRESS) - #error Missing required ioctl definitions - #endif - SCSI_ADDRESS addr = { .Lun = 0, .TargetId = 0, .PathId = 0 }; - return addr.Lun; - } -''') -endif -config_host_data.set('HAVE_NTDDSCSI', have_ntddscsi) +config_host_data.set('HAVE_VSS_SDK', have_vss_sdk) ignored = ['CONFIG_QEMU_INTERP_PREFIX', # actually per-target 'HAVE_GDB_BIN'] @@ -1972,6 +2136,18 @@ foreach k, v: config_host endif endforeach +# Older versions of MinGW do not import _lock_file and _unlock_file properly. +# This was fixed for v6.0.0 with commit b48e3ac8969d. +if targetos == 'windows' + config_host_data.set('HAVE__LOCK_FILE', cc.links(''' + #include + int main(void) { + _lock_file(NULL); + _unlock_file(NULL); + return 0; + }''', name: '_lock_file and _unlock_file')) +endif + ######################## # Target configuration # ######################## @@ -2021,14 +2197,14 @@ host_kconfig = \ (have_tpm ? ['CONFIG_TPM=y'] : []) + \ (spice.found() ? ['CONFIG_SPICE=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ - ('CONFIG_OPENGL' in config_host ? ['CONFIG_OPENGL=y'] : []) + \ + (opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \ (x11.found() ? ['CONFIG_X11=y'] : []) + \ ('CONFIG_VHOST_USER' in config_host ? ['CONFIG_VHOST_USER=y'] : []) + \ ('CONFIG_VHOST_VDPA' in config_host ? ['CONFIG_VHOST_VDPA=y'] : []) + \ ('CONFIG_VHOST_KERNEL' in config_host ? ['CONFIG_VHOST_KERNEL=y'] : []) + \ (have_virtfs ? ['CONFIG_VIRTFS=y'] : []) + \ ('CONFIG_LINUX' in config_host ? ['CONFIG_LINUX=y'] : []) + \ - ('CONFIG_PVRDMA' in config_host ? ['CONFIG_PVRDMA=y'] : []) + \ + (have_pvrdma ? ['CONFIG_PVRDMA=y'] : []) + \ (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=y'] : []) ignored = [ 'TARGET_XML_FILES', 'TARGET_ABI_DIR', 'TARGET_ARCH' ] @@ -2072,8 +2248,6 @@ foreach target : target_dirs config_all += { sym: 'y' } if sym == 'CONFIG_TCG' and tcg_arch == 'tci' config_target += { 'CONFIG_TCG_INTERPRETER': 'y' } - elif sym == 'CONFIG_XEN' and have_xen_pci_passthrough - config_target += { 'CONFIG_XEN_PCI_PASSTHROUGH': 'y' } endif if target in modular_tcg config_target += { 'CONFIG_TCG_MODULAR': 'y' } @@ -2105,6 +2279,9 @@ foreach target : target_dirs if 'TARGET_ABI_DIR' not in config_target config_target += {'TARGET_ABI_DIR': config_target['TARGET_ARCH']} endif + if 'TARGET_BIG_ENDIAN' not in config_target + config_target += {'TARGET_BIG_ENDIAN': 'n'} + endif foreach k, v: disassemblers if host_arch.startswith(k) or config_target['TARGET_BASE_ARCH'].startswith(k) @@ -2129,6 +2306,8 @@ foreach target : target_dirs config_target_data.set_quoted(k, v) elif v == 'y' config_target_data.set(k, 1) + elif v == 'n' + config_target_data.set(k, 0) else config_target_data.set(k, v) endif @@ -2421,8 +2600,8 @@ if get_option('cfi') and slirp_opt == 'system' endif fdt = not_found -fdt_opt = get_option('fdt') if have_system + fdt_opt = get_option('fdt') if fdt_opt in ['enabled', 'auto', 'system'] have_internal = fs.exists(meson.current_source_dir() / 'dtc/libfdt/Makefile.libfdt') fdt = cc.find_library('fdt', kwargs: static_kwargs, @@ -2466,6 +2645,8 @@ if have_system fdt = declare_dependency(link_with: libfdt, include_directories: fdt_inc) endif +else + fdt_opt = 'disabled' endif if not fdt.found() and fdt_required.length() > 0 error('fdt not available but required by targets ' + ', '.join(fdt_required)) @@ -2695,6 +2876,7 @@ if have_system or have_user 'target/i386', 'target/i386/kvm', 'target/mips/tcg', + 'target/nios2', 'target/ppc', 'target/riscv', 'target/s390x', @@ -2704,7 +2886,7 @@ if have_system or have_user endif vhost_user = not_found -if 'CONFIG_VHOST_USER' in config_host +if targetos == 'linux' and 'CONFIG_VHOST_USER' in config_host libvhost_user = subproject('libvhost-user') vhost_user = libvhost_user.get_variable('vhost_user_dep') endif @@ -2800,7 +2982,7 @@ if get_option('b_lto') if get_option('cfi') pagevary_flags += '-fno-sanitize=cfi-icall' endif - pagevary = static_library('page-vary-common', sources: pagevary, + pagevary = static_library('page-vary-common', sources: pagevary + genh, c_args: pagevary_flags, pic: 'AS_SHARED_LIB' in config_host) pagevary = declare_dependency(link_with: pagevary) @@ -3020,7 +3202,7 @@ qmp = declare_dependency(link_whole: [libqmp]) libchardev = static_library('chardev', chardev_ss.sources() + genh, name_suffix: 'fa', - dependencies: [gnutls], + dependencies: chardev_ss.dependencies(), build_by_default: false, pic: 'AS_SHARED_LIB' in config_host) @@ -3066,6 +3248,10 @@ common_all = static_library('common', feature_to_c = find_program('scripts/feature_to_c.sh') +if targetos == 'darwin' + entitlement = find_program('scripts/entitlement.sh') +endif + emulators = {} foreach target : target_dirs config_target = config_target_mak[target] @@ -3243,7 +3429,6 @@ foreach target : target_dirs install_input += meson.current_source_dir() / entitlements endif - entitlement = find_program('scripts/entitlement.sh') emulators += {exe['name'] : custom_target(exe['name'], input: build_input, output: exe['name'], @@ -3434,7 +3619,7 @@ summary_info += {'block layer': have_block} summary_info += {'Install blobs': get_option('install_blobs')} summary_info += {'module support': config_host.has_key('CONFIG_MODULES')} if config_host.has_key('CONFIG_MODULES') - summary_info += {'alternative module path': config_host.has_key('CONFIG_MODULE_UPGRADES')} + summary_info += {'alternative module path': get_option('module_upgrades')} endif summary_info += {'fuzzing support': get_option('fuzzing')} if have_system @@ -3480,12 +3665,19 @@ if link_language == 'cpp' + ['-O' + get_option('optimization')] + (get_option('debug') ? ['-g'] : []))} endif +if targetos == 'darwin' + summary_info += {'OBJCFLAGS': ' '.join(get_option('objc_args') + + ['-O' + get_option('optimization')] + + (get_option('debug') ? ['-g'] : []))} +endif link_args = get_option(link_language + '_link_args') if link_args.length() > 0 summary_info += {'LDFLAGS': ' '.join(link_args)} endif -summary_info += {'QEMU_CFLAGS': config_host['QEMU_CFLAGS']} -summary_info += {'QEMU_LDFLAGS': config_host['QEMU_LDFLAGS']} +summary_info += {'QEMU_CFLAGS': ' '.join(qemu_cflags)} +summary_info += {'QEMU_CXXFLAGS': ' '.join(qemu_cxxflags)} +summary_info += {'QEMU_OBJCFLAGS': ' '.join(qemu_objcflags)} +summary_info += {'QEMU_LDFLAGS': ' '.join(qemu_ldflags)} summary_info += {'profiler': get_option('profiler')} summary_info += {'link-time optimization (LTO)': get_option('b_lto')} summary_info += {'PIE': get_option('b_pie')} @@ -3595,7 +3787,7 @@ if nettle.found() endif summary_info += {'AF_ALG support': have_afalg} summary_info += {'rng-none': get_option('rng_none')} -summary_info += {'Linux keyring': config_host.has_key('CONFIG_SECRET_KEYRING')} +summary_info += {'Linux keyring': have_keyring} summary(summary_info, bool_yn: true, section: 'Crypto') # Libraries @@ -3616,11 +3808,11 @@ summary_info += {'curses support': curses} summary_info += {'virgl support': virgl} summary_info += {'curl support': curl} summary_info += {'Multipath support': mpathpersist} +summary_info += {'PNG support': png} summary_info += {'VNC support': vnc} if vnc.found() summary_info += {'VNC SASL support': sasl} summary_info += {'VNC JPEG support': jpeg} - summary_info += {'VNC PNG support': png} endif if targetos not in ['darwin', 'haiku', 'windows'] summary_info += {'OSS support': oss} @@ -3641,8 +3833,8 @@ summary_info += {'l2tpv3 support': have_l2tpv3} summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} -summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')} -summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')} +summary_info += {'RDMA support': rdma} +summary_info += {'PVRDMA support': have_pvrdma} summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} @@ -3655,14 +3847,13 @@ summary_info += {'smartcard support': cacard} summary_info += {'U2F support': u2f} summary_info += {'libusb': libusb} summary_info += {'usb net redir': usbredir} -summary_info += {'OpenGL support': config_host.has_key('CONFIG_OPENGL')} +summary_info += {'OpenGL support (epoxy)': opengl} summary_info += {'GBM': gbm} summary_info += {'libiscsi support': libiscsi} summary_info += {'libnfs support': libnfs} if targetos == 'windows' if have_ga summary_info += {'QGA VSS support': have_qga_vss} - summary_info += {'QGA w32 disk info': have_ntddscsi} endif endif summary_info += {'seccomp support': seccomp} diff --git a/meson_options.txt b/meson_options.txt index 52b11cead4..af432a4ee6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -34,6 +34,8 @@ option('fuzzing', type : 'boolean', value: false, description: 'build fuzzing targets') option('gettext', type : 'feature', value : 'auto', description: 'Localization of the GTK+ user interface') +option('module_upgrades', type : 'boolean', value : false, + description: 'try to load modules from alternate paths for upgrades') option('install_blobs', type : 'boolean', value : true, description: 'install provided firmware blobs') option('sparse', type : 'feature', value : 'auto', @@ -90,6 +92,8 @@ option('avx2', type: 'feature', value: 'auto', description: 'AVX2 optimizations') option('avx512f', type: 'feature', value: 'disabled', description: 'AVX512F optimizations') +option('keyring', type: 'feature', value: 'auto', + description: 'Linux keyring support') option('attr', type : 'feature', value : 'auto', description: 'attr/xattr support') @@ -107,6 +111,8 @@ option('cocoa', type : 'feature', value : 'auto', description: 'Cocoa user interface (macOS only)') option('curl', type : 'feature', value : 'auto', description: 'CURL block device driver') +option('gio', type : 'feature', value : 'auto', + description: 'use libgio for D-Bus support') option('glusterfs', type : 'feature', value : 'auto', description: 'Glusterfs block device driver') option('libiscsi', type : 'feature', value : 'auto', @@ -149,6 +155,12 @@ option('lzo', type : 'feature', value : 'auto', description: 'lzo compression support') option('rbd', type : 'feature', value : 'auto', description: 'Ceph block device driver') +option('opengl', type : 'feature', value : 'auto', + description: 'OpenGL support') +option('rdma', type : 'feature', value : 'auto', + description: 'Enable RDMA-based migration') +option('pvrdma', type : 'feature', value : 'auto', + description: 'Enable PVRDMA support') option('gtk', type : 'feature', value : 'auto', description: 'GTK+ user interface') option('sdl', type : 'feature', value : 'auto', @@ -177,12 +189,12 @@ option('vde', type : 'feature', value : 'auto', description: 'vde network backend support') option('virglrenderer', type : 'feature', value : 'auto', description: 'virgl rendering support') +option('png', type : 'feature', value : 'auto', + description: 'PNG support with libpng') option('vnc', type : 'feature', value : 'auto', description: 'VNC server') option('vnc_jpeg', type : 'feature', value : 'auto', description: 'JPEG lossy compression for VNC server') -option('vnc_png', type : 'feature', value : 'auto', - description: 'PNG compression for VNC server') option('vnc_sasl', type : 'feature', value : 'auto', description: 'SASL authentication for VNC server') option('vte', type : 'feature', value : 'auto', diff --git a/migration/block.c b/migration/block.c index a950977855..077a413325 100644 --- a/migration/block.c +++ b/migration/block.c @@ -932,7 +932,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) return -EINVAL; } - blk_invalidate_cache(blk, &local_err); + blk_activate(blk, &local_err); if (local_err) { error_report_err(local_err); return -EINVAL; diff --git a/migration/channel.c b/migration/channel.c index c4fc000a1a..c6a8dcf1d7 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -96,6 +96,5 @@ void migration_channel_connect(MigrationState *s, } } migrate_fd_connect(s, error); - g_free(s->hostname); error_free(error); } diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index d65e744af9..aace12a787 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -91,7 +91,7 @@ static struct DirtyRateInfo *query_dirty_rate_info(void) { int i; int64_t dirty_rate = DirtyStat.dirty_rate; - struct DirtyRateInfo *info = g_malloc0(sizeof(DirtyRateInfo)); + struct DirtyRateInfo *info = g_new0(DirtyRateInfo, 1); DirtyRateVcpuList *head = NULL, **tail = &head; info->status = CalculatingState; @@ -112,7 +112,7 @@ static struct DirtyRateInfo *query_dirty_rate_info(void) info->sample_pages = 0; info->has_vcpu_dirty_rate = true; for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) { - DirtyRateVcpu *rate = g_malloc0(sizeof(DirtyRateVcpu)); + DirtyRateVcpu *rate = g_new0(DirtyRateVcpu, 1); rate->id = DirtyStat.dirty_ring.rates[i].id; rate->dirty_rate = DirtyStat.dirty_ring.rates[i].dirty_rate; QAPI_LIST_APPEND(tail, rate); diff --git a/migration/meson.build b/migration/meson.build index 8b5ca5c047..6880b61b10 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -27,7 +27,7 @@ softmmu_ss.add(files( 'tls.c', ), gnutls) -softmmu_ss.add(when: ['CONFIG_RDMA', rdma], if_true: files('rdma.c')) +softmmu_ss.add(when: rdma, if_true: files('rdma.c')) if get_option('live_block_migration').allowed() softmmu_ss.add(files('block.c')) endif diff --git a/migration/migration.c b/migration/migration.c index bcc385b94b..5a31b23bd6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -180,6 +180,18 @@ static int migration_maybe_pause(MigrationState *s, int new_state); static void migrate_fd_cancel(MigrationState *s); +static bool migrate_allow_multi_channels = true; + +void migrate_protocol_allow_multi_channels(bool allow) +{ + migrate_allow_multi_channels = allow; +} + +bool migrate_multi_channels_is_allowed(void) +{ + return migrate_allow_multi_channels; +} + static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) { uintptr_t a = (uintptr_t) ap, b = (uintptr_t) bp; @@ -267,6 +279,19 @@ MigrationIncomingState *migration_incoming_get_current(void) return current_incoming; } +void migration_incoming_transport_cleanup(MigrationIncomingState *mis) +{ + if (mis->socket_address_list) { + qapi_free_SocketAddressList(mis->socket_address_list); + mis->socket_address_list = NULL; + } + + if (mis->transport_cleanup) { + mis->transport_cleanup(mis->transport_data); + mis->transport_data = mis->transport_cleanup = NULL; + } +} + void migration_incoming_state_destroy(void) { struct MigrationIncomingState *mis = migration_incoming_get_current(); @@ -287,10 +312,8 @@ void migration_incoming_state_destroy(void) g_array_free(mis->postcopy_remote_fds, TRUE); mis->postcopy_remote_fds = NULL; } - if (mis->transport_cleanup) { - mis->transport_cleanup(mis->transport_data); - } + migration_incoming_transport_cleanup(mis); qemu_event_reset(&mis->main_thread_load_event); if (mis->page_requested) { @@ -298,11 +321,6 @@ void migration_incoming_state_destroy(void) mis->page_requested = NULL; } - if (mis->socket_address_list) { - qapi_free_SocketAddressList(mis->socket_address_list); - mis->socket_address_list = NULL; - } - yank_unregister_instance(MIGRATION_YANK_INSTANCE); } @@ -463,12 +481,12 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) { const char *p = NULL; - migrate_protocol_allow_multifd(false); /* reset it anyway */ + migrate_protocol_allow_multi_channels(false); /* reset it anyway */ qapi_event_send_migration(MIGRATION_STATUS_SETUP); if (strstart(uri, "tcp:", &p) || strstart(uri, "unix:", NULL) || strstart(uri, "vsock:", NULL)) { - migrate_protocol_allow_multifd(true); + migrate_protocol_allow_multi_channels(true); socket_start_incoming_migration(p ? p : uri, errp); #ifdef CONFIG_RDMA } else if (strstart(uri, "rdma:", &p)) { @@ -497,9 +515,9 @@ static void process_incoming_migration_bh(void *opaque) if (!migrate_late_block_activate() || (autostart && (!global_state_received() || global_state_get_runstate() == RUN_STATE_RUNNING))) { - /* Make sure all file formats flush their mutable metadata. + /* Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ - bdrv_invalidate_cache_all(&local_err); + bdrv_activate_all(&local_err); if (local_err) { error_report_err(local_err); local_err = NULL; @@ -585,8 +603,8 @@ static void process_incoming_migration_co(void *opaque) /* we get COLO info, and know if we are in COLO mode */ if (!ret && migration_incoming_colo_enabled()) { - /* Make sure all file formats flush their mutable metadata */ - bdrv_invalidate_cache_all(&local_err); + /* Make sure all file formats throw away their mutable metadata */ + bdrv_activate_all(&local_err); if (local_err) { error_report_err(local_err); goto fail; @@ -653,19 +671,20 @@ void migration_incoming_process(void) } /* Returns true if recovered from a paused migration, otherwise false */ -static bool postcopy_try_recover(QEMUFile *f) +static bool postcopy_try_recover(void) { MigrationIncomingState *mis = migration_incoming_get_current(); if (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { /* Resumed from a paused postcopy migration */ - mis->from_src_file = f; + /* This should be set already in migration_incoming_setup() */ + assert(mis->from_src_file); /* Postcopy has standalone thread to do vm load */ - qemu_file_set_blocking(f, true); + qemu_file_set_blocking(mis->from_src_file, true); /* Re-configure the return path */ - mis->to_src_file = qemu_file_get_return_path(f); + mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_PAUSED, MIGRATION_STATUS_POSTCOPY_RECOVER); @@ -686,11 +705,10 @@ static bool postcopy_try_recover(QEMUFile *f) void migration_fd_process_incoming(QEMUFile *f, Error **errp) { - if (postcopy_try_recover(f)) { + if (!migration_incoming_setup(f, errp)) { return; } - - if (!migration_incoming_setup(f, errp)) { + if (postcopy_try_recover()) { return; } migration_incoming_process(); @@ -706,11 +724,6 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) /* The first connection (multifd may have multiple) */ QEMUFile *f = qemu_fopen_channel_input(ioc); - /* If it's a recovery, we're done */ - if (postcopy_try_recover(f)) { - return; - } - if (!migration_incoming_setup(f, errp)) { return; } @@ -731,6 +744,10 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) } if (start_migration) { + /* If it's a recovery, we're done */ + if (postcopy_try_recover()) { + return; + } migration_incoming_process(); } } @@ -1067,6 +1084,7 @@ static void populate_disk_info(MigrationInfo *info) static void fill_source_migration_info(MigrationInfo *info) { MigrationState *s = migrate_get_current(); + int state = qatomic_read(&s->state); GSList *cur_blocker = migration_blockers; info->blocked_reasons = NULL; @@ -1086,7 +1104,7 @@ static void fill_source_migration_info(MigrationInfo *info) } info->has_blocked_reasons = info->blocked_reasons != NULL; - switch (s->state) { + switch (state) { case MIGRATION_STATUS_NONE: /* no migration has happened ever */ /* do not overwrite destination migration status */ @@ -1131,7 +1149,7 @@ static void fill_source_migration_info(MigrationInfo *info) info->has_status = true; break; } - info->status = s->state; + info->status = state; } typedef enum WriteTrackingSupport { @@ -1255,7 +1273,7 @@ static bool migrate_caps_check(bool *cap_list, /* incoming side only */ if (runstate_check(RUN_STATE_INMIGRATE) && - !migrate_multifd_is_allowed() && + !migrate_multi_channels_is_allowed() && cap_list[MIGRATION_CAPABILITY_MULTIFD]) { error_setg(errp, "multifd is not supported by current protocol"); return false; @@ -1803,6 +1821,9 @@ static void migrate_fd_cleanup(MigrationState *s) qemu_bh_delete(s->cleanup_bh); s->cleanup_bh = NULL; + g_free(s->hostname); + s->hostname = NULL; + qemu_savevm_state_cleanup(); if (s->to_dst_file) { @@ -1926,7 +1947,7 @@ static void migrate_fd_cancel(MigrationState *s) if (s->state == MIGRATION_STATUS_CANCELLING && s->block_inactive) { Error *local_err = NULL; - bdrv_invalidate_cache_all(&local_err); + bdrv_activate_all(&local_err); if (local_err) { error_report_err(local_err); } else { @@ -2142,11 +2163,8 @@ void qmp_migrate_recover(const char *uri, Error **errp) return; } - if (qatomic_cmpxchg(&mis->postcopy_recover_triggered, - false, true) == true) { - error_setg(errp, "Migrate recovery is triggered already"); - return; - } + /* If there's an existing transport, release it */ + migration_incoming_transport_cleanup(mis); /* * Note that this call will never start a real migration; it will @@ -2154,12 +2172,6 @@ void qmp_migrate_recover(const char *uri, Error **errp) * to continue using that newly established channel. */ qemu_start_incoming_migration(uri, errp); - - /* Safe to dereference with the assert above */ - if (*errp) { - /* Reset the flag so user could still retry */ - qatomic_set(&mis->postcopy_recover_triggered, false); - } } void qmp_migrate_pause(Error **errp) @@ -2313,11 +2325,11 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, } } - migrate_protocol_allow_multifd(false); + migrate_protocol_allow_multi_channels(false); if (strstart(uri, "tcp:", &p) || strstart(uri, "unix:", NULL) || strstart(uri, "vsock:", NULL)) { - migrate_protocol_allow_multifd(true); + migrate_protocol_allow_multi_channels(true); socket_start_outgoing_migration(s, p ? p : uri, &local_err); #ifdef CONFIG_RDMA } else if (strstart(uri, "rdma:", &p)) { @@ -2646,7 +2658,7 @@ static struct rp_cmd_args { static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, ram_addr_t start, size_t len) { - long our_host_ps = qemu_real_host_page_size; + long our_host_ps = qemu_real_host_page_size(); trace_migrate_handle_rp_req_pages(rbname, start, len); @@ -2865,7 +2877,7 @@ retry: out: res = qemu_file_get_error(rp); if (res) { - if (res == -EIO && migration_in_postcopy()) { + if (res && migration_in_postcopy()) { /* * Maybe there is something we can do: it looks like a * network down issue, and we pause for a recovery. @@ -3105,7 +3117,7 @@ fail: */ Error *local_err = NULL; - bdrv_invalidate_cache_all(&local_err); + bdrv_activate_all(&local_err); if (local_err) { error_report_err(local_err); } @@ -3250,7 +3262,7 @@ fail_invalidate: Error *local_err = NULL; qemu_mutex_lock_iothread(); - bdrv_invalidate_cache_all(&local_err); + bdrv_activate_all(&local_err); if (local_err) { error_report_err(local_err); } else { @@ -3466,7 +3478,7 @@ static MigThrError migration_detect_error(MigrationState *s) error_free(local_error); } - if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) { + if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret) { /* * For postcopy, we allow the network to be down for a * while. After that, it can be continued by a diff --git a/migration/migration.h b/migration/migration.h index 8130b703eb..a863032b71 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -45,14 +45,37 @@ struct PostcopyBlocktimeContext; */ #define CLEAR_BITMAP_SHIFT_MAX 31 +/* This is an abstraction of a "temp huge page" for postcopy's purpose */ +typedef struct { + /* + * This points to a temporary huge page as a buffer for UFFDIO_COPY. It's + * mmap()ed and needs to be freed when cleanup. + */ + void *tmp_huge_page; + /* + * This points to the host page we're going to install for this temp page. + * It tells us after we've received the whole page, where we should put it. + */ + void *host_addr; + /* Number of small pages copied (in size of TARGET_PAGE_SIZE) */ + unsigned int target_pages; + /* Whether this page contains all zeros */ + bool all_zero; +} PostcopyTmpPage; + /* State for the incoming migration */ struct MigrationIncomingState { QEMUFile *from_src_file; - + /* Previously received RAM's RAMBlock pointer */ + RAMBlock *last_recv_block; /* A hook to allow cleanup at the end of incoming migration */ void *transport_data; void (*transport_cleanup)(void *data); - + /* + * Used to sync thread creations. Note that we can't create threads in + * parallel with this sem. + */ + QemuSemaphore thread_sync_sem; /* * Free at the start of the main state load, set as the main thread finishes * loading state. @@ -65,13 +88,11 @@ struct MigrationIncomingState { size_t largest_page_size; bool have_fault_thread; QemuThread fault_thread; - QemuSemaphore fault_thread_sem; /* Set this when we want the fault thread to quit */ bool fault_thread_quit; bool have_listen_thread; QemuThread listen_thread; - QemuSemaphore listen_thread_sem; /* For the kernel to send us notifications */ int userfault_fd; @@ -81,7 +102,22 @@ struct MigrationIncomingState { QemuMutex rp_mutex; /* We send replies from multiple threads */ /* RAMBlock of last request sent to source */ RAMBlock *last_rb; - void *postcopy_tmp_page; + /* + * Number of postcopy channels including the default precopy channel, so + * vanilla postcopy will only contain one channel which contain both + * precopy and postcopy streams. + * + * This is calculated when the src requests to enable postcopy but before + * it starts. Its value can depend on e.g. whether postcopy preemption is + * enabled. + */ + unsigned int postcopy_channels; + /* + * An array of temp host huge pages to be used, one for each postcopy + * channel. + */ + PostcopyTmpPage *postcopy_tmp_pages; + /* This is shared for all postcopy channels */ void *postcopy_tmp_zero_page; /* PostCopyFD's for external userfaultfds & handlers of shared memory */ GArray *postcopy_remote_fds; @@ -103,7 +139,6 @@ struct MigrationIncomingState { struct PostcopyBlocktimeContext *blocktime_ctx; /* notify PAUSED postcopy incoming migrations to try to continue */ - bool postcopy_recover_triggered; QemuSemaphore postcopy_pause_sem_dst; QemuSemaphore postcopy_pause_sem_fault; @@ -130,6 +165,7 @@ struct MigrationIncomingState { MigrationIncomingState *migration_incoming_get_current(void); void migration_incoming_state_destroy(void); +void migration_incoming_transport_cleanup(MigrationIncomingState *mis); /* * Functions to work with blocktime context */ @@ -391,5 +427,9 @@ bool migration_rate_limit(void); void migration_cancel(const Error *error); void populate_vfio_info(MigrationInfo *info); +void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page); + +bool migrate_multi_channels_is_allowed(void); +void migrate_protocol_allow_multi_channels(bool allow); #endif diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index aba1c88a0c..3a7ae44485 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -43,7 +43,7 @@ struct zlib_data { */ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) { - struct zlib_data *z = g_malloc0(sizeof(struct zlib_data)); + struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; zs->zalloc = Z_NULL; @@ -164,7 +164,7 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) */ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) { - struct zlib_data *z = g_malloc0(sizeof(struct zlib_data)); + struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; p->data = z; diff --git a/migration/multifd.c b/migration/multifd.c index 76b57a7177..9ea4f581e2 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -517,7 +517,7 @@ void multifd_save_cleanup(void) { int i; - if (!migrate_use_multifd() || !migrate_multifd_is_allowed()) { + if (!migrate_use_multifd() || !migrate_multi_channels_is_allowed()) { return; } multifd_send_terminate_threads(NULL); @@ -542,8 +542,6 @@ void multifd_save_cleanup(void) qemu_sem_destroy(&p->sem_sync); g_free(p->name); p->name = NULL; - g_free(p->tls_hostname); - p->tls_hostname = NULL; multifd_pages_clear(p->pages); p->pages = NULL; p->packet_len = 0; @@ -763,7 +761,7 @@ static void multifd_tls_channel_connect(MultiFDSendParams *p, Error **errp) { MigrationState *s = migrate_get_current(); - const char *hostname = p->tls_hostname; + const char *hostname = s->hostname; QIOChannelTLS *tioc; tioc = migration_tls_client_create(s, ioc, hostname, errp); @@ -787,7 +785,8 @@ static bool multifd_channel_connect(MultiFDSendParams *p, MigrationState *s = migrate_get_current(); trace_multifd_set_outgoing_channel( - ioc, object_get_typename(OBJECT(ioc)), p->tls_hostname, error); + ioc, object_get_typename(OBJECT(ioc)), + migrate_get_current()->hostname, error); if (!error) { if (s->parameters.tls_creds && @@ -858,33 +857,20 @@ cleanup: multifd_new_send_channel_cleanup(p, sioc, local_err); } -static bool migrate_allow_multifd = true; -void migrate_protocol_allow_multifd(bool allow) -{ - migrate_allow_multifd = allow; -} - -bool migrate_multifd_is_allowed(void) -{ - return migrate_allow_multifd; -} - int multifd_save_setup(Error **errp) { int thread_count; uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); uint8_t i; - MigrationState *s; if (!migrate_use_multifd()) { return 0; } - if (!migrate_multifd_is_allowed()) { + if (!migrate_multi_channels_is_allowed()) { error_setg(errp, "multifd is not supported by current protocol"); return -1; } - s = migrate_get_current(); thread_count = migrate_multifd_channels(); multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); @@ -909,7 +895,6 @@ int multifd_save_setup(Error **errp) p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); p->packet->version = cpu_to_be32(MULTIFD_VERSION); p->name = g_strdup_printf("multifdsend_%d", i); - p->tls_hostname = g_strdup(s->hostname); /* We need one extra place for the packet header */ p->iov = g_new0(struct iovec, page_count + 1); p->normal = g_new0(ram_addr_t, page_count); @@ -980,7 +965,7 @@ int multifd_load_cleanup(Error **errp) { int i; - if (!migrate_use_multifd() || !migrate_multifd_is_allowed()) { + if (!migrate_use_multifd() || !migrate_multi_channels_is_allowed()) { return 0; } multifd_recv_terminate_threads(NULL); @@ -1129,7 +1114,7 @@ int multifd_load_setup(Error **errp) if (!migrate_use_multifd()) { return 0; } - if (!migrate_multifd_is_allowed()) { + if (!migrate_multi_channels_is_allowed()) { error_setg(errp, "multifd is not supported by current protocol"); return -1; } diff --git a/migration/multifd.h b/migration/multifd.h index 4dda900a0b..7d0effcb03 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -13,8 +13,6 @@ #ifndef QEMU_MIGRATION_MULTIFD_H #define QEMU_MIGRATION_MULTIFD_H -bool migrate_multifd_is_allowed(void); -void migrate_protocol_allow_multifd(bool allow); int multifd_save_setup(Error **errp); void multifd_save_cleanup(void); int multifd_load_setup(Error **errp); @@ -72,8 +70,6 @@ typedef struct { uint8_t id; /* channel thread name */ char *name; - /* tls hostname */ - char *tls_hostname; /* channel thread id */ QemuThread thread; /* communication channel */ diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 2a2cc5faf8..a66dd536d9 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -78,6 +78,20 @@ int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp) &pnd); } +/* + * NOTE: this routine is not thread safe, we can't call it concurrently. But it + * should be good enough for migration's purposes. + */ +void postcopy_thread_create(MigrationIncomingState *mis, + QemuThread *thread, const char *name, + void *(*fn)(void *), int joinable) +{ + qemu_sem_init(&mis->thread_sync_sem, 0); + qemu_thread_create(thread, name, fn, mis, joinable); + qemu_sem_wait(&mis->thread_sync_sem); + qemu_sem_destroy(&mis->thread_sync_sem); +} + /* Postcopy needs to detect accesses to pages that haven't yet been copied * across, and efficiently map new pages in, the techniques for doing this * are target OS specific. @@ -305,7 +319,7 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) return false; } - if (qemu_real_host_page_size != ram_pagesize_summary()) { + if (qemu_real_host_page_size() != ram_pagesize_summary()) { bool have_hp = false; /* We've got a huge page */ #ifdef UFFD_FEATURE_MISSING_HUGETLBFS @@ -343,7 +357,7 @@ static int test_ramblock_postcopiable(RAMBlock *rb, void *opaque) */ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) { - long pagesize = qemu_real_host_page_size; + long pagesize = qemu_real_host_page_size(); int ufd = -1; bool ret = false; /* Error unless we change it */ void *testarea = NULL; @@ -526,9 +540,18 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis) static void postcopy_temp_pages_cleanup(MigrationIncomingState *mis) { - if (mis->postcopy_tmp_page) { - munmap(mis->postcopy_tmp_page, mis->largest_page_size); - mis->postcopy_tmp_page = NULL; + int i; + + if (mis->postcopy_tmp_pages) { + for (i = 0; i < mis->postcopy_channels; i++) { + if (mis->postcopy_tmp_pages[i].tmp_huge_page) { + munmap(mis->postcopy_tmp_pages[i].tmp_huge_page, + mis->largest_page_size); + mis->postcopy_tmp_pages[i].tmp_huge_page = NULL; + } + } + g_free(mis->postcopy_tmp_pages); + mis->postcopy_tmp_pages = NULL; } if (mis->postcopy_tmp_zero_page) { @@ -868,15 +891,11 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) affected_cpu); } -static bool postcopy_pause_fault_thread(MigrationIncomingState *mis) +static void postcopy_pause_fault_thread(MigrationIncomingState *mis) { trace_postcopy_pause_fault_thread(); - qemu_sem_wait(&mis->postcopy_pause_sem_fault); - trace_postcopy_pause_fault_thread_continued(); - - return true; } /* @@ -893,7 +912,7 @@ static void *postcopy_ram_fault_thread(void *opaque) trace_postcopy_ram_fault_thread_entry(); rcu_register_thread(); mis->last_rb = NULL; /* last RAMBlock we sent part of */ - qemu_sem_post(&mis->fault_thread_sem); + qemu_sem_post(&mis->thread_sync_sem); struct pollfd *pfd; size_t pfd_len = 2 + mis->postcopy_remote_fds->len; @@ -936,13 +955,7 @@ static void *postcopy_ram_fault_thread(void *opaque) * broken already using the event. We should hold until * the channel is rebuilt. */ - if (postcopy_pause_fault_thread(mis)) { - /* Continue to read the userfaultfd */ - } else { - error_report("%s: paused but don't allow to continue", - __func__); - break; - } + postcopy_pause_fault_thread(mis); } if (pfd[1].revents) { @@ -1016,15 +1029,8 @@ retry: msg.arg.pagefault.address); if (ret) { /* May be network failure, try to wait for recovery */ - if (ret == -EIO && postcopy_pause_fault_thread(mis)) { - /* We got reconnected somehow, try to continue */ - goto retry; - } else { - /* This is a unavoidable fault */ - error_report("%s: postcopy_request_page() get %d", - __func__, ret); - break; - } + postcopy_pause_fault_thread(mis); + goto retry; } } @@ -1092,17 +1098,30 @@ retry: static int postcopy_temp_pages_setup(MigrationIncomingState *mis) { - int err; + PostcopyTmpPage *tmp_page; + int err, i, channels; + void *temp_page; - mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (mis->postcopy_tmp_page == MAP_FAILED) { - err = errno; - mis->postcopy_tmp_page = NULL; - error_report("%s: Failed to map postcopy_tmp_page %s", - __func__, strerror(err)); - return -err; + /* TODO: will be boosted when enable postcopy preemption */ + mis->postcopy_channels = 1; + + channels = mis->postcopy_channels; + mis->postcopy_tmp_pages = g_malloc0_n(sizeof(PostcopyTmpPage), channels); + + for (i = 0; i < channels; i++) { + tmp_page = &mis->postcopy_tmp_pages[i]; + temp_page = mmap(NULL, mis->largest_page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (temp_page == MAP_FAILED) { + err = errno; + error_report("%s: Failed to map postcopy_tmp_pages[%d]: %s", + __func__, i, strerror(err)); + /* Clean up will be done later */ + return -err; + } + tmp_page->tmp_huge_page = temp_page; + /* Initialize default states for each tmp page */ + postcopy_temp_page_reset(tmp_page); } /* @@ -1151,11 +1170,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } - qemu_sem_init(&mis->fault_thread_sem, 0); - qemu_thread_create(&mis->fault_thread, "postcopy/fault", - postcopy_ram_fault_thread, mis, QEMU_THREAD_JOINABLE); - qemu_sem_wait(&mis->fault_thread_sem); - qemu_sem_destroy(&mis->fault_thread_sem); + postcopy_thread_create(mis, &mis->fault_thread, "postcopy/fault", + postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE); mis->have_fault_thread = true; /* Mark so that we get notified of accesses to unwritten areas */ @@ -1352,6 +1368,16 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, #endif /* ------------------------------------------------------------------------- */ +void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page) +{ + tmp_page->target_pages = 0; + tmp_page->host_addr = NULL; + /* + * This is set to true when reset, and cleared as long as we received any + * of the non-zero small page within this huge page. + */ + tmp_page->all_zero = true; +} void postcopy_fault_thread_notify(MigrationIncomingState *mis) { diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index 6d2b3cf124..07684c0e1d 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -135,6 +135,10 @@ void postcopy_remove_notifier(NotifierWithReturn *n); /* Call the notifier list set by postcopy_add_start_notifier */ int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp); +void postcopy_thread_create(MigrationIncomingState *mis, + QemuThread *thread, const char *name, + void *(*fn)(void *), int joinable); + struct PostCopyFD; /* ufd is a pointer to the struct uffd_msg *TODO: more Portable! */ diff --git a/migration/ram.c b/migration/ram.c index 781f0745dc..a2489a2699 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -414,6 +414,8 @@ struct PageSearchStatus { unsigned long page; /* Set once we wrap around */ bool complete_round; + /* Whether current page is explicitly requested by postcopy */ + bool postcopy_requested; }; typedef struct PageSearchStatus PageSearchStatus; @@ -1289,7 +1291,7 @@ static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, offset | RAM_SAVE_FLAG_PAGE)); if (async) { qemu_put_buffer_async(rs->f, buf, TARGET_PAGE_SIZE, - migrate_release_ram() & + migrate_release_ram() && migration_in_postcopy()); } else { qemu_put_buffer(rs->f, buf, TARGET_PAGE_SIZE); @@ -1487,6 +1489,9 @@ retry: */ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) { + /* This is not a postcopy requested page */ + pss->postcopy_requested = false; + pss->page = migration_bitmap_find_dirty(rs, pss->block, pss->page); if (pss->complete_round && pss->block == rs->last_seen_block && pss->page >= rs->last_page) { @@ -1981,6 +1986,7 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) * really rare. */ pss->complete_round = false; + pss->postcopy_requested = true; } return !!block; @@ -2059,7 +2065,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) } struct RAMSrcPageRequest *new_entry = - g_malloc0(sizeof(struct RAMSrcPageRequest)); + g_new0(struct RAMSrcPageRequest, 1); new_entry->rb = ramblock; new_entry->offset = start; new_entry->len = len; @@ -3185,12 +3191,14 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) * * Returns a pointer from within the RCU-protected ram_list. * + * @mis: the migration incoming state pointer * @f: QEMUFile where to read the data from * @flags: Page flags (mostly to see if it's a continuation of previous block) */ -static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) +static inline RAMBlock *ram_block_from_stream(MigrationIncomingState *mis, + QEMUFile *f, int flags) { - static RAMBlock *block; + RAMBlock *block = mis->last_recv_block; char id[256]; uint8_t len; @@ -3217,6 +3225,8 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) return NULL; } + mis->last_recv_block = block; + return block; } @@ -3635,17 +3645,14 @@ int ram_postcopy_incoming_init(MigrationIncomingState *mis) * * @f: QEMUFile where to send the data */ -static int ram_load_postcopy(QEMUFile *f) +int ram_load_postcopy(QEMUFile *f) { int flags = 0, ret = 0; bool place_needed = false; bool matches_target_page_size = false; MigrationIncomingState *mis = migration_incoming_get_current(); - /* Temporary page that is later 'placed' */ - void *postcopy_host_page = mis->postcopy_tmp_page; - void *host_page = NULL; - bool all_zero = true; - int target_pages = 0; + /* Currently we only use channel 0. TODO: use all the channels */ + PostcopyTmpPage *tmp_page = &mis->postcopy_tmp_pages[0]; while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr; @@ -3672,7 +3679,7 @@ static int ram_load_postcopy(QEMUFile *f) trace_ram_load_postcopy_loop((uint64_t)addr, flags); if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_COMPRESS_PAGE)) { - block = ram_block_from_stream(f, flags); + block = ram_block_from_stream(mis, f, flags); if (!block) { ret = -EINVAL; break; @@ -3689,7 +3696,7 @@ static int ram_load_postcopy(QEMUFile *f) ret = -EINVAL; break; } - target_pages++; + tmp_page->target_pages++; matches_target_page_size = block->page_size == TARGET_PAGE_SIZE; /* * Postcopy requires that we place whole host pages atomically; @@ -3701,16 +3708,21 @@ static int ram_load_postcopy(QEMUFile *f) * however the source ensures it always sends all the components * of a host page in one chunk. */ - page_buffer = postcopy_host_page + + page_buffer = tmp_page->tmp_huge_page + host_page_offset_from_ram_block_offset(block, addr); /* If all TP are zero then we can optimise the place */ - if (target_pages == 1) { - host_page = host_page_from_ram_block_offset(block, addr); - } else if (host_page != host_page_from_ram_block_offset(block, - addr)) { + if (tmp_page->target_pages == 1) { + tmp_page->host_addr = + host_page_from_ram_block_offset(block, addr); + } else if (tmp_page->host_addr != + host_page_from_ram_block_offset(block, addr)) { /* not the 1st TP within the HP */ - error_report("Non-same host page %p/%p", host_page, - host_page_from_ram_block_offset(block, addr)); + error_report("Non-same host page detected. " + "Target host page %p, received host page %p " + "(rb %s offset 0x"RAM_ADDR_FMT" target_pages %d)", + tmp_page->host_addr, + host_page_from_ram_block_offset(block, addr), + block->idstr, addr, tmp_page->target_pages); ret = -EINVAL; break; } @@ -3719,10 +3731,11 @@ static int ram_load_postcopy(QEMUFile *f) * If it's the last part of a host page then we place the host * page */ - if (target_pages == (block->page_size / TARGET_PAGE_SIZE)) { + if (tmp_page->target_pages == + (block->page_size / TARGET_PAGE_SIZE)) { place_needed = true; } - place_source = postcopy_host_page; + place_source = tmp_page->tmp_huge_page; } switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { @@ -3736,12 +3749,12 @@ static int ram_load_postcopy(QEMUFile *f) memset(page_buffer, ch, TARGET_PAGE_SIZE); } if (ch) { - all_zero = false; + tmp_page->all_zero = false; } break; case RAM_SAVE_FLAG_PAGE: - all_zero = false; + tmp_page->all_zero = false; if (!matches_target_page_size) { /* For huge pages, we always use temporary buffer */ qemu_get_buffer(f, page_buffer, TARGET_PAGE_SIZE); @@ -3759,7 +3772,7 @@ static int ram_load_postcopy(QEMUFile *f) } break; case RAM_SAVE_FLAG_COMPRESS_PAGE: - all_zero = false; + tmp_page->all_zero = false; len = qemu_get_be32(f); if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { error_report("Invalid compressed data length: %d", len); @@ -3791,16 +3804,14 @@ static int ram_load_postcopy(QEMUFile *f) } if (!ret && place_needed) { - if (all_zero) { - ret = postcopy_place_page_zero(mis, host_page, block); + if (tmp_page->all_zero) { + ret = postcopy_place_page_zero(mis, tmp_page->host_addr, block); } else { - ret = postcopy_place_page(mis, host_page, place_source, - block); + ret = postcopy_place_page(mis, tmp_page->host_addr, + place_source, block); } place_needed = false; - target_pages = 0; - /* Assume we have a zero page until we detect something different */ - all_zero = true; + postcopy_temp_page_reset(tmp_page); } } @@ -3880,6 +3891,7 @@ void colo_flush_ram_cache(void) */ static int ram_load_precopy(QEMUFile *f) { + MigrationIncomingState *mis = migration_incoming_get_current(); int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0; /* ADVISE is earlier, it shows the source has the postcopy capability on */ bool postcopy_advised = postcopy_is_advised(); @@ -3918,7 +3930,7 @@ static int ram_load_precopy(QEMUFile *f) if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { - RAMBlock *block = ram_block_from_stream(f, flags); + RAMBlock *block = ram_block_from_stream(mis, f, flags); host = host_from_ram_block_offset(block, addr); /* diff --git a/migration/ram.h b/migration/ram.h index 2c6dc3675d..ded0a3a086 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -61,6 +61,7 @@ void ram_postcopy_send_discard_bitmap(MigrationState *ms); /* For incoming postcopy discard */ int ram_discard_range(const char *block_name, uint64_t start, size_t length); int ram_postcopy_incoming_init(MigrationIncomingState *mis); +int ram_load_postcopy(QEMUFile *f); void ram_handle_compressed(void *host, uint8_t ch, uint64_t size); diff --git a/migration/rdma.c b/migration/rdma.c index c7c7a38487..ef1e65ec36 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -2705,6 +2705,7 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) char ip[40] = "unknown"; struct rdma_addrinfo *res, *e; char port_str[16]; + int reuse = 1; for (idx = 0; idx < RDMA_WRID_MAX; idx++) { rdma->wr_data[idx].control_len = 0; @@ -2740,6 +2741,12 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) goto err_dest_init_bind_addr; } + ret = rdma_set_option(listen_id, RDMA_OPTION_ID, RDMA_OPTION_ID_REUSEADDR, + &reuse, sizeof reuse); + if (ret) { + ERROR(errp, "Error: could not set REUSEADDR option"); + goto err_dest_init_bind_addr; + } for (e = res; e != NULL; e = e->ai_next) { inet_ntop(e->ai_family, &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); diff --git a/migration/savevm.c b/migration/savevm.c index 1599b02fbc..d9076897b8 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1438,7 +1438,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, if (inactivate_disks) { /* Inactivate before sending QEMU_VM_EOF so that the - * bdrv_invalidate_cache_all() on the other end won't fail. */ + * bdrv_activate_all() on the other end won't fail. */ ret = bdrv_inactivate_all(); if (ret) { error_report("%s: bdrv_inactivate_all() failed (%d)", @@ -1863,7 +1863,7 @@ static void *postcopy_ram_listen_thread(void *opaque) migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_POSTCOPY_ACTIVE); - qemu_sem_post(&mis->listen_thread_sem); + qemu_sem_post(&mis->thread_sync_sem); trace_postcopy_ram_listen_thread_start(); rcu_register_thread(); @@ -1948,9 +1948,10 @@ static void *postcopy_ram_listen_thread(void *opaque) static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) { PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_LISTENING); - trace_loadvm_postcopy_handle_listen(); Error *local_err = NULL; + trace_loadvm_postcopy_handle_listen("enter"); + if (ps != POSTCOPY_INCOMING_ADVISE && ps != POSTCOPY_INCOMING_DISCARD) { error_report("CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps); return -1; @@ -1965,6 +1966,8 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) } } + trace_loadvm_postcopy_handle_listen("after discard"); + /* * Sensitise RAM - can now generate requests for blocks that don't exist * However, at this point the CPU shouldn't be running, and the IO @@ -1977,19 +1980,17 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) } } + trace_loadvm_postcopy_handle_listen("after uffd"); + if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, &local_err)) { error_report_err(local_err); return -1; } mis->have_listen_thread = true; - /* Start up the listening thread and wait for it to signal ready */ - qemu_sem_init(&mis->listen_thread_sem, 0); - qemu_thread_create(&mis->listen_thread, "postcopy/listen", - postcopy_ram_listen_thread, NULL, - QEMU_THREAD_DETACHED); - qemu_sem_wait(&mis->listen_thread_sem); - qemu_sem_destroy(&mis->listen_thread_sem); + postcopy_thread_create(mis, &mis->listen_thread, "postcopy/listen", + postcopy_ram_listen_thread, QEMU_THREAD_DETACHED); + trace_loadvm_postcopy_handle_listen("return"); return 0; } @@ -1999,25 +2000,29 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; + trace_loadvm_postcopy_handle_run_bh("enter"); + /* TODO we should move all of this lot into postcopy_ram.c or a shared code * in migration.c */ cpu_synchronize_all_post_init(); + trace_loadvm_postcopy_handle_run_bh("after cpu sync"); + qemu_announce_self(&mis->announce_timer, migrate_announce_params()); - /* Make sure all file formats flush their mutable metadata. + trace_loadvm_postcopy_handle_run_bh("after announce"); + + /* Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ - bdrv_invalidate_cache_all(&local_err); + bdrv_activate_all(&local_err); if (local_err) { error_report_err(local_err); local_err = NULL; autostart = false; } - trace_loadvm_postcopy_handle_run_cpu_sync(); - - trace_loadvm_postcopy_handle_run_vmstart(); + trace_loadvm_postcopy_handle_run_bh("after invalidate cache"); dirty_bitmap_mig_before_vm_start(); @@ -2030,6 +2035,8 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) } qemu_bh_delete(mis->bh); + + trace_loadvm_postcopy_handle_run_bh("return"); } /* After all discards we can start running and asking for pages */ @@ -2273,12 +2280,13 @@ static int loadvm_process_command(QEMUFile *f) return qemu_file_get_error(f); } - trace_loadvm_process_command(cmd, len); if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) { error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len); return -EINVAL; } + trace_loadvm_process_command(mig_cmd_args[cmd].name, len); + if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) { error_report("%s received with bad length - expecting %zu, got %d", mig_cmd_args[cmd].name, @@ -2565,13 +2573,22 @@ void qemu_loadvm_state_cleanup(void) /* Return true if we should continue the migration, or false. */ static bool postcopy_pause_incoming(MigrationIncomingState *mis) { + int i; + + /* + * If network is interrupted, any temp page we received will be useless + * because we didn't mark them as "received" in receivedmap. After a + * proper recovery later (which will sync src dirty bitmap with receivedmap + * on dest) these cached small pages will be resent again. + */ + for (i = 0; i < mis->postcopy_channels; i++) { + postcopy_temp_page_reset(&mis->postcopy_tmp_pages[i]); + } + trace_postcopy_pause_incoming(); assert(migrate_postcopy_ram()); - /* Clear the triggered bit to allow one recovery */ - mis->postcopy_recover_triggered = false; - /* * Unregister yank with either from/to src would work, since ioc behind it * is the same @@ -2788,6 +2805,8 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, g_autoptr(GDateTime) now = g_date_time_new_now_local(); AioContext *aio_context; + GLOBAL_STATE_CODE(); + if (migration_is_blocked(errp)) { return false; } diff --git a/migration/tls.c b/migration/tls.c index ca1ea3bbdd..32c384a8b6 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -137,10 +137,6 @@ QIOChannelTLS *migration_tls_client_create(MigrationState *s, if (s->parameters.tls_hostname && *s->parameters.tls_hostname) { hostname = s->parameters.tls_hostname; } - if (!hostname) { - error_setg(errp, "No hostname available for TLS"); - return NULL; - } tioc = qio_channel_tls_new_client( ioc, creds, hostname, errp); diff --git a/migration/trace-events b/migration/trace-events index 48aa7b10ee..1aec580e92 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -14,15 +14,14 @@ loadvm_handle_cmd_packaged_main(int ret) "%d" loadvm_handle_cmd_packaged_received(int ret) "%d" loadvm_handle_recv_bitmap(char *s) "%s" loadvm_postcopy_handle_advise(void) "" -loadvm_postcopy_handle_listen(void) "" +loadvm_postcopy_handle_listen(const char *str) "%s" loadvm_postcopy_handle_run(void) "" -loadvm_postcopy_handle_run_cpu_sync(void) "" -loadvm_postcopy_handle_run_vmstart(void) "" +loadvm_postcopy_handle_run_bh(const char *str) "%s" loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud" -loadvm_process_command(uint16_t com, uint16_t len) "com=0x%x len=%d" +loadvm_process_command(const char *s, uint16_t len) "com=%s len=%d" loadvm_process_command_ping(uint32_t val) "0x%x" postcopy_ram_listen_thread_exit(void) "" postcopy_ram_listen_thread_start(void) "" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index bf4d440308..e83bfccb9e 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cpu-float.h" #include "qemu-file.h" #include "migration.h" #include "migration/vmstate.h" diff --git a/migration/vmstate.c b/migration/vmstate.c index 05f87cdddc..36ae8b9e19 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -90,12 +90,6 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, return -EINVAL; } if (version_id < vmsd->minimum_version_id) { - if (vmsd->load_state_old && - version_id >= vmsd->minimum_version_id_old) { - ret = vmsd->load_state_old(f, opaque, version_id); - trace_vmstate_load_state_end(vmsd->name, "old path", ret); - return ret; - } error_report("%s: incoming version_id %d is too old " "for local minimum version_id %d", vmsd->name, version_id, vmsd->minimum_version_id); diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 8c384dc1b2..93061a11af 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1396,10 +1396,35 @@ void hmp_set_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); const char *password = qdict_get_str(qdict, "password"); + const char *display = qdict_get_try_str(qdict, "display"); const char *connected = qdict_get_try_str(qdict, "connected"); Error *err = NULL; - qmp_set_password(protocol, password, !!connected, connected, &err); + SetPasswordOptions opts = { + .password = (char *)password, + .has_connected = !!connected, + }; + + opts.connected = qapi_enum_parse(&SetPasswordAction_lookup, connected, + SET_PASSWORD_ACTION_KEEP, &err); + if (err) { + goto out; + } + + opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, + DISPLAY_PROTOCOL_VNC, &err); + if (err) { + goto out; + } + + if (opts.protocol == DISPLAY_PROTOCOL_VNC) { + opts.u.vnc.has_display = !!display; + opts.u.vnc.display = (char *)display; + } + + qmp_set_password(&opts, &err); + +out: hmp_handle_error(mon, err); } @@ -1407,9 +1432,27 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); const char *whenstr = qdict_get_str(qdict, "time"); + const char *display = qdict_get_try_str(qdict, "display"); Error *err = NULL; - qmp_expire_password(protocol, whenstr, &err); + ExpirePasswordOptions opts = { + .time = (char *)whenstr, + }; + + opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, + DISPLAY_PROTOCOL_VNC, &err); + if (err) { + goto out; + } + + if (opts.protocol == DISPLAY_PROTOCOL_VNC) { + opts.u.vnc.has_display = !!display; + opts.u.vnc.display = (char *)display; + } + + qmp_expire_password(&opts, &err); + +out: hmp_handle_error(mon, err); } @@ -1429,6 +1472,7 @@ void hmp_change(Monitor *mon, const QDict *qdict) const char *target = qdict_get_str(qdict, "target"); const char *arg = qdict_get_try_str(qdict, "arg"); const char *read_only = qdict_get_try_str(qdict, "read-only-mode"); + bool force = qdict_get_try_bool(qdict, "force", false); BlockdevChangeReadOnlyMode read_only_mode = 0; Error *err = NULL; @@ -1465,7 +1509,8 @@ void hmp_change(Monitor *mon, const QDict *qdict) } qmp_blockdev_change_medium(true, device, false, NULL, target, - !!arg, arg, !!read_only, read_only_mode, + !!arg, arg, true, force, + !!read_only, read_only_mode, &err); } @@ -1677,9 +1722,19 @@ hmp_screendump(Monitor *mon, const QDict *qdict) const char *filename = qdict_get_str(qdict, "filename"); const char *id = qdict_get_try_str(qdict, "device"); int64_t head = qdict_get_try_int(qdict, "head", 0); + const char *input_format = qdict_get_try_str(qdict, "format"); Error *err = NULL; + ImageFormat format; - qmp_screendump(filename, id != NULL, id, id != NULL, head, &err); + format = qapi_enum_parse(&ImageFormat_lookup, input_format, + IMAGE_FORMAT_PPM, &err); + if (err) { + goto end; + } + + qmp_screendump(filename, id != NULL, id, id != NULL, head, + input_format != NULL, format, &err); +end: hmp_handle_error(mon, err); } diff --git a/monitor/hmp.c b/monitor/hmp.c index b20737e63c..15ca04735c 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -308,8 +308,8 @@ void help_cmd(Monitor *mon, const char *name) static const char *pch; static sigjmp_buf expr_env; -static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN -expr_error(Monitor *mon, const char *fmt, ...) +static G_NORETURN G_GNUC_PRINTF(2, 3) +void expr_error(Monitor *mon, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -981,6 +981,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, { const char *tmp = p; int skip_key = 0; + int ret; /* option */ c = *typestr++; @@ -1003,11 +1004,27 @@ static QDict *monitor_parse_arguments(Monitor *mon, } if (skip_key) { p = tmp; + } else if (*typestr == 's') { + /* has option with string value */ + typestr++; + tmp = p++; + while (qemu_isspace(*p)) { + p++; + } + ret = get_str(buf, sizeof(buf), &p); + if (ret < 0) { + monitor_printf(mon, "%s: value expected for -%c\n", + cmd->name, *tmp); + goto fail; + } + qdict_put_str(qdict, key, buf); } else { - /* has option */ + /* has boolean option */ p++; qdict_put_bool(qdict, key, true); } + } else if (*typestr == 's') { + typestr++; } } break; @@ -1445,7 +1462,7 @@ static void monitor_event(void *opaque, QEMUChrEvent event) * These functions just adapt the readline interface in a typesafe way. We * could cast function pointers but that discards compiler checks. */ -static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque, +static void G_GNUC_PRINTF(2, 3) monitor_readline_printf(void *opaque, const char *fmt, ...) { MonitorHMP *mon = opaque; diff --git a/monitor/misc.c b/monitor/misc.c index b1839cb8ee..6c5bb82d3b 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -48,7 +48,6 @@ #include "qapi/util.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" -#include "sysemu/tcg.h" #include "sysemu/tpm.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" @@ -419,8 +418,7 @@ static void hmp_logfile(Monitor *mon, const QDict *qdict) { Error *err = NULL; - qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err); - if (err) { + if (!qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err)) { error_report_err(err); } } @@ -429,6 +427,7 @@ static void hmp_log(Monitor *mon, const QDict *qdict) { int mask; const char *items = qdict_get_str(qdict, "items"); + Error *err = NULL; if (!strcmp(items, "none")) { mask = 0; @@ -439,7 +438,10 @@ static void hmp_log(Monitor *mon, const QDict *qdict) return; } } - qemu_set_log(mask); + + if (!qemu_set_log(mask, &err)) { + error_report_err(err); + } } static void hmp_singlestep(Monitor *mon, const QDict *qdict) @@ -720,7 +722,7 @@ static uint64_t vtop(void *ptr, Error **errp) uint64_t pinfo; uint64_t ret = -1; uintptr_t addr = (uintptr_t) ptr; - uintptr_t pagesize = qemu_real_host_page_size; + uintptr_t pagesize = qemu_real_host_page_size(); off_t offset = addr / pagesize * sizeof(pinfo); int fd; @@ -1028,7 +1030,7 @@ void qmp_getfd(const char *fdname, Error **errp) return; } - monfd = g_malloc0(sizeof(mon_fd_t)); + monfd = g_new0(mon_fd_t, 1); monfd->name = g_strdup(fdname); monfd->fd = fd; diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 3da3f86c6a..caa2e90ef2 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -63,7 +63,8 @@ * '.' other form of optional type (for 'i' and 'l') * 'b' boolean * user mode accepts "on" or "off" - * '-' optional parameter (eg. '-f') + * '-' optional parameter (eg. '-f'); if followed by a 's', it + * specifies an optional string param (e.g. '-fs' allows '-f foo') * */ diff --git a/monitor/monitor.c b/monitor/monitor.c index 21c7a68758..86949024f6 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -286,6 +286,16 @@ int error_vprintf_unless_qmp(const char *fmt, va_list ap) return -1; } +int error_printf_unless_qmp(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = error_vprintf_unless_qmp(fmt, ap); + va_end(ap); + return ret; +} static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { /* Limit guest-triggerable events to 1 per second */ diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index db4d186448..1ebb89f46c 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -14,7 +14,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "qemu/option.h" #include "monitor/monitor.h" @@ -85,7 +84,7 @@ void qmp_stop(Error **errp) { /* if there is a dump in background, we should wait until the dump * finished */ - if (dump_in_progress()) { + if (qemu_system_dump_in_progress()) { error_setg(errp, "There is a dump in process, please wait."); return; } @@ -115,7 +114,7 @@ void qmp_cont(Error **errp) /* if there is a dump in background, we should wait until the dump * finished */ - if (dump_in_progress()) { + if (qemu_system_dump_in_progress()) { error_setg(errp, "There is a dump in process, please wait."); return; } @@ -144,7 +143,7 @@ void qmp_cont(Error **errp) * If there are no inactive block nodes (e.g. because the VM was just * paused rather than completing a migration), bdrv_inactivate_all() simply * doesn't do anything. */ - bdrv_invalidate_cache_all(&local_err); + bdrv_activate_all(&local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -168,45 +167,27 @@ void qmp_system_wakeup(Error **errp) qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); } -void qmp_set_password(const char *protocol, const char *password, - bool has_connected, const char *connected, Error **errp) +void qmp_set_password(SetPasswordOptions *opts, Error **errp) { - int disconnect_if_connected = 0; - int fail_if_connected = 0; int rc; - if (has_connected) { - if (strcmp(connected, "fail") == 0) { - fail_if_connected = 1; - } else if (strcmp(connected, "disconnect") == 0) { - disconnect_if_connected = 1; - } else if (strcmp(connected, "keep") == 0) { - /* nothing */ - } else { - error_setg(errp, QERR_INVALID_PARAMETER, "connected"); - return; - } - } - - if (strcmp(protocol, "spice") == 0) { + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { if (!qemu_using_spice(errp)) { return; } - rc = qemu_spice.set_passwd(password, fail_if_connected, - disconnect_if_connected); - } else if (strcmp(protocol, "vnc") == 0) { - if (fail_if_connected || disconnect_if_connected) { + rc = qemu_spice.set_passwd(opts->password, + opts->connected == SET_PASSWORD_ACTION_FAIL, + opts->connected == SET_PASSWORD_ACTION_DISCONNECT); + } else { + assert(opts->protocol == DISPLAY_PROTOCOL_VNC); + if (opts->connected != SET_PASSWORD_ACTION_KEEP) { /* vnc supports "connected=keep" only */ error_setg(errp, QERR_INVALID_PARAMETER, "connected"); return; } /* Note that setting an empty password will not disable login through * this interface. */ - rc = vnc_display_password(NULL, password); - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", - "'vnc' or 'spice'"); - return; + rc = vnc_display_password(opts->u.vnc.display, opts->password); } if (rc != 0) { @@ -214,11 +195,11 @@ void qmp_set_password(const char *protocol, const char *password, } } -void qmp_expire_password(const char *protocol, const char *whenstr, - Error **errp) +void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp) { time_t when; int rc; + const char *whenstr = opts->time; if (strcmp(whenstr, "now") == 0) { when = 0; @@ -230,17 +211,14 @@ void qmp_expire_password(const char *protocol, const char *whenstr, when = strtoull(whenstr, NULL, 10); } - if (strcmp(protocol, "spice") == 0) { + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { if (!qemu_using_spice(errp)) { return; } rc = qemu_spice.set_pw_expire(when); - } else if (strcmp(protocol, "vnc") == 0) { - rc = vnc_display_pw_expire(NULL, when); } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", - "'vnc' or 'spice'"); - return; + assert(opts->protocol == DISPLAY_PROTOCOL_VNC); + rc = vnc_display_pw_expire(opts->u.vnc.display, when); } if (rc != 0) { @@ -339,7 +317,7 @@ ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp) MemoryInfo *qmp_query_memory_size_summary(Error **errp) { - MemoryInfo *mem_info = g_malloc0(sizeof(MemoryInfo)); + MemoryInfo *mem_info = g_new0(MemoryInfo, 1); MachineState *ms = MACHINE(qdev_get_machine()); mem_info->base_memory = ms->ram_size; @@ -368,6 +346,21 @@ void qmp_display_reload(DisplayReloadOptions *arg, Error **errp) } } +void qmp_display_update(DisplayUpdateOptions *arg, Error **errp) +{ + switch (arg->type) { + case DISPLAY_UPDATE_TYPE_VNC: +#ifdef CONFIG_VNC + vnc_display_update(&arg->u.vnc, errp); +#else + error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'"); +#endif + break; + default: + abort(); + } +} + static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) { RdmaProvider *rdma; diff --git a/nbd/client-connection.c b/nbd/client-connection.c index 2bda42641d..2a632931c3 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -33,6 +33,7 @@ struct NBDClientConnection { /* Initialization constants, never change */ SocketAddress *saddr; /* address to connect to */ QCryptoTLSCreds *tlscreds; + char *tlshostname; NBDExportInfo initial_info; bool do_negotiation; bool do_retry; @@ -77,7 +78,8 @@ NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, bool do_negotiation, const char *export_name, const char *x_dirty_bitmap, - QCryptoTLSCreds *tlscreds) + QCryptoTLSCreds *tlscreds, + const char *tlshostname) { NBDClientConnection *conn = g_new(NBDClientConnection, 1); @@ -85,6 +87,7 @@ NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, *conn = (NBDClientConnection) { .saddr = QAPI_CLONE(SocketAddress, saddr), .tlscreds = tlscreds, + .tlshostname = g_strdup(tlshostname), .do_negotiation = do_negotiation, .initial_info.request_sizes = true, @@ -107,6 +110,7 @@ static void nbd_client_connection_do_free(NBDClientConnection *conn) } error_free(conn->err); qapi_free_SocketAddress(conn->saddr); + g_free(conn->tlshostname); object_unref(OBJECT(conn->tlscreds)); g_free(conn->initial_info.x_dirty_bitmap); g_free(conn->initial_info.name); @@ -120,6 +124,7 @@ static void nbd_client_connection_do_free(NBDClientConnection *conn) */ static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, NBDExportInfo *info, QCryptoTLSCreds *tlscreds, + const char *tlshostname, QIOChannel **outioc, Error **errp) { int ret; @@ -140,7 +145,7 @@ static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, } ret = nbd_receive_negotiate(NULL, QIO_CHANNEL(sioc), tlscreds, - tlscreds ? addr->u.inet.host : NULL, + tlshostname, outioc, info, errp); if (ret < 0) { /* @@ -183,7 +188,8 @@ static void *connect_thread_func(void *opaque) ret = nbd_connect(conn->sioc, conn->saddr, conn->do_negotiation ? &conn->updated_info : NULL, - conn->tlscreds, &conn->ioc, &local_err); + conn->tlscreds, conn->tlshostname, + &conn->ioc, &local_err); /* * conn->updated_info will finally be returned to the user. Clear the diff --git a/nbd/server.c b/nbd/server.c index 9fb2f26402..4cdbc062c1 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -25,6 +25,7 @@ #include "trace.h" #include "nbd-internal.h" #include "qemu/units.h" +#include "qemu/memalign.h" #define NBD_META_ID_BASE_ALLOCATION 0 #define NBD_META_ID_ALLOCATION_DEPTH 1 @@ -212,7 +213,7 @@ static int nbd_negotiate_send_rep(NBDClient *client, uint32_t type, /* Send an error reply. * Return -errno on error, 0 on success. */ -static int GCC_FMT_ATTR(4, 0) +static int G_GNUC_PRINTF(4, 0) nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type, Error **errp, const char *fmt, va_list va) { @@ -252,7 +253,7 @@ nbd_sanitize_name(const char *name) /* Send an error reply. * Return -errno on error, 0 on success. */ -static int GCC_FMT_ATTR(4, 5) +static int G_GNUC_PRINTF(4, 5) nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, Error **errp, const char *fmt, ...) { @@ -268,7 +269,7 @@ nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, /* Drop remainder of the current option, and send a reply with the * given error type and message. Return -errno on read or write * failure; or 0 if connection is still live. */ -static int GCC_FMT_ATTR(4, 0) +static int G_GNUC_PRINTF(4, 0) nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp, const char *fmt, va_list va) { @@ -281,7 +282,7 @@ nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp, return ret; } -static int GCC_FMT_ATTR(4, 5) +static int G_GNUC_PRINTF(4, 5) nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, const char *fmt, ...) { @@ -295,7 +296,7 @@ nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, return ret; } -static int GCC_FMT_ATTR(3, 4) +static int G_GNUC_PRINTF(3, 4) nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...) { int ret; @@ -1642,7 +1643,7 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, uint64_t perm, shared_perm; bool readonly = !exp_args->writable; bool shared = !exp_args->writable; - strList *bitmaps; + BlockDirtyBitmapOrStrList *bitmaps; size_t i; int ret; @@ -1708,40 +1709,59 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, } exp->export_bitmaps = g_new0(BdrvDirtyBitmap *, exp->nr_export_bitmaps); for (i = 0, bitmaps = arg->bitmaps; bitmaps; - i++, bitmaps = bitmaps->next) { - const char *bitmap = bitmaps->value; + i++, bitmaps = bitmaps->next) + { + const char *bitmap; BlockDriverState *bs = blk_bs(blk); BdrvDirtyBitmap *bm = NULL; - while (bs) { - bm = bdrv_find_dirty_bitmap(bs, bitmap); - if (bm != NULL) { - break; + switch (bitmaps->value->type) { + case QTYPE_QSTRING: + bitmap = bitmaps->value->u.local; + while (bs) { + bm = bdrv_find_dirty_bitmap(bs, bitmap); + if (bm != NULL) { + break; + } + + bs = bdrv_filter_or_cow_bs(bs); } - bs = bdrv_filter_or_cow_bs(bs); + if (bm == NULL) { + ret = -ENOENT; + error_setg(errp, "Bitmap '%s' is not found", + bitmaps->value->u.local); + goto fail; + } + + if (readonly && bdrv_is_writable(bs) && + bdrv_dirty_bitmap_enabled(bm)) { + ret = -EINVAL; + error_setg(errp, "Enabled bitmap '%s' incompatible with " + "readonly export", bitmap); + goto fail; + } + break; + case QTYPE_QDICT: + bitmap = bitmaps->value->u.external.name; + bm = block_dirty_bitmap_lookup(bitmaps->value->u.external.node, + bitmap, NULL, errp); + if (!bm) { + ret = -ENOENT; + goto fail; + } + break; + default: + abort(); } - if (bm == NULL) { - ret = -ENOENT; - error_setg(errp, "Bitmap '%s' is not found", bitmap); - goto fail; - } + assert(bm); if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) { ret = -EINVAL; goto fail; } - if (readonly && bdrv_is_writable(bs) && - bdrv_dirty_bitmap_enabled(bm)) { - ret = -EINVAL; - error_setg(errp, - "Enabled bitmap '%s' incompatible with readonly export", - bitmap); - goto fail; - } - exp->export_bitmaps[i] = bm; assert(strlen(bitmap) <= BDRV_BITMAP_MAX_NAME_SIZE); } @@ -2063,7 +2083,7 @@ static void nbd_extent_array_free(NBDExtentArray *ea) g_free(ea->extents); g_free(ea); } -G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free) /* Further modifications of the array after conversion are abandoned */ static void nbd_extent_array_convert_to_be(NBDExtentArray *ea) @@ -2084,11 +2104,10 @@ static void nbd_extent_array_convert_to_be(NBDExtentArray *ea) * Add extent to NBDExtentArray. If extent can't be added (no available space), * return -1. * For safety, when returning -1 for the first time, .can_add is set to false, - * further call to nbd_extent_array_add() will crash. - * (to avoid the situation, when after failing to add an extent (returned -1), - * user miss this failure and add another extent, which is successfully added - * (array is full, but new extent may be squashed into the last one), then we - * have invalid array with skipped extent) + * and further calls to nbd_extent_array_add() will crash. + * (this avoids the situation where a caller ignores failure to add one extent, + * where adding another extent that would squash into the last array entry + * would result in an incorrect range reported to the client) */ static int nbd_extent_array_add(NBDExtentArray *ea, uint32_t length, uint32_t flags) @@ -2287,7 +2306,7 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, assert(client->recv_coroutine == qemu_coroutine_self()); ret = nbd_receive_request(client, request, errp); if (ret < 0) { - return ret; + return ret; } trace_nbd_co_receive_request_decode_type(request->handle, request->type, @@ -2647,7 +2666,7 @@ static coroutine_fn void nbd_trip(void *opaque) } if (ret < 0) { - /* It wans't -EIO, so, according to nbd_co_receive_request() + /* It wasn't -EIO, so, according to nbd_co_receive_request() * semantics, we should return the error to the client. */ Error *export_err = local_err; diff --git a/net/announce.c b/net/announce.c index 26f057f5ee..62c60192a3 100644 --- a/net/announce.c +++ b/net/announce.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #include "net/announce.h" #include "net/net.h" #include "qapi/clone-visitor.h" @@ -120,6 +120,19 @@ static int announce_self_create(uint8_t *buf, return 60; /* len (FCS will be added by hardware) */ } +/* + * Helper to print ethernet mac address + */ +static const char *qemu_ether_ntoa(const MACAddr *mac) +{ + static char ret[18]; + + snprintf(ret, sizeof(ret), "%02x:%02x:%02x:%02x:%02x:%02x", + mac->a[0], mac->a[1], mac->a[2], mac->a[3], mac->a[4], mac->a[5]); + + return ret; +} + static void qemu_announce_self_iter(NICState *nic, void *opaque) { AnnounceTimer *timer = opaque; diff --git a/net/can/can_socketcan.c b/net/can/can_socketcan.c index 4b68f60c6b..c1a1ad0563 100644 --- a/net/can/can_socketcan.c +++ b/net/can/can_socketcan.c @@ -76,19 +76,21 @@ QEMU_BUILD_BUG_ON(offsetof(qemu_can_frame, data) static void can_host_socketcan_display_msg(struct qemu_can_frame *msg) { int i; - FILE *logfile = qemu_log_lock(); - qemu_log("[cansocketcan]: %03X [%01d] %s %s", - msg->can_id & QEMU_CAN_EFF_MASK, - msg->can_dlc, - msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF", - msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT"); + FILE *logfile = qemu_log_trylock(); - for (i = 0; i < msg->can_dlc; i++) { - qemu_log(" %02X", msg->data[i]); + if (logfile) { + fprintf(logfile, "[cansocketcan]: %03X [%01d] %s %s", + msg->can_id & QEMU_CAN_EFF_MASK, + msg->can_dlc, + msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF", + msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT"); + + for (i = 0; i < msg->can_dlc; i++) { + fprintf(logfile, " %02X", msg->data[i]); + } + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); } - qemu_log("\n"); - qemu_log_flush(); - qemu_log_unlock(logfile); } static void can_host_socketcan_read(void *opaque) diff --git a/net/colo-compare.c b/net/colo-compare.c index 62554b5b3c..d5d0965805 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -13,7 +13,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "trace.h" #include "qapi/error.h" diff --git a/net/l2tpv3.c b/net/l2tpv3.c index e4d4218db6..af373e5c30 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -34,7 +34,7 @@ #include "qemu/sockets.h" #include "qemu/iov.h" #include "qemu/main-loop.h" - +#include "qemu/memalign.h" /* The buffer size needs to be investigated for optimum numbers and * optimum means of paging in on different systems. This size is @@ -716,7 +716,7 @@ int net_init_l2tpv3(const Netdev *netdev, s->vec = g_new(struct iovec, MAX_L2TPV3_IOVCNT); s->header_buf = g_malloc(s->header_size); - qemu_set_nonblock(fd); + qemu_socket_set_nonblock(fd); s->fd = fd; s->counter = 0; diff --git a/net/net.c b/net/net.c index f0d14dbfc1..a094cf1d29 100644 --- a/net/net.c +++ b/net/net.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "net/net.h" #include "clients.h" @@ -524,7 +523,7 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) int qemu_set_vnet_le(NetClientState *nc, bool is_le) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN if (!nc || !nc->info->set_vnet_le) { return -ENOSYS; } @@ -537,7 +536,7 @@ int qemu_set_vnet_le(NetClientState *nc, bool is_le) int qemu_set_vnet_be(NetClientState *nc, bool is_be) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN return 0; #else if (!nc || !nc->info->set_vnet_be) { diff --git a/net/socket.c b/net/socket.c index 15b410e8d8..bfd8596250 100644 --- a/net/socket.c +++ b/net/socket.c @@ -27,7 +27,6 @@ #include "clients.h" #include "monitor/monitor.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/sockets.h" @@ -120,9 +119,9 @@ static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, do { if (s->dgram_dst.sin_family != AF_UNIX) { - ret = qemu_sendto(s->fd, buf, size, 0, - (struct sockaddr *)&s->dgram_dst, - sizeof(s->dgram_dst)); + ret = sendto(s->fd, buf, size, 0, + (struct sockaddr *)&s->dgram_dst, + sizeof(s->dgram_dst)); } else { ret = send(s->fd, buf, size, 0); } @@ -163,7 +162,7 @@ static void net_socket_send(void *opaque) uint8_t buf1[NET_BUFSIZE]; const uint8_t *buf; - size = qemu_recv(s->fd, buf1, sizeof(buf1), 0); + size = recv(s->fd, buf1, sizeof(buf1), 0); if (size < 0) { if (errno != EWOULDBLOCK) goto eoc; @@ -198,7 +197,7 @@ static void net_socket_send_dgram(void *opaque) NetSocketState *s = opaque; int size; - size = qemu_recv(s->fd, s->rs.buf, sizeof(s->rs.buf), 0); + size = recv(s->fd, s->rs.buf, sizeof(s->rs.buf), 0); if (size < 0) return; if (size == 0) { @@ -246,7 +245,7 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, * only on posix systems. */ val = 1; - ret = qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (ret < 0) { error_setg_errno(errp, errno, "can't set socket option SO_REUSEADDR"); @@ -268,8 +267,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, imr.imr_interface.s_addr = htonl(INADDR_ANY); } - ret = qemu_setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &imr, sizeof(struct ip_mreq)); + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &imr, sizeof(struct ip_mreq)); if (ret < 0) { error_setg_errno(errp, errno, "can't add socket to multicast group %s", @@ -279,8 +278,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, /* Force mcast msgs to loopback (eg. several QEMUs in same host */ loop = 1; - ret = qemu_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, - &loop, sizeof(loop)); + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + &loop, sizeof(loop)); if (ret < 0) { error_setg_errno(errp, errno, "can't force multicast message to loopback"); @@ -289,8 +288,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, /* If a bind address is given, only send packets from that address */ if (localaddr != NULL) { - ret = qemu_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, - localaddr, sizeof(*localaddr)); + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + localaddr, sizeof(*localaddr)); if (ret < 0) { error_setg_errno(errp, errno, "can't set the default network send interface"); @@ -298,7 +297,7 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, } } - qemu_set_nonblock(fd); + qemu_socket_set_nonblock(fd); return fd; fail: if (fd >= 0) @@ -523,7 +522,7 @@ static int net_socket_listen_init(NetClientState *peer, error_setg_errno(errp, errno, "can't create stream socket"); return -1; } - qemu_set_nonblock(fd); + qemu_socket_set_nonblock(fd); socket_set_fast_reuse(fd); @@ -571,7 +570,7 @@ static int net_socket_connect_init(NetClientState *peer, error_setg_errno(errp, errno, "can't create stream socket"); return -1; } - qemu_set_nonblock(fd); + qemu_socket_set_nonblock(fd); connected = 0; for(;;) { @@ -689,7 +688,7 @@ static int net_socket_udp_init(NetClientState *peer, closesocket(fd); return -1; } - qemu_set_nonblock(fd); + qemu_socket_set_nonblock(fd); s = net_socket_fd_init(peer, model, name, fd, 0, NULL, errp); if (!s) { @@ -731,7 +730,7 @@ int net_init_socket(const Netdev *netdev, const char *name, if (fd == -1) { return -1; } - ret = qemu_try_set_nonblock(fd); + ret = qemu_socket_try_set_nonblock(fd); if (ret < 0) { error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", name, fd); diff --git a/net/tap-bsd.c b/net/tap-bsd.c index e45a6d124e..005ce05c6e 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "tap_int.h" #include "qemu/cutils.h" @@ -99,7 +98,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } } - fcntl(fd, F_SETFL, O_NONBLOCK); + g_unix_set_fd_nonblocking(fd, true, NULL); return fd; } @@ -190,7 +189,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, goto error; } - fcntl(fd, F_SETFL, O_NONBLOCK); + g_unix_set_fd_nonblocking(fd, true, NULL); return fd; error: diff --git a/net/tap-linux.c b/net/tap-linux.c index 5e70b93037..304ff45071 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "tap_int.h" #include "tap-linux.h" #include "net/tap.h" @@ -114,7 +113,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } pstrcpy(ifname, ifname_size, ifr.ifr_name); - fcntl(fd, F_SETFL, O_NONBLOCK); + g_unix_set_fd_nonblocking(fd, true, NULL); return fd; } diff --git a/net/tap-solaris.c b/net/tap-solaris.c index d85224242b..a44f8805c2 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -27,7 +27,6 @@ #include "tap_int.h" #include "qemu/ctype.h" #include "qemu/cutils.h" -#include "qemu-common.h" #include #include @@ -199,7 +198,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } } - fcntl(fd, F_SETFL, O_NONBLOCK); + g_unix_set_fd_nonblocking(fd, true, NULL); return fd; } diff --git a/net/tap-win32.c b/net/tap-win32.c index 6096972f5d..7466f22e77 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -29,7 +29,6 @@ #include "qemu/osdep.h" #include "tap_int.h" -#include "qemu-common.h" #include "clients.h" /* net_init_tap */ #include "net/eth.h" #include "net/net.h" diff --git a/net/tap.c b/net/tap.c index c5cbeaa7a2..b3ddfd4a74 100644 --- a/net/tap.c +++ b/net/tap.c @@ -38,7 +38,6 @@ #include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "qapi/error.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -620,7 +619,10 @@ int net_init_bridge(const Netdev *netdev, const char *name, return -1; } - qemu_set_nonblock(fd); + if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return -1; + } vnet_hdr = tap_probe_vnet_hdr(fd, errp); if (vnet_hdr < 0) { close(fd); @@ -717,8 +719,6 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } if (vhostfdname) { - int ret; - vhostfd = monitor_fd_param(monitor_cur(), vhostfdname, &err); if (vhostfd == -1) { if (tap->has_vhostforce && tap->vhostforce) { @@ -728,9 +728,8 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } return; } - ret = qemu_try_set_nonblock(vhostfd); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", + if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { + error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", name, fd); return; } @@ -746,7 +745,10 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } return; } - qemu_set_nonblock(vhostfd); + if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return; + } } options.opaque = (void *)(uintptr_t)vhostfd; options.nvqs = 2; @@ -833,9 +835,8 @@ int net_init_tap(const Netdev *netdev, const char *name, return -1; } - ret = qemu_try_set_nonblock(fd); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", + if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", name, fd); close(fd); return -1; @@ -890,9 +891,9 @@ int net_init_tap(const Netdev *netdev, const char *name, goto free_fail; } - ret = qemu_try_set_nonblock(fd); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", + ret = g_unix_set_fd_nonblocking(fd, true, NULL); + if (!ret) { + error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", name, fd); goto free_fail; } @@ -947,7 +948,10 @@ free_fail: return -1; } - qemu_set_nonblock(fd); + if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return -1; + } vnet_hdr = tap_probe_vnet_hdr(fd, errp); if (vnet_hdr < 0) { close(fd); diff --git a/net/util.h b/net/util.h index 358185fd50..288312979f 100644 --- a/net/util.h +++ b/net/util.h @@ -30,7 +30,7 @@ * Structure of an internet header, naked of options. */ struct ip { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint8_t ip_v:4, /* version */ ip_hl:4; /* header length */ #else diff --git a/net/vde.c b/net/vde.c index 99189cccb6..1083916bcf 100644 --- a/net/vde.c +++ b/net/vde.c @@ -27,7 +27,6 @@ #include "net/net.h" #include "clients.h" -#include "qemu-common.h" #include "qemu/option.h" #include "qemu/main-loop.h" #include "qapi/error.h" diff --git a/os-posix.c b/os-posix.c index ae6c9f2a5e..321fc4bd13 100644 --- a/os-posix.c +++ b/os-posix.c @@ -29,7 +29,6 @@ #include #include -#include "qemu-common.h" /* Needed early for CONFIG_BSD etc. */ #include "net/slirp.h" #include "qemu/qemu-options.h" @@ -151,14 +150,6 @@ int os_parse_cmd_args(int index, const char *optarg) case QEMU_OPTION_daemonize: daemonize = 1; break; -#if defined(CONFIG_LINUX) - case QEMU_OPTION_enablefips: - warn_report("-enable-fips is deprecated, please build QEMU with " - "the `libgcrypt` library as the cryptography provider " - "to enable FIPS compliance"); - fips_set_state(true); - break; -#endif default: return -1; } @@ -224,7 +215,7 @@ void os_daemonize(void) pid_t pid; int fds[2]; - if (pipe(fds) == -1) { + if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { exit(1); } @@ -249,7 +240,6 @@ void os_daemonize(void) close(fds[0]); daemon_pipe = fds[1]; - qemu_set_cloexec(daemon_pipe); setsid(); @@ -292,7 +282,7 @@ void os_setup_post(void) dup2(fd, 0); dup2(fd, 1); /* In case -D is given do not redirect stderr to /dev/null */ - if (!qemu_logfile) { + if (!qemu_log_enabled()) { dup2(fd, 2); } @@ -317,6 +307,12 @@ bool is_daemonized(void) return daemonize; } +int os_set_daemonize(bool d) +{ + daemonize = d; + return 0; +} + int os_mlock(void) { #ifdef HAVE_MLOCKALL diff --git a/os-win32.c b/os-win32.c index e31c921983..725ad652e8 100644 --- a/os-win32.c +++ b/os-win32.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include #include -#include "qemu-common.h" #include "sysemu/runstate.h" static BOOL WINAPI qemu_ctrl_handler(DWORD type) @@ -61,12 +60,3 @@ void os_set_line_buffering(void) setbuf(stdout, NULL); setbuf(stderr, NULL); } - -/* - * Parse OS specific command line options. - * return 0 if option handled, -1 otherwise - */ -int os_parse_cmd_args(int index, const char *optarg) -{ - return -1; -} diff --git a/page-vary-common.c b/page-vary-common.c index 9175556498..ab77672dd4 100644 --- a/page-vary-common.c +++ b/page-vary-common.c @@ -20,7 +20,6 @@ #define IN_PAGE_VARY 1 #include "qemu/osdep.h" -#include "qemu-common.h" #include "exec/page-vary.h" /* WARNING: This file must *not* be complied with -flto. */ diff --git a/page-vary.c b/page-vary.c index 057c7f1815..343b4adb95 100644 --- a/page-vary.c +++ b/page-vary.c @@ -20,7 +20,7 @@ #define IN_PAGE_VARY 1 #include "qemu/osdep.h" -#include "qemu-common.h" +#include "exec/page-vary.h" #include "exec/exec-all.h" bool set_preferred_target_page_bits(int bits) diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index e0796344df..6163fb8149 100644 Binary files a/pc-bios/bios-256k.bin and b/pc-bios/bios-256k.bin differ diff --git a/pc-bios/bios-microvm.bin b/pc-bios/bios-microvm.bin index f0215521b0..97fbd3192a 100644 Binary files a/pc-bios/bios-microvm.bin and b/pc-bios/bios-microvm.bin differ diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index bcf8b484c9..68f65ff2fd 100644 Binary files a/pc-bios/bios.bin and b/pc-bios/bios.bin differ diff --git a/pc-bios/edk2-aarch64-code.fd.bz2 b/pc-bios/edk2-aarch64-code.fd.bz2 index 5bf311464a..0262f5bd8f 100644 Binary files a/pc-bios/edk2-aarch64-code.fd.bz2 and b/pc-bios/edk2-aarch64-code.fd.bz2 differ diff --git a/pc-bios/edk2-arm-code.fd.bz2 b/pc-bios/edk2-arm-code.fd.bz2 index 7a98069814..4ca97b43ea 100644 Binary files a/pc-bios/edk2-arm-code.fd.bz2 and b/pc-bios/edk2-arm-code.fd.bz2 differ diff --git a/pc-bios/edk2-i386-code.fd.bz2 b/pc-bios/edk2-i386-code.fd.bz2 index e7b1befe2c..6e02c9b995 100644 Binary files a/pc-bios/edk2-i386-code.fd.bz2 and b/pc-bios/edk2-i386-code.fd.bz2 differ diff --git a/pc-bios/edk2-i386-secure-code.fd.bz2 b/pc-bios/edk2-i386-secure-code.fd.bz2 index b5df5bed30..a4b1cc92bd 100644 Binary files a/pc-bios/edk2-i386-secure-code.fd.bz2 and b/pc-bios/edk2-i386-secure-code.fd.bz2 differ diff --git a/pc-bios/edk2-x86_64-code.fd.bz2 b/pc-bios/edk2-x86_64-code.fd.bz2 index e1654d4003..37bfb0dbed 100644 Binary files a/pc-bios/edk2-x86_64-code.fd.bz2 and b/pc-bios/edk2-x86_64-code.fd.bz2 differ diff --git a/pc-bios/edk2-x86_64-microvm.fd.bz2 b/pc-bios/edk2-x86_64-microvm.fd.bz2 new file mode 100644 index 0000000000..1d65c61ded Binary files /dev/null and b/pc-bios/edk2-x86_64-microvm.fd.bz2 differ diff --git a/pc-bios/edk2-x86_64-secure-code.fd.bz2 b/pc-bios/edk2-x86_64-secure-code.fd.bz2 index 767274c38c..76dc6d5aad 100644 Binary files a/pc-bios/edk2-x86_64-secure-code.fd.bz2 and b/pc-bios/edk2-x86_64-secure-code.fd.bz2 differ diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index 5f35274cbe..d8203a5074 100644 Binary files a/pc-bios/openbios-ppc and b/pc-bios/openbios-ppc differ diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index c9f1f72472..118b1cc1c0 100644 Binary files a/pc-bios/openbios-sparc32 and b/pc-bios/openbios-sparc32 differ diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 792905d812..846cb4854c 100644 Binary files a/pc-bios/openbios-sparc64 and b/pc-bios/openbios-sparc64 differ diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index 5d55d25acc..f1ef898073 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -14,6 +14,10 @@ cc-option = $(if $(shell $(CC) $1 -c -o /dev/null -xc /dev/null >/dev/null 2>&1 override CFLAGS += -march=i486 -Wall +# If -fcf-protection is enabled in flags or compiler defaults that will +# conflict with -march=i486 +override CFLAGS += $(call cc-option, -fcf-protection=none) + # Flags for dependency generation override CPPFLAGS += -MMD -MP -MT $@ -MF $(@D)/$(*F).d diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid index 8a3c278512..58ec5ec38e 100644 Binary files a/pc-bios/skiboot.lid and b/pc-bios/skiboot.lid differ diff --git a/pc-bios/vgabios-ati.bin b/pc-bios/vgabios-ati.bin index 7171a56f9d..4533d0d063 100644 Binary files a/pc-bios/vgabios-ati.bin and b/pc-bios/vgabios-ati.bin differ diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin index afea4c930d..3ecf92de01 100644 Binary files a/pc-bios/vgabios-bochs-display.bin and b/pc-bios/vgabios-bochs-display.bin differ diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin index 194c8139a7..9b4ffdf45f 100644 Binary files a/pc-bios/vgabios-cirrus.bin and b/pc-bios/vgabios-cirrus.bin differ diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin index 056b6657b3..8a27dac557 100644 Binary files a/pc-bios/vgabios-qxl.bin and b/pc-bios/vgabios-qxl.bin differ diff --git a/pc-bios/vgabios-ramfb.bin b/pc-bios/vgabios-ramfb.bin index 02662006f2..ec9541cfb4 100644 Binary files a/pc-bios/vgabios-ramfb.bin and b/pc-bios/vgabios-ramfb.bin differ diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin index cf81ce2876..55390c45c9 100644 Binary files a/pc-bios/vgabios-stdvga.bin and b/pc-bios/vgabios-stdvga.bin differ diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin index f4178f70de..2334733a75 100644 Binary files a/pc-bios/vgabios-virtio.bin and b/pc-bios/vgabios-virtio.bin differ diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin index 8fae88af28..b668ac04a6 100644 Binary files a/pc-bios/vgabios-vmware.bin and b/pc-bios/vgabios-vmware.bin differ diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin index e5f45f0c9e..a924891ea5 100644 Binary files a/pc-bios/vgabios.bin and b/pc-bios/vgabios.bin differ diff --git a/plugins/loader.c b/plugins/loader.c index 4883b0a1cb..88c30bde2d 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -27,6 +27,7 @@ #include "qemu/cacheinfo.h" #include "qemu/xxhash.h" #include "qemu/plugin.h" +#include "qemu/memalign.h" #include "hw/core/cpu.h" #include "exec/exec-all.h" #ifndef CONFIG_USER_ONLY diff --git a/python/Makefile b/python/Makefile index 949c472624..3334311362 100644 --- a/python/Makefile +++ b/python/Makefile @@ -68,8 +68,6 @@ $(QEMU_VENV_DIR) $(QEMU_VENV_DIR)/bin/activate: setup.cfg echo "ACTIVATE $(QEMU_VENV_DIR)"; \ . $(QEMU_VENV_DIR)/bin/activate; \ echo "INSTALL qemu[devel] $(QEMU_VENV_DIR)"; \ - pip install --disable-pip-version-check \ - "setuptools<60.0.0" 1>/dev/null; \ make develop 1>/dev/null; \ ) @touch $(QEMU_VENV_DIR) diff --git a/python/README.rst b/python/README.rst index fcf74f69ea..9c1fceaee7 100644 --- a/python/README.rst +++ b/python/README.rst @@ -59,7 +59,7 @@ Package installation also normally provides executable console scripts, so that tools like ``qmp-shell`` are always available via $PATH. To invoke them without installation, you can invoke e.g.: -``> PYTHONPATH=~/src/qemu/python python3 -m qemu.aqmp.qmp_shell`` +``> PYTHONPATH=~/src/qemu/python python3 -m qemu.qmp.qmp_shell`` The mappings between console script name and python module path can be found in ``setup.cfg``. diff --git a/python/qemu/aqmp/__init__.py b/python/qemu/aqmp/__init__.py deleted file mode 100644 index 4c22c38079..0000000000 --- a/python/qemu/aqmp/__init__.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -QEMU Monitor Protocol (QMP) development library & tooling. - -This package provides a fairly low-level class for communicating -asynchronously with QMP protocol servers, as implemented by QEMU, the -QEMU Guest Agent, and the QEMU Storage Daemon. - -`QMPClient` provides the main functionality of this package. All errors -raised by this library derive from `QMPError`, see `aqmp.error` for -additional detail. See `aqmp.events` for an in-depth tutorial on -managing QMP events. -""" - -# Copyright (C) 2020, 2021 John Snow for Red Hat, Inc. -# -# Authors: -# John Snow -# -# Based on earlier work by Luiz Capitulino . -# -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. - -import logging - -from .error import QMPError -from .events import EventListener -from .message import Message -from .protocol import ( - ConnectError, - Runstate, - SocketAddrT, - StateError, -) -from .qmp_client import ExecInterruptedError, ExecuteError, QMPClient - - -# Suppress logging unless an application engages it. -logging.getLogger('qemu.aqmp').addHandler(logging.NullHandler()) - - -# The order of these fields impact the Sphinx documentation order. -__all__ = ( - # Classes, most to least important - 'QMPClient', - 'Message', - 'EventListener', - 'Runstate', - - # Exceptions, most generic to most explicit - 'QMPError', - 'StateError', - 'ConnectError', - 'ExecuteError', - 'ExecInterruptedError', - - # Type aliases - 'SocketAddrT', -) diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py deleted file mode 100644 index 6baa5f3409..0000000000 --- a/python/qemu/aqmp/legacy.py +++ /dev/null @@ -1,180 +0,0 @@ -""" -Sync QMP Wrapper - -This class pretends to be qemu.qmp.QEMUMonitorProtocol. -""" - -import asyncio -from typing import ( - Any, - Awaitable, - Dict, - List, - Optional, - TypeVar, - Union, -) - -import qemu.qmp - -from .error import QMPError -from .protocol import Runstate, SocketAddrT -from .qmp_client import QMPClient - - -# (Temporarily) Re-export QMPBadPortError -QMPBadPortError = qemu.qmp.QMPBadPortError - -#: QMPMessage is an entire QMP message of any kind. -QMPMessage = Dict[str, Any] - -#: QMPReturnValue is the 'return' value of a command. -QMPReturnValue = object - -#: QMPObject is any object in a QMP message. -QMPObject = Dict[str, object] - -# QMPMessage can be outgoing commands or incoming events/returns. -# QMPReturnValue is usually a dict/json object, but due to QAPI's -# 'returns-whitelist', it can actually be anything. -# -# {'return': {}} is a QMPMessage, -# {} is the QMPReturnValue. - - -# pylint: disable=missing-docstring - - -class QEMUMonitorProtocol(qemu.qmp.QEMUMonitorProtocol): - def __init__(self, address: SocketAddrT, - server: bool = False, - nickname: Optional[str] = None): - - # pylint: disable=super-init-not-called - self._aqmp = QMPClient(nickname) - self._aloop = asyncio.get_event_loop() - self._address = address - self._timeout: Optional[float] = None - - if server: - self._aqmp._bind_hack(address) # pylint: disable=protected-access - - _T = TypeVar('_T') - - def _sync( - self, future: Awaitable[_T], timeout: Optional[float] = None - ) -> _T: - return self._aloop.run_until_complete( - asyncio.wait_for(future, timeout=timeout) - ) - - def _get_greeting(self) -> Optional[QMPMessage]: - if self._aqmp.greeting is not None: - # pylint: disable=protected-access - return self._aqmp.greeting._asdict() - return None - - # __enter__ and __exit__ need no changes - # parse_address needs no changes - - def connect(self, negotiate: bool = True) -> Optional[QMPMessage]: - self._aqmp.await_greeting = negotiate - self._aqmp.negotiate = negotiate - - self._sync( - self._aqmp.connect(self._address) - ) - return self._get_greeting() - - def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage: - self._aqmp.await_greeting = True - self._aqmp.negotiate = True - - self._sync( - self._aqmp.accept(self._address), - timeout - ) - - ret = self._get_greeting() - assert ret is not None - return ret - - def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage: - return dict( - self._sync( - # pylint: disable=protected-access - - # _raw() isn't a public API, because turning off - # automatic ID assignment is discouraged. For - # compatibility with iotests *only*, do it anyway. - self._aqmp._raw(qmp_cmd, assign_id=False), - self._timeout - ) - ) - - # Default impl of cmd() delegates to cmd_obj - - def command(self, cmd: str, **kwds: object) -> QMPReturnValue: - return self._sync( - self._aqmp.execute(cmd, kwds), - self._timeout - ) - - def pull_event(self, - wait: Union[bool, float] = False) -> Optional[QMPMessage]: - if not wait: - # wait is False/0: "do not wait, do not except." - if self._aqmp.events.empty(): - return None - - # If wait is 'True', wait forever. If wait is False/0, the events - # queue must not be empty; but it still needs some real amount - # of time to complete. - timeout = None - if wait and isinstance(wait, float): - timeout = wait - - return dict( - self._sync( - self._aqmp.events.get(), - timeout - ) - ) - - def get_events(self, wait: Union[bool, float] = False) -> List[QMPMessage]: - events = [dict(x) for x in self._aqmp.events.clear()] - if events: - return events - - event = self.pull_event(wait) - return [event] if event is not None else [] - - def clear_events(self) -> None: - self._aqmp.events.clear() - - def close(self) -> None: - self._sync( - self._aqmp.disconnect() - ) - - def settimeout(self, timeout: Optional[float]) -> None: - self._timeout = timeout - - def send_fd_scm(self, fd: int) -> None: - self._aqmp.send_fd_scm(fd) - - def __del__(self) -> None: - if self._aqmp.runstate == Runstate.IDLE: - return - - if not self._aloop.is_running(): - self.close() - else: - # Garbage collection ran while the event loop was running. - # Nothing we can do about it now, but if we don't raise our - # own error, the user will be treated to a lot of traceback - # they might not understand. - raise QMPError( - "QEMUMonitorProtocol.close()" - " was not called before object was garbage collected" - ) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index a5972fab4d..07ac5a710b 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -40,21 +40,16 @@ from typing import ( TypeVar, ) -from qemu.qmp import ( # pylint: disable=import-error +from qemu.qmp import SocketAddrT +from qemu.qmp.legacy import ( + QEMUMonitorProtocol, QMPMessage, QMPReturnValue, - SocketAddrT, ) from . import console_socket -if os.environ.get('QEMU_PYTHON_LEGACY_QMP'): - from qemu.qmp import QEMUMonitorProtocol -else: - from qemu.aqmp.legacy import QEMUMonitorProtocol - - LOG = logging.getLogger(__name__) @@ -743,8 +738,9 @@ class QEMUMachine: :param timeout: Optional timeout, in seconds. See QEMUMonitorProtocol.pull_event. - :raise QMPTimeoutError: If timeout was non-zero and no matching events - were found. + :raise asyncio.TimeoutError: + If timeout was non-zero and no matching events were found. + :return: A QMP event matching the filter criteria. If timeout was 0 and no event matched, None. """ @@ -767,7 +763,7 @@ class QEMUMachine: event = self._qmp.pull_event(wait=timeout) if event is None: # NB: None is only returned when timeout is false-ish. - # Timeouts raise QMPTimeoutError instead! + # Timeouts raise asyncio.TimeoutError instead! break if _match(event): return event diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py index f2f9aaa5e5..1a1fc6c9b0 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -26,7 +26,7 @@ from typing import ( TextIO, ) -from qemu.qmp import SocketAddrT # pylint: disable=import-error +from qemu.qmp import SocketAddrT from .machine import QEMUMachine diff --git a/python/qemu/qmp/README.rst b/python/qemu/qmp/README.rst deleted file mode 100644 index 5bfb82535f..0000000000 --- a/python/qemu/qmp/README.rst +++ /dev/null @@ -1,9 +0,0 @@ -qemu.qmp package -================ - -This package provides a library used for connecting to and communicating -with QMP servers. It is used extensively by iotests, vm tests, -avocado tests, and other utilities in the ./scripts directory. It is -not a fully-fledged SDK and is subject to change at any time. - -See the documentation in ``__init__.py`` for more information. diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py index 358c0971d0..69190d057a 100644 --- a/python/qemu/qmp/__init__.py +++ b/python/qemu/qmp/__init__.py @@ -1,422 +1,59 @@ """ QEMU Monitor Protocol (QMP) development library & tooling. -This package provides a fairly low-level class for communicating to QMP -protocol servers, as implemented by QEMU, the QEMU Guest Agent, and the -QEMU Storage Daemon. This library is not intended for production use. +This package provides a fairly low-level class for communicating +asynchronously with QMP protocol servers, as implemented by QEMU, the +QEMU Guest Agent, and the QEMU Storage Daemon. -`QEMUMonitorProtocol` is the primary class of interest, and all errors -raised derive from `QMPError`. +`QMPClient` provides the main functionality of this package. All errors +raised by this library derive from `QMPError`, see `qmp.error` for +additional detail. See `qmp.events` for an in-depth tutorial on +managing QMP events. """ -# Copyright (C) 2009, 2010 Red Hat Inc. +# Copyright (C) 2020-2022 John Snow for Red Hat, Inc. # # Authors: -# Luiz Capitulino +# John Snow # -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. +# Based on earlier work by Luiz Capitulino . +# +# This work is licensed under the terms of the GNU LGPL, version 2 or +# later. See the COPYING file in the top-level directory. -import errno -import json import logging -import socket -import struct -from types import TracebackType -from typing import ( - Any, - Dict, - List, - Optional, - TextIO, - Tuple, - Type, - TypeVar, - Union, - cast, + +from .error import QMPError +from .events import EventListener +from .message import Message +from .protocol import ( + ConnectError, + Runstate, + SocketAddrT, + StateError, ) +from .qmp_client import ExecInterruptedError, ExecuteError, QMPClient -#: QMPMessage is an entire QMP message of any kind. -QMPMessage = Dict[str, Any] +# Suppress logging unless an application engages it. +logging.getLogger('qemu.qmp').addHandler(logging.NullHandler()) -#: QMPReturnValue is the 'return' value of a command. -QMPReturnValue = object -#: QMPObject is any object in a QMP message. -QMPObject = Dict[str, object] +# The order of these fields impact the Sphinx documentation order. +__all__ = ( + # Classes, most to least important + 'QMPClient', + 'Message', + 'EventListener', + 'Runstate', -# QMPMessage can be outgoing commands or incoming events/returns. -# QMPReturnValue is usually a dict/json object, but due to QAPI's -# 'returns-whitelist', it can actually be anything. -# -# {'return': {}} is a QMPMessage, -# {} is the QMPReturnValue. + # Exceptions, most generic to most explicit + 'QMPError', + 'StateError', + 'ConnectError', + 'ExecuteError', + 'ExecInterruptedError', - -InternetAddrT = Tuple[str, int] -UnixAddrT = str -SocketAddrT = Union[InternetAddrT, UnixAddrT] - - -class QMPError(Exception): - """ - QMP base exception - """ - - -class QMPConnectError(QMPError): - """ - QMP connection exception - """ - - -class QMPCapabilitiesError(QMPError): - """ - QMP negotiate capabilities exception - """ - - -class QMPTimeoutError(QMPError): - """ - QMP timeout exception - """ - - -class QMPProtocolError(QMPError): - """ - QMP protocol error; unexpected response - """ - - -class QMPResponseError(QMPError): - """ - Represents erroneous QMP monitor reply - """ - def __init__(self, reply: QMPMessage): - try: - desc = reply['error']['desc'] - except KeyError: - desc = reply - super().__init__(desc) - self.reply = reply - - -class QMPBadPortError(QMPError): - """ - Unable to parse socket address: Port was non-numerical. - """ - - -class QEMUMonitorProtocol: - """ - Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then - allow to handle commands and events. - """ - - #: Logger object for debugging messages - logger = logging.getLogger('QMP') - - def __init__(self, address: SocketAddrT, - server: bool = False, - nickname: Optional[str] = None): - """ - Create a QEMUMonitorProtocol class. - - @param address: QEMU address, can be either a unix socket path (string) - or a tuple in the form ( address, port ) for a TCP - connection - @param server: server mode listens on the socket (bool) - @raise OSError on socket connection errors - @note No connection is established, this is done by the connect() or - accept() methods - """ - self.__events: List[QMPMessage] = [] - self.__address = address - self.__sock = self.__get_sock() - self.__sockfile: Optional[TextIO] = None - self._nickname = nickname - if self._nickname: - self.logger = logging.getLogger('QMP').getChild(self._nickname) - if server: - self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.__sock.bind(self.__address) - self.__sock.listen(1) - - def __get_sock(self) -> socket.socket: - if isinstance(self.__address, tuple): - family = socket.AF_INET - else: - family = socket.AF_UNIX - return socket.socket(family, socket.SOCK_STREAM) - - def __negotiate_capabilities(self) -> QMPMessage: - greeting = self.__json_read() - if greeting is None or "QMP" not in greeting: - raise QMPConnectError - # Greeting seems ok, negotiate capabilities - resp = self.cmd('qmp_capabilities') - if resp and "return" in resp: - return greeting - raise QMPCapabilitiesError - - def __json_read(self, only_event: bool = False) -> Optional[QMPMessage]: - assert self.__sockfile is not None - while True: - data = self.__sockfile.readline() - if not data: - return None - # By definition, any JSON received from QMP is a QMPMessage, - # and we are asserting only at static analysis time that it - # has a particular shape. - resp: QMPMessage = json.loads(data) - if 'event' in resp: - self.logger.debug("<<< %s", resp) - self.__events.append(resp) - if not only_event: - continue - return resp - - def __get_events(self, wait: Union[bool, float] = False) -> None: - """ - Check for new events in the stream and cache them in __events. - - @param wait (bool): block until an event is available. - @param wait (float): If wait is a float, treat it as a timeout value. - - @raise QMPTimeoutError: If a timeout float is provided and the timeout - period elapses. - @raise QMPConnectError: If wait is True but no events could be - retrieved or if some other error occurred. - """ - - # Current timeout and blocking status - current_timeout = self.__sock.gettimeout() - - # Check for new events regardless and pull them into the cache: - self.__sock.settimeout(0) # i.e. setblocking(False) - try: - self.__json_read() - except OSError as err: - # EAGAIN: No data available; not critical - if err.errno != errno.EAGAIN: - raise - finally: - self.__sock.settimeout(current_timeout) - - # Wait for new events, if needed. - # if wait is 0.0, this means "no wait" and is also implicitly false. - if not self.__events and wait: - if isinstance(wait, float): - self.__sock.settimeout(wait) - try: - ret = self.__json_read(only_event=True) - except socket.timeout as err: - raise QMPTimeoutError("Timeout waiting for event") from err - except Exception as err: - msg = "Error while reading from socket" - raise QMPConnectError(msg) from err - finally: - self.__sock.settimeout(current_timeout) - - if ret is None: - raise QMPConnectError("Error while reading from socket") - - T = TypeVar('T') - - def __enter__(self: T) -> T: - # Implement context manager enter function. - return self - - def __exit__(self, - # pylint: disable=duplicate-code - # see https://github.com/PyCQA/pylint/issues/3619 - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType]) -> None: - # Implement context manager exit function. - self.close() - - @classmethod - def parse_address(cls, address: str) -> SocketAddrT: - """ - Parse a string into a QMP address. - - Figure out if the argument is in the port:host form. - If it's not, it's probably a file path. - """ - components = address.split(':') - if len(components) == 2: - try: - port = int(components[1]) - except ValueError: - msg = f"Bad port: '{components[1]}' in '{address}'." - raise QMPBadPortError(msg) from None - return (components[0], port) - - # Treat as filepath. - return address - - def connect(self, negotiate: bool = True) -> Optional[QMPMessage]: - """ - Connect to the QMP Monitor and perform capabilities negotiation. - - @return QMP greeting dict, or None if negotiate is false - @raise OSError on socket connection errors - @raise QMPConnectError if the greeting is not received - @raise QMPCapabilitiesError if fails to negotiate capabilities - """ - self.__sock.connect(self.__address) - self.__sockfile = self.__sock.makefile(mode='r') - if negotiate: - return self.__negotiate_capabilities() - return None - - def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage: - """ - Await connection from QMP Monitor and perform capabilities negotiation. - - @param timeout: timeout in seconds (nonnegative float number, or - None). The value passed will set the behavior of the - underneath QMP socket as described in [1]. - Default value is set to 15.0. - - @return QMP greeting dict - @raise OSError on socket connection errors - @raise QMPConnectError if the greeting is not received - @raise QMPCapabilitiesError if fails to negotiate capabilities - - [1] - https://docs.python.org/3/library/socket.html#socket.socket.settimeout - """ - self.__sock.settimeout(timeout) - self.__sock, _ = self.__sock.accept() - self.__sockfile = self.__sock.makefile(mode='r') - return self.__negotiate_capabilities() - - def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage: - """ - Send a QMP command to the QMP Monitor. - - @param qmp_cmd: QMP command to be sent as a Python dict - @return QMP response as a Python dict - """ - self.logger.debug(">>> %s", qmp_cmd) - self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8')) - resp = self.__json_read() - if resp is None: - raise QMPConnectError("Unexpected empty reply from server") - self.logger.debug("<<< %s", resp) - return resp - - def cmd(self, name: str, - args: Optional[Dict[str, object]] = None, - cmd_id: Optional[object] = None) -> QMPMessage: - """ - Build a QMP command and send it to the QMP Monitor. - - @param name: command name (string) - @param args: command arguments (dict) - @param cmd_id: command id (dict, list, string or int) - """ - qmp_cmd: QMPMessage = {'execute': name} - if args: - qmp_cmd['arguments'] = args - if cmd_id: - qmp_cmd['id'] = cmd_id - return self.cmd_obj(qmp_cmd) - - def command(self, cmd: str, **kwds: object) -> QMPReturnValue: - """ - Build and send a QMP command to the monitor, report errors if any - """ - ret = self.cmd(cmd, kwds) - if 'error' in ret: - raise QMPResponseError(ret) - if 'return' not in ret: - raise QMPProtocolError( - "'return' key not found in QMP response '{}'".format(str(ret)) - ) - return cast(QMPReturnValue, ret['return']) - - def pull_event(self, - wait: Union[bool, float] = False) -> Optional[QMPMessage]: - """ - Pulls a single event. - - @param wait (bool): block until an event is available. - @param wait (float): If wait is a float, treat it as a timeout value. - - @raise QMPTimeoutError: If a timeout float is provided and the timeout - period elapses. - @raise QMPConnectError: If wait is True but no events could be - retrieved or if some other error occurred. - - @return The first available QMP event, or None. - """ - self.__get_events(wait) - - if self.__events: - return self.__events.pop(0) - return None - - def get_events(self, wait: bool = False) -> List[QMPMessage]: - """ - Get a list of available QMP events and clear all pending events. - - @param wait (bool): block until an event is available. - @param wait (float): If wait is a float, treat it as a timeout value. - - @raise QMPTimeoutError: If a timeout float is provided and the timeout - period elapses. - @raise QMPConnectError: If wait is True but no events could be - retrieved or if some other error occurred. - - @return The list of available QMP events. - """ - self.__get_events(wait) - events = self.__events - self.__events = [] - return events - - def clear_events(self) -> None: - """ - Clear current list of pending events. - """ - self.__events = [] - - def close(self) -> None: - """ - Close the socket and socket file. - """ - if self.__sock: - self.__sock.close() - if self.__sockfile: - self.__sockfile.close() - - def settimeout(self, timeout: Optional[float]) -> None: - """ - Set the socket timeout. - - @param timeout (float): timeout in seconds (non-zero), or None. - @note This is a wrap around socket.settimeout - - @raise ValueError: if timeout was set to 0. - """ - if timeout == 0: - msg = "timeout cannot be 0; this engages non-blocking mode." - msg += " Use 'None' instead to disable timeouts." - raise ValueError(msg) - self.__sock.settimeout(timeout) - - def send_fd_scm(self, fd: int) -> None: - """ - Send a file descriptor to the remote via SCM_RIGHTS. - """ - if self.__sock.family != socket.AF_UNIX: - raise RuntimeError("Can't use SCM_RIGHTS on non-AF_UNIX socket.") - - self.__sock.sendmsg( - [b' '], - [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))] - ) + # Type aliases + 'SocketAddrT', +) diff --git a/python/qemu/aqmp/error.py b/python/qemu/qmp/error.py similarity index 100% rename from python/qemu/aqmp/error.py rename to python/qemu/qmp/error.py diff --git a/python/qemu/aqmp/events.py b/python/qemu/qmp/events.py similarity index 99% rename from python/qemu/aqmp/events.py rename to python/qemu/qmp/events.py index f3d4e2b5e8..6199776cc6 100644 --- a/python/qemu/aqmp/events.py +++ b/python/qemu/qmp/events.py @@ -1,5 +1,5 @@ """ -AQMP Events and EventListeners +QMP Events and EventListeners Asynchronous QMP uses `EventListener` objects to listen for events. An `EventListener` is a FIFO event queue that can be pre-filtered to listen diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py new file mode 100644 index 0000000000..03b5574618 --- /dev/null +++ b/python/qemu/qmp/legacy.py @@ -0,0 +1,315 @@ +""" +(Legacy) Sync QMP Wrapper + +This module provides the `QEMUMonitorProtocol` class, which is a +synchronous wrapper around `QMPClient`. + +Its design closely resembles that of the original QEMUMonitorProtocol +class, originally written by Luiz Capitulino. It is provided here for +compatibility with scripts inside the QEMU source tree that expect the +old interface. +""" + +# +# Copyright (C) 2009-2022 Red Hat Inc. +# +# Authors: +# Luiz Capitulino +# John Snow +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# + +import asyncio +from types import TracebackType +from typing import ( + Any, + Awaitable, + Dict, + List, + Optional, + Type, + TypeVar, + Union, +) + +from .error import QMPError +from .protocol import Runstate, SocketAddrT +from .qmp_client import QMPClient + + +#: QMPMessage is an entire QMP message of any kind. +QMPMessage = Dict[str, Any] + +#: QMPReturnValue is the 'return' value of a command. +QMPReturnValue = object + +#: QMPObject is any object in a QMP message. +QMPObject = Dict[str, object] + +# QMPMessage can be outgoing commands or incoming events/returns. +# QMPReturnValue is usually a dict/json object, but due to QAPI's +# 'returns-whitelist', it can actually be anything. +# +# {'return': {}} is a QMPMessage, +# {} is the QMPReturnValue. + + +class QMPBadPortError(QMPError): + """ + Unable to parse socket address: Port was non-numerical. + """ + + +class QEMUMonitorProtocol: + """ + Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) + and then allow to handle commands and events. + + :param address: QEMU address, can be either a unix socket path (string) + or a tuple in the form ( address, port ) for a TCP + connection + :param server: Act as the socket server. (See 'accept') + :param nickname: Optional nickname used for logging. + """ + + def __init__(self, address: SocketAddrT, + server: bool = False, + nickname: Optional[str] = None): + + self._qmp = QMPClient(nickname) + self._aloop = asyncio.get_event_loop() + self._address = address + self._timeout: Optional[float] = None + + if server: + self._sync(self._qmp.start_server(self._address)) + + _T = TypeVar('_T') + + def _sync( + self, future: Awaitable[_T], timeout: Optional[float] = None + ) -> _T: + return self._aloop.run_until_complete( + asyncio.wait_for(future, timeout=timeout) + ) + + def _get_greeting(self) -> Optional[QMPMessage]: + if self._qmp.greeting is not None: + # pylint: disable=protected-access + return self._qmp.greeting._asdict() + return None + + def __enter__(self: _T) -> _T: + # Implement context manager enter function. + return self + + def __exit__(self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType]) -> None: + # Implement context manager exit function. + self.close() + + @classmethod + def parse_address(cls, address: str) -> SocketAddrT: + """ + Parse a string into a QMP address. + + Figure out if the argument is in the port:host form. + If it's not, it's probably a file path. + """ + components = address.split(':') + if len(components) == 2: + try: + port = int(components[1]) + except ValueError: + msg = f"Bad port: '{components[1]}' in '{address}'." + raise QMPBadPortError(msg) from None + return (components[0], port) + + # Treat as filepath. + return address + + def connect(self, negotiate: bool = True) -> Optional[QMPMessage]: + """ + Connect to the QMP Monitor and perform capabilities negotiation. + + :return: QMP greeting dict, or None if negotiate is false + :raise ConnectError: on connection errors + """ + self._qmp.await_greeting = negotiate + self._qmp.negotiate = negotiate + + self._sync( + self._qmp.connect(self._address) + ) + return self._get_greeting() + + def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage: + """ + Await connection from QMP Monitor and perform capabilities negotiation. + + :param timeout: + timeout in seconds (nonnegative float number, or None). + If None, there is no timeout, and this may block forever. + + :return: QMP greeting dict + :raise ConnectError: on connection errors + """ + self._qmp.await_greeting = True + self._qmp.negotiate = True + + self._sync(self._qmp.accept(), timeout) + + ret = self._get_greeting() + assert ret is not None + return ret + + def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage: + """ + Send a QMP command to the QMP Monitor. + + :param qmp_cmd: QMP command to be sent as a Python dict + :return: QMP response as a Python dict + """ + return dict( + self._sync( + # pylint: disable=protected-access + + # _raw() isn't a public API, because turning off + # automatic ID assignment is discouraged. For + # compatibility with iotests *only*, do it anyway. + self._qmp._raw(qmp_cmd, assign_id=False), + self._timeout + ) + ) + + def cmd(self, name: str, + args: Optional[Dict[str, object]] = None, + cmd_id: Optional[object] = None) -> QMPMessage: + """ + Build a QMP command and send it to the QMP Monitor. + + :param name: command name (string) + :param args: command arguments (dict) + :param cmd_id: command id (dict, list, string or int) + """ + qmp_cmd: QMPMessage = {'execute': name} + if args: + qmp_cmd['arguments'] = args + if cmd_id: + qmp_cmd['id'] = cmd_id + return self.cmd_obj(qmp_cmd) + + def command(self, cmd: str, **kwds: object) -> QMPReturnValue: + """ + Build and send a QMP command to the monitor, report errors if any + """ + return self._sync( + self._qmp.execute(cmd, kwds), + self._timeout + ) + + def pull_event(self, + wait: Union[bool, float] = False) -> Optional[QMPMessage]: + """ + Pulls a single event. + + :param wait: + If False or 0, do not wait. Return None if no events ready. + If True, wait forever until the next event. + Otherwise, wait for the specified number of seconds. + + :raise asyncio.TimeoutError: + When a timeout is requested and the timeout period elapses. + + :return: The first available QMP event, or None. + """ + if not wait: + # wait is False/0: "do not wait, do not except." + if self._qmp.events.empty(): + return None + + # If wait is 'True', wait forever. If wait is False/0, the events + # queue must not be empty; but it still needs some real amount + # of time to complete. + timeout = None + if wait and isinstance(wait, float): + timeout = wait + + return dict( + self._sync( + self._qmp.events.get(), + timeout + ) + ) + + def get_events(self, wait: Union[bool, float] = False) -> List[QMPMessage]: + """ + Get a list of QMP events and clear all pending events. + + :param wait: + If False or 0, do not wait. Return None if no events ready. + If True, wait until we have at least one event. + Otherwise, wait for up to the specified number of seconds for at + least one event. + + :raise asyncio.TimeoutError: + When a timeout is requested and the timeout period elapses. + + :return: A list of QMP events. + """ + events = [dict(x) for x in self._qmp.events.clear()] + if events: + return events + + event = self.pull_event(wait) + return [event] if event is not None else [] + + def clear_events(self) -> None: + """Clear current list of pending events.""" + self._qmp.events.clear() + + def close(self) -> None: + """Close the connection.""" + self._sync( + self._qmp.disconnect() + ) + + def settimeout(self, timeout: Optional[float]) -> None: + """ + Set the timeout for QMP RPC execution. + + This timeout affects the `cmd`, `cmd_obj`, and `command` methods. + The `accept`, `pull_event` and `get_event` methods have their + own configurable timeouts. + + :param timeout: + timeout in seconds, or None. + None will wait indefinitely. + """ + self._timeout = timeout + + def send_fd_scm(self, fd: int) -> None: + """ + Send a file descriptor to the remote via SCM_RIGHTS. + """ + self._qmp.send_fd_scm(fd) + + def __del__(self) -> None: + if self._qmp.runstate == Runstate.IDLE: + return + + if not self._aloop.is_running(): + self.close() + else: + # Garbage collection ran while the event loop was running. + # Nothing we can do about it now, but if we don't raise our + # own error, the user will be treated to a lot of traceback + # they might not understand. + raise QMPError( + "QEMUMonitorProtocol.close()" + " was not called before object was garbage collected" + ) diff --git a/python/qemu/aqmp/message.py b/python/qemu/qmp/message.py similarity index 100% rename from python/qemu/aqmp/message.py rename to python/qemu/qmp/message.py diff --git a/python/qemu/aqmp/models.py b/python/qemu/qmp/models.py similarity index 100% rename from python/qemu/aqmp/models.py rename to python/qemu/qmp/models.py diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/qmp/protocol.py similarity index 76% rename from python/qemu/aqmp/protocol.py rename to python/qemu/qmp/protocol.py index 33358f5cd7..6ea86650ad 100644 --- a/python/qemu/aqmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -10,12 +10,14 @@ In this package, it is used as the implementation for the `QMPClient` class. """ +# It's all the docstrings ... ! It's long for a good reason ^_^; +# pylint: disable=too-many-lines + import asyncio from asyncio import StreamReader, StreamWriter from enum import Enum from functools import wraps import logging -import socket from ssl import SSLContext from typing import ( Any, @@ -194,9 +196,9 @@ class AsyncProtocol(Generic[T]): :param name: Name used for logging messages, if any. By default, messages - will log to 'qemu.aqmp.protocol', but each individual connection + will log to 'qemu.qmp.protocol', but each individual connection can be given its own logger by giving it a name; messages will - then log to 'qemu.aqmp.protocol.${name}'. + then log to 'qemu.qmp.protocol.${name}'. """ # pylint: disable=too-many-instance-attributes @@ -239,8 +241,9 @@ class AsyncProtocol(Generic[T]): self._runstate = Runstate.IDLE self._runstate_changed: Optional[asyncio.Event] = None - # Workaround for bind() - self._sock: Optional[socket.socket] = None + # Server state for start_server() and _incoming() + self._server: Optional[asyncio.AbstractServer] = None + self._accepted: Optional[asyncio.Event] = None def __repr__(self) -> str: cls_name = type(self).__name__ @@ -265,21 +268,90 @@ class AsyncProtocol(Generic[T]): @upper_half @require(Runstate.IDLE) - async def accept(self, address: SocketAddrT, - ssl: Optional[SSLContext] = None) -> None: + async def start_server_and_accept( + self, address: SocketAddrT, + ssl: Optional[SSLContext] = None + ) -> None: """ Accept a connection and begin processing message queues. If this call fails, `runstate` is guaranteed to be set back to `IDLE`. + This method is precisely equivalent to calling `start_server()` + followed by `accept()`. :param address: - Address to listen to; UNIX socket path or TCP address/port. + Address to listen on; UNIX socket path or TCP address/port. :param ssl: SSL context to use, if any. :raise StateError: When the `Runstate` is not `IDLE`. - :raise ConnectError: If a connection could not be accepted. + :raise ConnectError: + When a connection or session cannot be established. + + This exception will wrap a more concrete one. In most cases, + the wrapped exception will be `OSError` or `EOFError`. If a + protocol-level failure occurs while establishing a new + session, the wrapped error may also be an `QMPError`. """ - await self._new_session(address, ssl, accept=True) + await self.start_server(address, ssl) + await self.accept() + assert self.runstate == Runstate.RUNNING + + @upper_half + @require(Runstate.IDLE) + async def start_server(self, address: SocketAddrT, + ssl: Optional[SSLContext] = None) -> None: + """ + Start listening for an incoming connection, but do not wait for a peer. + + This method starts listening for an incoming connection, but + does not block waiting for a peer. This call will return + immediately after binding and listening on a socket. A later + call to `accept()` must be made in order to finalize the + incoming connection. + + :param address: + Address to listen on; UNIX socket path or TCP address/port. + :param ssl: SSL context to use, if any. + + :raise StateError: When the `Runstate` is not `IDLE`. + :raise ConnectError: + When the server could not start listening on this address. + + This exception will wrap a more concrete one. In most cases, + the wrapped exception will be `OSError`. + """ + await self._session_guard( + self._do_start_server(address, ssl), + 'Failed to establish connection') + assert self.runstate == Runstate.CONNECTING + + @upper_half + @require(Runstate.CONNECTING) + async def accept(self) -> None: + """ + Accept an incoming connection and begin processing message queues. + + If this call fails, `runstate` is guaranteed to be set back to `IDLE`. + + :raise StateError: When the `Runstate` is not `CONNECTING`. + :raise QMPError: When `start_server()` was not called yet. + :raise ConnectError: + When a connection or session cannot be established. + + This exception will wrap a more concrete one. In most cases, + the wrapped exception will be `OSError` or `EOFError`. If a + protocol-level failure occurs while establishing a new + session, the wrapped error may also be an `QMPError`. + """ + if self._accepted is None: + raise QMPError("Cannot call accept() before start_server().") + await self._session_guard( + self._do_accept(), + 'Failed to establish connection') + await self._session_guard( + self._establish_session(), + 'Failed to establish session') + assert self.runstate == Runstate.RUNNING @upper_half @require(Runstate.IDLE) @@ -295,9 +367,21 @@ class AsyncProtocol(Generic[T]): :param ssl: SSL context to use, if any. :raise StateError: When the `Runstate` is not `IDLE`. - :raise ConnectError: If a connection cannot be made to the server. + :raise ConnectError: + When a connection or session cannot be established. + + This exception will wrap a more concrete one. In most cases, + the wrapped exception will be `OSError` or `EOFError`. If a + protocol-level failure occurs while establishing a new + session, the wrapped error may also be an `QMPError`. """ - await self._new_session(address, ssl) + await self._session_guard( + self._do_connect(address, ssl), + 'Failed to establish connection') + await self._session_guard( + self._establish_session(), + 'Failed to establish session') + assert self.runstate == Runstate.RUNNING @upper_half async def disconnect(self) -> None: @@ -317,6 +401,62 @@ class AsyncProtocol(Generic[T]): # Section: Session machinery # -------------------------- + async def _session_guard(self, coro: Awaitable[None], emsg: str) -> None: + """ + Async guard function used to roll back to `IDLE` on any error. + + On any Exception, the state machine will be reset back to + `IDLE`. Most Exceptions will be wrapped with `ConnectError`, but + `BaseException` events will be left alone (This includes + asyncio.CancelledError, even prior to Python 3.8). + + :param error_message: + Human-readable string describing what connection phase failed. + + :raise BaseException: + When `BaseException` occurs in the guarded block. + :raise ConnectError: + When any other error is encountered in the guarded block. + """ + # Note: After Python 3.6 support is removed, this should be an + # @asynccontextmanager instead of accepting a callback. + try: + await coro + except BaseException as err: + self.logger.error("%s: %s", emsg, exception_summary(err)) + self.logger.debug("%s:\n%s\n", emsg, pretty_traceback()) + try: + # Reset the runstate back to IDLE. + await self.disconnect() + except: + # We don't expect any Exceptions from the disconnect function + # here, because we failed to connect in the first place. + # The disconnect() function is intended to perform + # only cannot-fail cleanup here, but you never know. + emsg = ( + "Unexpected bottom half exception. " + "This is a bug in the QMP library. " + "Please report it to and " + "CC: John Snow ." + ) + self.logger.critical("%s:\n%s\n", emsg, pretty_traceback()) + raise + + # CancelledError is an Exception with special semantic meaning; + # We do NOT want to wrap it up under ConnectError. + # NB: CancelledError is not a BaseException before Python 3.8 + if isinstance(err, asyncio.CancelledError): + raise + + # Any other kind of error can be treated as some kind of connection + # failure broadly. Inspect the 'exc' field to explore the root + # cause in greater detail. + if isinstance(err, Exception): + raise ConnectError(emsg, err) from err + + # Raise BaseExceptions un-wrapped, they're more important. + raise + @property def _runstate_event(self) -> asyncio.Event: # asyncio.Event() objects should not be created prior to entrance into @@ -343,127 +483,64 @@ class AsyncProtocol(Generic[T]): self._runstate_event.set() self._runstate_event.clear() - @upper_half - async def _new_session(self, - address: SocketAddrT, - ssl: Optional[SSLContext] = None, - accept: bool = False) -> None: + @bottom_half + async def _stop_server(self) -> None: """ - Establish a new connection and initialize the session. - - Connect or accept a new connection, then begin the protocol - session machinery. If this call fails, `runstate` is guaranteed - to be set back to `IDLE`. - - :param address: - Address to connect to/listen on; - UNIX socket path or TCP address/port. - :param ssl: SSL context to use, if any. - :param accept: Accept a connection instead of connecting when `True`. - - :raise ConnectError: - When a connection or session cannot be established. - - This exception will wrap a more concrete one. In most cases, - the wrapped exception will be `OSError` or `EOFError`. If a - protocol-level failure occurs while establishing a new - session, the wrapped error may also be an `QMPError`. + Stop listening for / accepting new incoming connections. """ - assert self.runstate == Runstate.IDLE + if self._server is None: + return try: - phase = "connection" - await self._establish_connection(address, ssl, accept) + self.logger.debug("Stopping server.") + self._server.close() + await self._server.wait_closed() + self.logger.debug("Server stopped.") + finally: + self._server = None - phase = "session" - await self._establish_session() + @bottom_half # However, it does not run from the R/W tasks. + async def _incoming(self, + reader: asyncio.StreamReader, + writer: asyncio.StreamWriter) -> None: + """ + Accept an incoming connection and signal the upper_half. - except BaseException as err: - emsg = f"Failed to establish {phase}" - self.logger.error("%s: %s", emsg, exception_summary(err)) - self.logger.debug("%s:\n%s\n", emsg, pretty_traceback()) - try: - # Reset from CONNECTING back to IDLE. - await self.disconnect() - except: - emsg = "Unexpected bottom half exception" - self.logger.critical("%s:\n%s\n", emsg, pretty_traceback()) - raise + This method does the minimum necessary to accept a single + incoming connection. It signals back to the upper_half ASAP so + that any errors during session initialization can occur + naturally in the caller's stack. - # NB: CancelledError is not a BaseException before Python 3.8 - if isinstance(err, asyncio.CancelledError): - raise + :param reader: Incoming `asyncio.StreamReader` + :param writer: Incoming `asyncio.StreamWriter` + """ + peer = writer.get_extra_info('peername', 'Unknown peer') + self.logger.debug("Incoming connection from %s", peer) - if isinstance(err, Exception): - raise ConnectError(emsg, err) from err + if self._reader or self._writer: + # Sadly, we can have more than one pending connection + # because of https://bugs.python.org/issue46715 + # Close any extra connections we don't actually want. + self.logger.warning("Extraneous connection inadvertently accepted") + writer.close() + return - # Raise BaseExceptions un-wrapped, they're more important. - raise - - assert self.runstate == Runstate.RUNNING + # A connection has been accepted; stop listening for new ones. + assert self._accepted is not None + await self._stop_server() + self._reader, self._writer = (reader, writer) + self._accepted.set() @upper_half - async def _establish_connection( - self, - address: SocketAddrT, - ssl: Optional[SSLContext] = None, - accept: bool = False - ) -> None: + async def _do_start_server(self, address: SocketAddrT, + ssl: Optional[SSLContext] = None) -> None: """ - Establish a new connection. + Start listening for an incoming connection, but do not wait for a peer. - :param address: - Address to connect to/listen on; - UNIX socket path or TCP address/port. - :param ssl: SSL context to use, if any. - :param accept: Accept a connection instead of connecting when `True`. - """ - assert self.runstate == Runstate.IDLE - self._set_state(Runstate.CONNECTING) - - # Allow runstate watchers to witness 'CONNECTING' state; some - # failures in the streaming layer are synchronous and will not - # otherwise yield. - await asyncio.sleep(0) - - if accept: - await self._do_accept(address, ssl) - else: - await self._do_connect(address, ssl) - - def _bind_hack(self, address: Union[str, Tuple[str, int]]) -> None: - """ - Used to create a socket in advance of accept(). - - This is a workaround to ensure that we can guarantee timing of - precisely when a socket exists to avoid a connection attempt - bouncing off of nothing. - - Python 3.7+ adds a feature to separate the server creation and - listening phases instead, and should be used instead of this - hack. - """ - if isinstance(address, tuple): - family = socket.AF_INET - else: - family = socket.AF_UNIX - - sock = socket.socket(family, socket.SOCK_STREAM) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - try: - sock.bind(address) - except: - sock.close() - raise - - self._sock = sock - - @upper_half - async def _do_accept(self, address: SocketAddrT, - ssl: Optional[SSLContext] = None) -> None: - """ - Acting as the transport server, accept a single connection. + This method starts listening for an incoming connection, but does not + block waiting for a peer. This call will return immediately after + binding and listening to a socket. A later call to accept() must be + made in order to finalize the incoming connection. :param address: Address to listen on; UNIX socket path or TCP address/port. @@ -471,52 +548,54 @@ class AsyncProtocol(Generic[T]): :raise OSError: For stream-related errors. """ + assert self.runstate == Runstate.IDLE + self._set_state(Runstate.CONNECTING) + self.logger.debug("Awaiting connection on %s ...", address) - connected = asyncio.Event() - server: Optional[asyncio.AbstractServer] = None - - async def _client_connected_cb(reader: asyncio.StreamReader, - writer: asyncio.StreamWriter) -> None: - """Used to accept a single incoming connection, see below.""" - nonlocal server - nonlocal connected - - # A connection has been accepted; stop listening for new ones. - assert server is not None - server.close() - await server.wait_closed() - server = None - - # Register this client as being connected - self._reader, self._writer = (reader, writer) - - # Signal back: We've accepted a client! - connected.set() + self._accepted = asyncio.Event() if isinstance(address, tuple): coro = asyncio.start_server( - _client_connected_cb, - host=None if self._sock else address[0], - port=None if self._sock else address[1], + self._incoming, + host=address[0], + port=address[1], ssl=ssl, backlog=1, limit=self._limit, - sock=self._sock, ) else: coro = asyncio.start_unix_server( - _client_connected_cb, - path=None if self._sock else address, + self._incoming, + path=address, ssl=ssl, backlog=1, limit=self._limit, - sock=self._sock, ) - server = await coro # Starts listening - await connected.wait() # Waits for the callback to fire (and finish) - assert server is None - self._sock = None + # Allow runstate watchers to witness 'CONNECTING' state; some + # failures in the streaming layer are synchronous and will not + # otherwise yield. + await asyncio.sleep(0) + + # This will start the server (bind(2), listen(2)). It will also + # call accept(2) if we yield, but we don't block on that here. + self._server = await coro + self.logger.debug("Server listening on %s", address) + + @upper_half + async def _do_accept(self) -> None: + """ + Wait for and accept an incoming connection. + + Requires that we have not yet accepted an incoming connection + from the upper_half, but it's OK if the server is no longer + running because the bottom_half has already accepted the + connection. + """ + assert self._accepted is not None + await self._accepted.wait() + assert self._server is None + self._accepted = None self.logger.debug("Connection accepted.") @@ -532,6 +611,14 @@ class AsyncProtocol(Generic[T]): :raise OSError: For stream-related errors. """ + assert self.runstate == Runstate.IDLE + self._set_state(Runstate.CONNECTING) + + # Allow runstate watchers to witness 'CONNECTING' state; some + # failures in the streaming layer are synchronous and will not + # otherwise yield. + await asyncio.sleep(0) + self.logger.debug("Connecting to %s ...", address) if isinstance(address, tuple): @@ -644,6 +731,7 @@ class AsyncProtocol(Generic[T]): self._reader = None self._writer = None + self._accepted = None # NB: _runstate_changed cannot be cleared because we still need it to # send the final runstate changed event ...! @@ -667,6 +755,9 @@ class AsyncProtocol(Generic[T]): def _done(task: Optional['asyncio.Future[Any]']) -> bool: return task is not None and task.done() + # If the server is running, stop it. + await self._stop_server() + # Are we already in an error pathway? If either of the tasks are # already done, or if we have no tasks but a reader/writer; we # must be. diff --git a/python/qemu/aqmp/qmp_client.py b/python/qemu/qmp/qmp_client.py similarity index 97% rename from python/qemu/aqmp/qmp_client.py rename to python/qemu/qmp/qmp_client.py index 90a8737f03..5dcda04a75 100644 --- a/python/qemu/aqmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -192,7 +192,7 @@ class QMPClient(AsyncProtocol[Message], Events): await self.qmp.runstate_changed.wait() await self.disconnect() - See `aqmp.events` for more detail on event handling patterns. + See `qmp.events` for more detail on event handling patterns. """ #: Logger object used for debugging messages. logger = logging.getLogger(__name__) @@ -416,7 +416,7 @@ class QMPClient(AsyncProtocol[Message], Events): @upper_half def _get_exec_id(self) -> str: - exec_id = f"__aqmp#{self._execute_id:05d}" + exec_id = f"__qmp#{self._execute_id:05d}" self._execute_id += 1 return exec_id @@ -476,7 +476,7 @@ class QMPClient(AsyncProtocol[Message], Events): An execution ID will be assigned if assign_id is `True`. It can be disabled, but this requires that an ID is manually assigned instead. For manually assigned IDs, you must not use the string - '__aqmp#' anywhere in the ID. + '__qmp#' anywhere in the ID. :param msg: The QMP `Message` to execute. :param assign_id: If True, assign a new execution ID. @@ -490,7 +490,7 @@ class QMPClient(AsyncProtocol[Message], Events): msg['id'] = self._get_exec_id() elif 'id' in msg: assert isinstance(msg['id'], str) - assert '__aqmp#' not in msg['id'] + assert '__qmp#' not in msg['id'] exec_id = await self._issue(msg) return await self._reply(exec_id) @@ -512,7 +512,7 @@ class QMPClient(AsyncProtocol[Message], Events): Assign an arbitrary execution ID to this message. If `False`, the existing id must either be absent (and no other such pending execution may omit an ID) or a string. If it is - a string, it must not start with '__aqmp#' and no other such + a string, it must not start with '__qmp#' and no other such pending execution may currently be using that ID. :return: Execution reply from the server. @@ -524,7 +524,7 @@ class QMPClient(AsyncProtocol[Message], Events): When assign_id is `False`, an ID is given, and it is not a string. :raise ValueError: When assign_id is `False`, but the ID is not usable; - Either because it starts with '__aqmp#' or it is already in-use. + Either because it starts with '__qmp#' or it is already in-use. """ # 1. convert generic Mapping or bytes to a QMP Message # 2. copy Message objects so that we assign an ID only to the copy. @@ -534,9 +534,9 @@ class QMPClient(AsyncProtocol[Message], Events): if not assign_id and 'id' in msg: if not isinstance(exec_id, str): raise TypeError(f"ID ('{exec_id}') must be a string.") - if exec_id.startswith('__aqmp#'): + if exec_id.startswith('__qmp#'): raise ValueError( - f"ID ('{exec_id}') must not start with '__aqmp#'." + f"ID ('{exec_id}') must not start with '__qmp#'." ) if not assign_id and exec_id in self._pending: diff --git a/python/qemu/aqmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py similarity index 84% rename from python/qemu/aqmp/qmp_shell.py rename to python/qemu/qmp/qmp_shell.py index d11bf54b00..619ab42ced 100644 --- a/python/qemu/aqmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -1,11 +1,12 @@ # -# Copyright (C) 2009, 2010 Red Hat Inc. +# Copyright (C) 2009-2022 Red Hat Inc. # # Authors: # Luiz Capitulino +# John Snow # -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. +# This work is licensed under the terms of the GNU LGPL, version 2 or +# later. See the COPYING file in the top-level directory. # """ @@ -86,8 +87,10 @@ import logging import os import re import readline +from subprocess import Popen import sys from typing import ( + IO, Iterator, List, NoReturn, @@ -95,8 +98,8 @@ from typing import ( Sequence, ) -from qemu.aqmp import ConnectError, QMPError, SocketAddrT -from qemu.aqmp.legacy import ( +from qemu.qmp import ConnectError, QMPError, SocketAddrT +from qemu.qmp.legacy import ( QEMUMonitorProtocol, QMPBadPortError, QMPMessage, @@ -167,8 +170,11 @@ class QMPShell(QEMUMonitorProtocol): :param verbose: Echo outgoing QMP messages to console. """ def __init__(self, address: SocketAddrT, - pretty: bool = False, verbose: bool = False): - super().__init__(address) + pretty: bool = False, + verbose: bool = False, + server: bool = False, + logfile: Optional[str] = None): + super().__init__(address, server=server) self._greeting: Optional[QMPMessage] = None self._completer = QMPCompleter() self._transmode = False @@ -177,6 +183,10 @@ class QMPShell(QEMUMonitorProtocol): '.qmp-shell_history') self.pretty = pretty self.verbose = verbose + self.logfile = None + + if logfile is not None: + self.logfile = open(logfile, "w", encoding='utf-8') def close(self) -> None: # Hook into context manager of parent to save shell history. @@ -317,11 +327,11 @@ class QMPShell(QEMUMonitorProtocol): self._cli_expr(cmdargs[1:], qmpcmd['arguments']) return qmpcmd - def _print(self, qmp_message: object) -> None: + def _print(self, qmp_message: object, fh: IO[str] = sys.stdout) -> None: jsobj = json.dumps(qmp_message, indent=4 if self.pretty else None, sort_keys=self.pretty) - print(str(jsobj)) + print(str(jsobj), file=fh) def _execute_cmd(self, cmdline: str) -> bool: try: @@ -344,6 +354,9 @@ class QMPShell(QEMUMonitorProtocol): print('Disconnected') return False self._print(resp) + if self.logfile is not None: + cmd = {**qmpcmd, **resp} + self._print(cmd, fh=self.logfile) return True def connect(self, negotiate: bool = True) -> None: @@ -409,8 +422,11 @@ class HMPShell(QMPShell): :param verbose: Echo outgoing QMP messages to console. """ def __init__(self, address: SocketAddrT, - pretty: bool = False, verbose: bool = False): - super().__init__(address, pretty, verbose) + pretty: bool = False, + verbose: bool = False, + server: bool = False, + logfile: Optional[str] = None): + super().__init__(address, pretty, verbose, server, logfile) self._cpu_index = 0 def _cmd_completion(self) -> None: @@ -503,6 +519,8 @@ def main() -> None: help='Verbose (echo commands sent and received)') parser.add_argument('-p', '--pretty', action='store_true', help='Pretty-print JSON') + parser.add_argument('-l', '--logfile', + help='Save log of all QMP messages to PATH') default_server = os.environ.get('QMP_SOCKET') parser.add_argument('qmp_server', action='store', @@ -521,7 +539,7 @@ def main() -> None: parser.error(f"Bad port number: {args.qmp_server}") return # pycharm doesn't know error() is noreturn - with shell_class(address, args.pretty, args.verbose) as qemu: + with shell_class(address, args.pretty, args.verbose, args.logfile) as qemu: try: qemu.connect(negotiate=not args.skip_negotiation) except ConnectError as err: @@ -533,5 +551,60 @@ def main() -> None: pass +def main_wrap() -> None: + """ + qmp-shell-wrap entry point: parse command line arguments and + start the REPL. + """ + parser = argparse.ArgumentParser() + parser.add_argument('-H', '--hmp', action='store_true', + help='Use HMP interface') + parser.add_argument('-v', '--verbose', action='store_true', + help='Verbose (echo commands sent and received)') + parser.add_argument('-p', '--pretty', action='store_true', + help='Pretty-print JSON') + parser.add_argument('-l', '--logfile', + help='Save log of all QMP messages to PATH') + + parser.add_argument('command', nargs=argparse.REMAINDER, + help='QEMU command line to invoke') + + args = parser.parse_args() + + cmd = args.command + if len(cmd) != 0 and cmd[0] == '--': + cmd = cmd[1:] + if len(cmd) == 0: + cmd = ["qemu-system-x86_64"] + + sockpath = "qmp-shell-wrap-%d" % os.getpid() + cmd += ["-qmp", "unix:%s" % sockpath] + + shell_class = HMPShell if args.hmp else QMPShell + + try: + address = shell_class.parse_address(sockpath) + except QMPBadPortError: + parser.error(f"Bad port number: {sockpath}") + return # pycharm doesn't know error() is noreturn + + try: + with shell_class(address, args.pretty, args.verbose, + True, args.logfile) as qemu: + with Popen(cmd): + + try: + qemu.accept() + except ConnectError as err: + if isinstance(err.exc, OSError): + die(f"Couldn't connect to {args.qmp_server}: {err!s}") + die(str(err)) + + for _ in qemu.repl(): + pass + finally: + os.unlink(sockpath) + + if __name__ == '__main__': main() diff --git a/python/qemu/aqmp/aqmp_tui.py b/python/qemu/qmp/qmp_tui.py similarity index 98% rename from python/qemu/aqmp/aqmp_tui.py rename to python/qemu/qmp/qmp_tui.py index f1e926dd75..ce239d8979 100644 --- a/python/qemu/aqmp/aqmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -3,16 +3,16 @@ # Authors: # Niteesh Babu G S # -# This work is licensed under the terms of the GNU GPL, version 2 or +# This work is licensed under the terms of the GNU LGPL, version 2 or # later. See the COPYING file in the top-level directory. """ -AQMP TUI +QMP TUI -AQMP TUI is an asynchronous interface built on top the of the AQMP library. +QMP TUI is an asynchronous interface built on top the of the QMP library. It is the successor of QMP-shell and is bought-in as a replacement for it. -Example Usage: aqmp-tui -Full Usage: aqmp-tui --help +Example Usage: qmp-tui +Full Usage: qmp-tui --help """ import argparse @@ -35,9 +35,8 @@ from pygments import token as Token import urwid import urwid_readline -from qemu.qmp import QEMUMonitorProtocol, QMPBadPortError - from .error import ProtocolError +from .legacy import QEMUMonitorProtocol, QMPBadPortError from .message import DeserializationError, Message, UnexpectedTypeError from .protocol import ConnectError, Runstate from .qmp_client import ExecInterruptedError, QMPClient @@ -130,7 +129,7 @@ def has_handler_type(logger: logging.Logger, class App(QMPClient): """ - Implements the AQMP TUI. + Implements the QMP TUI. Initializes the widgets and starts the urwid event loop. @@ -613,7 +612,7 @@ def main() -> None: Driver of the whole script, parses arguments, initialize the TUI and the logger. """ - parser = argparse.ArgumentParser(description='AQMP TUI') + parser = argparse.ArgumentParser(description='QMP TUI') parser.add_argument('qmp_server', help='Address of the QMP server. ' 'Format ') parser.add_argument('--num-retries', type=int, default=10, diff --git a/python/qemu/aqmp/util.py b/python/qemu/qmp/util.py similarity index 100% rename from python/qemu/aqmp/util.py rename to python/qemu/qmp/util.py diff --git a/python/qemu/utils/__init__.py b/python/qemu/utils/__init__.py index 7f1a5138c4..9fb273b13d 100644 --- a/python/qemu/utils/__init__.py +++ b/python/qemu/utils/__init__.py @@ -15,7 +15,11 @@ various tasks not directly related to the launching of a VM. # the COPYING file in the top-level directory. # +import os import re +import shutil +from subprocess import CalledProcessError +import textwrap from typing import Optional # pylint: disable=import-error @@ -23,6 +27,8 @@ from .accel import kvm_available, list_accel, tcg_available __all__ = ( + 'VerboseProcessError', + 'add_visual_margin', 'get_info_usernet_hostfwd_port', 'kvm_available', 'list_accel', @@ -43,3 +49,114 @@ def get_info_usernet_hostfwd_port(info_usernet_output: str) -> Optional[int]: if match is not None: return int(match[1]) return None + + +# pylint: disable=too-many-arguments +def add_visual_margin( + content: str = '', + width: Optional[int] = None, + name: Optional[str] = None, + padding: int = 1, + upper_left: str = '┏', + lower_left: str = '┗', + horizontal: str = '━', + vertical: str = '┃', +) -> str: + """ + Decorate and wrap some text with a visual decoration around it. + + This function assumes that the text decoration characters are single + characters that display using a single monospace column. + + ┏━ Example ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ┃ This is what this function looks like with text content that's + ┃ wrapped to 66 characters. The right-hand margin is left open to + ┃ accommodate the occasional unicode character that might make + ┃ predicting the total "visual" width of a line difficult. This + ┃ provides a visual distinction that's good-enough, though. + ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + :param content: The text to wrap and decorate. + :param width: + The number of columns to use, including for the decoration + itself. The default (None) uses the the available width of the + current terminal, or a fallback of 72 lines. A negative number + subtracts a fixed-width from the default size. The default obeys + the COLUMNS environment variable, if set. + :param name: A label to apply to the upper-left of the box. + :param padding: How many columns of padding to apply inside. + :param upper_left: Upper-left single-width text decoration character. + :param lower_left: Lower-left single-width text decoration character. + :param horizontal: Horizontal single-width text decoration character. + :param vertical: Vertical single-width text decoration character. + """ + if width is None or width < 0: + avail = shutil.get_terminal_size(fallback=(72, 24))[0] + if width is None: + _width = avail + else: + _width = avail + width + else: + _width = width + + prefix = vertical + (' ' * padding) + + def _bar(name: Optional[str], top: bool = True) -> str: + ret = upper_left if top else lower_left + if name is not None: + ret += f"{horizontal} {name} " + + filler_len = _width - len(ret) + ret += f"{horizontal * filler_len}" + return ret + + def _wrap(line: str) -> str: + return os.linesep.join( + textwrap.wrap( + line, width=_width - padding, initial_indent=prefix, + subsequent_indent=prefix, replace_whitespace=False, + drop_whitespace=True, break_on_hyphens=False) + ) + + return os.linesep.join(( + _bar(name, top=True), + os.linesep.join(_wrap(line) for line in content.splitlines()), + _bar(None, top=False), + )) + + +class VerboseProcessError(CalledProcessError): + """ + The same as CalledProcessError, but more verbose. + + This is useful for debugging failed calls during test executions. + The return code, signal (if any), and terminal output will be displayed + on unhandled exceptions. + """ + def summary(self) -> str: + """Return the normal CalledProcessError str() output.""" + return super().__str__() + + def __str__(self) -> str: + lmargin = ' ' + width = -len(lmargin) + sections = [] + + # Does self.stdout contain both stdout and stderr? + has_combined_output = self.stderr is None + + name = 'output' if has_combined_output else 'stdout' + if self.stdout: + sections.append(add_visual_margin(self.stdout, width, name)) + else: + sections.append(f"{name}: N/A") + + if self.stderr: + sections.append(add_visual_margin(self.stderr, width, 'stderr')) + elif not has_combined_output: + sections.append("stderr: N/A") + + return os.linesep.join(( + self.summary(), + textwrap.indent(os.linesep.join(sections), prefix=lmargin), + )) diff --git a/python/qemu/utils/qemu_ga_client.py b/python/qemu/utils/qemu_ga_client.py index 15ed430c61..8c38a7ac9c 100644 --- a/python/qemu/utils/qemu_ga_client.py +++ b/python/qemu/utils/qemu_ga_client.py @@ -50,8 +50,8 @@ from typing import ( Sequence, ) -from qemu.aqmp import ConnectError, SocketAddrT -from qemu.aqmp.legacy import QEMUMonitorProtocol +from qemu.qmp import ConnectError, SocketAddrT +from qemu.qmp.legacy import QEMUMonitorProtocol # This script has not seen many patches or careful attention in quite diff --git a/python/qemu/utils/qom.py b/python/qemu/utils/qom.py index bb5d1a78f5..bcf192f477 100644 --- a/python/qemu/utils/qom.py +++ b/python/qemu/utils/qom.py @@ -32,7 +32,7 @@ QOM commands: import argparse -from qemu.aqmp import ExecuteError +from qemu.qmp import ExecuteError from .qom_common import QOMCommand diff --git a/python/qemu/utils/qom_common.py b/python/qemu/utils/qom_common.py index e034a6f247..80da1b2304 100644 --- a/python/qemu/utils/qom_common.py +++ b/python/qemu/utils/qom_common.py @@ -27,8 +27,8 @@ from typing import ( TypeVar, ) -from qemu.aqmp import QMPError -from qemu.aqmp.legacy import QEMUMonitorProtocol +from qemu.qmp import QMPError +from qemu.qmp.legacy import QEMUMonitorProtocol class ObjectPropertyInfo: diff --git a/python/qemu/utils/qom_fuse.py b/python/qemu/utils/qom_fuse.py index 653a76b93b..8dcd59fcde 100644 --- a/python/qemu/utils/qom_fuse.py +++ b/python/qemu/utils/qom_fuse.py @@ -48,7 +48,7 @@ from typing import ( import fuse from fuse import FUSE, FuseOSError, Operations -from qemu.aqmp import ExecuteError +from qemu.qmp import ExecuteError from .qom_common import QOMCommand diff --git a/python/setup.cfg b/python/setup.cfg index 18aea2bab3..e877ea5647 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -27,7 +27,6 @@ packages = qemu.qmp qemu.machine qemu.utils - qemu.aqmp [options.package_data] * = py.typed @@ -52,7 +51,7 @@ devel = fuse = fusepy >= 2.0.4 -# AQMP TUI dependencies +# QMP TUI dependencies tui = urwid >= 2.1.2 urwid-readline >= 0.13 @@ -67,8 +66,9 @@ console_scripts = qom-tree = qemu.utils.qom:QOMTree.entry_point qom-fuse = qemu.utils.qom_fuse:QOMFuse.entry_point [fuse] qemu-ga-client = qemu.utils.qemu_ga_client:main - qmp-shell = qemu.aqmp.qmp_shell:main - aqmp-tui = qemu.aqmp.aqmp_tui:main [tui] + qmp-shell = qemu.qmp.qmp_shell:main + qmp-shell-wrap = qemu.qmp.qmp_shell:main_wrap + qmp-tui = qemu.qmp.qmp_tui:main [tui] [flake8] extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's @@ -84,7 +84,7 @@ namespace_packages = True # fusepy has no type stubs: allow_subclassing_any = True -[mypy-qemu.aqmp.aqmp_tui] +[mypy-qemu.qmp.qmp_tui] # urwid and urwid_readline have no type stubs: allow_subclassing_any = True @@ -113,7 +113,10 @@ ignore_missing_imports = True # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=consider-using-f-string, + consider-using-with, + too-many-arguments, too-many-function-args, # mypy handles this with less false positives. + too-many-instance-attributes, no-member, # mypy also handles this better. [pylint.basic] @@ -163,7 +166,6 @@ deps = .[devel] .[fuse] # Workaround to trigger tox venv rebuild .[tui] # Workaround to trigger tox venv rebuild - setuptools < 60 # Workaround, please see commit msg. commands = make check diff --git a/python/setup.py b/python/setup.py index 2014f81b75..c5bc45919a 100755 --- a/python/setup.py +++ b/python/setup.py @@ -5,9 +5,26 @@ Copyright (c) 2020-2021 John Snow for Red Hat, Inc. """ import setuptools +from setuptools.command import bdist_egg +import sys import pkg_resources +class bdist_egg_guard(bdist_egg.bdist_egg): + """ + Protect against bdist_egg from being executed + + This prevents calling 'setup.py install' directly, as the 'install' + CLI option will invoke the deprecated bdist_egg hook. "pip install" + calls the more modern bdist_wheel hook, which is what we want. + """ + def run(self): + sys.exit( + 'Installation directly via setup.py is not supported.\n' + 'Please use `pip install .` instead.' + ) + + def main(): """ QEMU tooling installer @@ -16,7 +33,7 @@ def main(): # https://medium.com/@daveshawley/safely-using-setup-cfg-for-metadata-1babbe54c108 pkg_resources.require('setuptools>=39.2') - setuptools.setup() + setuptools.setup(cmdclass={'bdist_egg': bdist_egg_guard}) if __name__ == '__main__': diff --git a/python/tests/iotests-pylint.sh b/python/tests/iotests-pylint.sh index 4cae03424b..33c5ae900a 100755 --- a/python/tests/iotests-pylint.sh +++ b/python/tests/iotests-pylint.sh @@ -1,4 +1,5 @@ #!/bin/sh -e cd ../tests/qemu-iotests/ -python3 -m linters --pylint +# See commit message for environment variable explainer. +SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m linters --pylint diff --git a/python/tests/protocol.py b/python/tests/protocol.py index 5cd7938be3..56c4d441f9 100644 --- a/python/tests/protocol.py +++ b/python/tests/protocol.py @@ -6,9 +6,9 @@ from tempfile import TemporaryDirectory import avocado -from qemu.aqmp import ConnectError, Runstate -from qemu.aqmp.protocol import AsyncProtocol, StateError -from qemu.aqmp.util import asyncio_run, create_task +from qemu.qmp import ConnectError, Runstate +from qemu.qmp.protocol import AsyncProtocol, StateError +from qemu.qmp.util import asyncio_run, create_task class NullProtocol(AsyncProtocol[None]): @@ -41,12 +41,25 @@ class NullProtocol(AsyncProtocol[None]): self.trigger_input = asyncio.Event() await super()._establish_session() - async def _do_accept(self, address, ssl=None): - if not self.fake_session: - await super()._do_accept(address, ssl) + async def _do_start_server(self, address, ssl=None): + if self.fake_session: + self._accepted = asyncio.Event() + self._set_state(Runstate.CONNECTING) + await asyncio.sleep(0) + else: + await super()._do_start_server(address, ssl) + + async def _do_accept(self): + if self.fake_session: + self._accepted = None + else: + await super()._do_accept() async def _do_connect(self, address, ssl=None): - if not self.fake_session: + if self.fake_session: + self._set_state(Runstate.CONNECTING) + await asyncio.sleep(0) + else: await super()._do_connect(address, ssl) async def _do_recv(self) -> None: @@ -170,7 +183,7 @@ class Smoke(avocado.Test): def testLogger(self): self.assertEqual( self.proto.logger.name, - 'qemu.aqmp.protocol' + 'qemu.qmp.protocol' ) def testName(self): @@ -183,7 +196,7 @@ class Smoke(avocado.Test): self.assertEqual( self.proto.logger.name, - 'qemu.aqmp.protocol.Steve' + 'qemu.qmp.protocol.Steve' ) self.assertEqual( @@ -413,14 +426,14 @@ class Accept(Connect): assert family in ('INET', 'UNIX') if family == 'INET': - await self.proto.accept(('example.com', 1)) + await self.proto.start_server_and_accept(('example.com', 1)) elif family == 'UNIX': - await self.proto.accept('/dev/null') + await self.proto.start_server_and_accept('/dev/null') async def _hanging_connection(self): - with TemporaryDirectory(suffix='.aqmp') as tmpdir: + with TemporaryDirectory(suffix='.qmp') as tmpdir: sock = os.path.join(tmpdir, type(self.proto).__name__ + ".sock") - await self.proto.accept(sock) + await self.proto.start_server_and_accept(sock) class FakeSession(TestBase): @@ -449,13 +462,13 @@ class FakeSession(TestBase): @TestBase.async_test async def testFakeAccept(self): """Test the full state lifecycle (via accept) with a no-op session.""" - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') self.assertEqual(self.proto.runstate, Runstate.RUNNING) @TestBase.async_test async def testFakeRecv(self): """Test receiving a fake/null message.""" - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') logname = self.proto.logger.name with self.assertLogs(logname, level='DEBUG') as context: @@ -471,7 +484,7 @@ class FakeSession(TestBase): @TestBase.async_test async def testFakeSend(self): """Test sending a fake/null message.""" - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') logname = self.proto.logger.name with self.assertLogs(logname, level='DEBUG') as context: @@ -493,7 +506,7 @@ class FakeSession(TestBase): ): with self.assertRaises(StateError) as context: if accept: - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') else: await self.proto.connect('/not/a/real/path') @@ -504,7 +517,7 @@ class FakeSession(TestBase): @TestBase.async_test async def testAcceptRequireRunning(self): """Test that accept() cannot be called when Runstate=RUNNING""" - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') await self._prod_session_api( Runstate.RUNNING, @@ -515,7 +528,7 @@ class FakeSession(TestBase): @TestBase.async_test async def testConnectRequireRunning(self): """Test that connect() cannot be called when Runstate=RUNNING""" - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') await self._prod_session_api( Runstate.RUNNING, @@ -526,7 +539,7 @@ class FakeSession(TestBase): @TestBase.async_test async def testAcceptRequireDisconnecting(self): """Test that accept() cannot be called when Runstate=DISCONNECTING""" - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') # Cheat: force a disconnect. await self.proto.simulate_disconnect() @@ -541,7 +554,7 @@ class FakeSession(TestBase): @TestBase.async_test async def testConnectRequireDisconnecting(self): """Test that connect() cannot be called when Runstate=DISCONNECTING""" - await self.proto.accept('/not/a/real/path') + await self.proto.start_server_and_accept('/not/a/real/path') # Cheat: force a disconnect. await self.proto.simulate_disconnect() @@ -574,9 +587,9 @@ class SimpleSession(TestBase): @TestBase.async_test async def testSmoke(self): - with TemporaryDirectory(suffix='.aqmp') as tmpdir: + with TemporaryDirectory(suffix='.qmp') as tmpdir: sock = os.path.join(tmpdir, type(self.proto).__name__ + ".sock") - server_task = create_task(self.server.accept(sock)) + server_task = create_task(self.server.start_server_and_accept(sock)) # give the server a chance to start listening [...] await asyncio.sleep(0) diff --git a/python/tests/pylint.sh b/python/tests/pylint.sh index 4b10b34db7..03d64705a1 100755 --- a/python/tests/pylint.sh +++ b/python/tests/pylint.sh @@ -1,2 +1,3 @@ #!/bin/sh -e -python3 -m pylint qemu/ +# See commit message for environment variable explainer. +SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m pylint qemu/ diff --git a/qapi/acpi.json b/qapi/acpi.json index 51f0d55db7..d148f6db9f 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -133,8 +133,9 @@ # Example: # # <- { "event": "ACPI_DEVICE_OST", -# "data": { "device": "d1", "slot": "0", -# "slot-type": "DIMM", "source": 1, "status": 0 } } +# "data": { "info": { "device": "d1", "slot": "0", +# "slot-type": "DIMM", "source": 1, "status": 0 } }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } # ## { 'event': 'ACPI_DEVICE_OST', diff --git a/qapi/block-core.json b/qapi/block-core.json index 9a5a3641d0..b66494e8c5 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1776,6 +1776,7 @@ # "file":"disks/test.qcow2", # "node-name": "my-node", # "backing_file_depth":1, +# "detect_zeroes":"off", # "bps":1000000, # "bps_rd":0, # "bps_wr":0, @@ -2078,7 +2079,7 @@ '*persistent': 'bool', '*disabled': 'bool' } } ## -# @BlockDirtyBitmapMergeSource: +# @BlockDirtyBitmapOrStr: # # @local: name of the bitmap, attached to the same node as target bitmap. # @@ -2086,7 +2087,7 @@ # # Since: 4.1 ## -{ 'alternate': 'BlockDirtyBitmapMergeSource', +{ 'alternate': 'BlockDirtyBitmapOrStr', 'data': { 'local': 'str', 'external': 'BlockDirtyBitmap' } } @@ -2105,7 +2106,7 @@ ## { 'struct': 'BlockDirtyBitmapMerge', 'data': { 'node': 'str', 'target': 'str', - 'bitmaps': ['BlockDirtyBitmapMergeSource'] } } + 'bitmaps': ['BlockDirtyBitmapOrStr'] } } ## # @block-dirty-bitmap-add: @@ -2914,13 +2915,14 @@ # @blkreplay: Since 4.2 # @compress: Since 5.0 # @copy-before-write: Since 6.2 +# @snapshot-access: Since 7.0 # # Since: 2.9 ## { 'enum': 'BlockdevDriver', 'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs', 'cloop', 'compress', 'copy-before-write', 'copy-on-read', 'dmg', - 'file', 'ftp', 'ftps', 'gluster', + 'file', 'snapshot-access', 'ftp', 'ftps', 'gluster', {'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, {'name': 'host_device', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, 'http', 'https', 'iscsi', @@ -4078,6 +4080,8 @@ # # @tls-creds: TLS credentials ID # +# @tls-hostname: TLS hostname override for certificate validation (Since 7.0) +# # @x-dirty-bitmap: A metadata context name such as "qemu:dirty-bitmap:NAME" # or "qemu:allocation-depth" to query in place of the # traditional "base:allocation" block status (see @@ -4108,6 +4112,7 @@ 'data': { 'server': 'SocketAddress', '*export': 'str', '*tls-creds': 'str', + '*tls-hostname': 'str', '*x-dirty-bitmap': { 'type': 'str', 'features': [ 'unstable' ] }, '*reconnect-delay': 'uint32', '*open-timeout': 'uint32' } } @@ -4171,11 +4176,19 @@ # # @target: The target for copy-before-write operations. # +# @bitmap: If specified, copy-before-write filter will do +# copy-before-write operations only for dirty regions of the +# bitmap. Bitmap size must be equal to length of file and +# target child of the filter. Note also, that bitmap is used +# only to initialize internal bitmap of the process, so further +# modifications (or removing) of specified bitmap doesn't +# influence the filter. (Since 7.0) +# # Since: 6.2 ## { 'struct': 'BlockdevOptionsCbw', 'base': 'BlockdevOptionsGenericFormat', - 'data': { 'target': 'BlockdevRef' } } + 'data': { 'target': 'BlockdevRef', '*bitmap': 'BlockDirtyBitmap' } } ## # @BlockdevOptions: @@ -4259,6 +4272,7 @@ 'rbd': 'BlockdevOptionsRbd', 'replication': { 'type': 'BlockdevOptionsReplication', 'if': 'CONFIG_REPLICATION' }, + 'snapshot-access': 'BlockdevOptionsGenericFormat', 'ssh': 'BlockdevOptionsSsh', 'throttle': 'BlockdevOptionsThrottle', 'vdi': 'BlockdevOptionsGenericFormat', @@ -4993,10 +5007,9 @@ # Example: # # <- { "event": "BLOCK_IMAGE_CORRUPTED", -# "data": { "device": "ide0-hd0", "node-name": "node0", -# "msg": "Prevented active L1 table overwrite", "offset": 196608, -# "size": 65536 }, -# "timestamp": { "seconds": 1378126126, "microseconds": 966463 } } +# "data": { "device": "", "node-name": "drive", "fatal": false, +# "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)" }, +# "timestamp": { "seconds": 1648243240, "microseconds": 906060 } } # # Since: 1.7 ## @@ -5046,7 +5059,8 @@ # "data": { "device": "ide0-hd1", # "node-name": "#block212", # "operation": "write", -# "action": "stop" }, +# "action": "stop", +# "reason": "No space left on device" }, # "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } # ## @@ -5212,8 +5226,8 @@ # # Example: # -# <- { "event": "BLOCK_JOB_WAITING", -# "data": { "device": "drive0", "type": "mirror" }, +# <- { "event": "BLOCK_JOB_PENDING", +# "data": { "type": "mirror", "id": "backup_1" }, # "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } # ## diff --git a/qapi/block-export.json b/qapi/block-export.json index f183522d0d..1de16d2589 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -6,6 +6,7 @@ ## { 'include': 'sockets.json' } +{ 'include': 'block-core.json' } ## # @NbdServerOptions: @@ -89,6 +90,7 @@ # @device, so the NBD client can use NBD_OPT_SET_META_CONTEXT with # the metadata context name "qemu:dirty-bitmap:BITMAP" to inspect # each bitmap. +# Since 7.1 bitmap may be specified by node/name pair. # # @allocation-depth: Also export the allocation depth map for @device, so # the NBD client can use NBD_OPT_SET_META_CONTEXT with @@ -99,7 +101,8 @@ ## { 'struct': 'BlockExportOptionsNbd', 'base': 'BlockExportOptionsNbdBase', - 'data': { '*bitmaps': ['str'], '*allocation-depth': 'bool' } } + 'data': { '*bitmaps': ['BlockDirtyBitmapOrStr'], + '*allocation-depth': 'bool' } } ## # @BlockExportOptionsVhostUserBlk: @@ -219,13 +222,13 @@ # # @hard: Drop all connections immediately and remove export. # -# Potential additional modes to be added in the future: +# TODO: Potential additional modes to be added in the future: # -# hide: Just hide export from new clients, leave existing connections as is. -# Remove export after all clients are disconnected. +# hide: Just hide export from new clients, leave existing connections as is. +# Remove export after all clients are disconnected. # -# soft: Hide export from new clients, answer with ESHUTDOWN for all further -# requests from existing clients. +# soft: Hide export from new clients, answer with ESHUTDOWN for all further +# requests from existing clients. # # Since: 2.12 ## diff --git a/qapi/block.json b/qapi/block.json index 82fcf2c914..3f100d4887 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -326,6 +326,11 @@ # @read-only-mode: change the read-only mode of the device; defaults # to 'retain' # +# @force: if false (the default), an eject request through blockdev-open-tray +# will be sent to the guest if it has locked the tray (and the tray +# will not be opened immediately); if true, the tray will be opened +# regardless of whether it is locked. (since 7.1) +# # Features: # @deprecated: Member @device is deprecated. Use @id instead. # @@ -367,6 +372,7 @@ '*id': 'str', 'filename': 'str', '*format': 'str', + '*force': 'bool', '*read-only-mode': 'BlockdevChangeReadOnlyMode' } } diff --git a/qapi/compat.json b/qapi/compat.json index c53b69fe3f..39b52872d5 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -41,7 +41,7 @@ # # Limitation: covers only syntactic aspects of QMP, i.e. stuff tagged # with feature 'deprecated'. We may want to extend it to cover -# semantic aspects, CLI, and experimental features. +# semantic aspects and CLI. # # Limitation: deprecated-output policy @hide is not implemented for # enumeration values. They behave the same as with policy @accept. diff --git a/qapi/dump.json b/qapi/dump.json index f7c4267e3f..29441af9d8 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -83,7 +83,7 @@ # Example: # # -> { "execute": "dump-guest-memory", -# "arguments": { "protocol": "fd:dump" } } +# "arguments": { "paging": false, "protocol": "fd:dump" } } # <- { "return": {} } # ## @@ -161,9 +161,10 @@ # # Example: # -# { "event": "DUMP_COMPLETED", -# "data": {"result": {"total": 1090650112, "status": "completed", -# "completed": 1090650112} } } +# <- { "event": "DUMP_COMPLETED", +# "data": { "result": { "total": 1090650112, "status": "completed", +# "completed": 1090650112 } }, +# "timestamp": { "seconds": 1648244171, "microseconds": 950316 } } # ## { 'event': 'DUMP_COMPLETED' , diff --git a/qapi/machine.json b/qapi/machine.json index 42fc68403d..d25a481ce4 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -109,7 +109,6 @@ # "socket-id": 0 # }, # "qom-path": "/machine/unattached/device[0]", -# "arch":"x86", # "target":"x86_64", # "cpu-index": 0 # }, @@ -121,7 +120,6 @@ # "socket-id": 1 # }, # "qom-path": "/machine/unattached/device[2]", -# "arch":"x86", # "target":"x86_64", # "cpu-index": 1 # } @@ -841,6 +839,7 @@ # "merge": false, # "dump": true, # "prealloc": false, +# "share": false, # "host-nodes": [0, 1], # "policy": "bind" # }, @@ -849,6 +848,7 @@ # "merge": false, # "dump": true, # "prealloc": true, +# "share": false, # "host-nodes": [2, 3], # "policy": "preferred" # } @@ -1356,7 +1356,8 @@ # Example: # # <- { "event": "MEMORY_DEVICE_SIZE_CHANGE", -# "data": { "id": "vm0", "size": 1073741824}, +# "data": { "id": "vm0", "size": 1073741824, +# "qom-path": "/machine/unattached/device[2]" }, # "timestamp": { "seconds": 1588168529, "microseconds": 201316 } } # ## diff --git a/qapi/migration.json b/qapi/migration.json index 5975a0e104..409eb086a2 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1619,7 +1619,7 @@ # # Query replication status while the vm is running. # -# Returns: A @ReplicationResult object showing the status. +# Returns: A @ReplicationStatus object showing the status. # # Example: # @@ -1679,7 +1679,7 @@ # Example: # # -> { "execute": "query-colo-status" } -# <- { "return": { "mode": "primary", "reason": "request" } } +# <- { "return": { "mode": "primary", "last-mode": "none", "reason": "request" } } # # Since: 3.1 ## @@ -1736,7 +1736,9 @@ # Since: 4.2 # # Example: -# {"event": "UNPLUG_PRIMARY", "data": {"device-id": "hostdev0"} } +# <- { "event": "UNPLUG_PRIMARY", +# "data": { "device-id": "hostdev0" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } # ## { 'event': 'UNPLUG_PRIMARY', @@ -1843,8 +1845,8 @@ # Since: 5.2 # # Example: -# {"command": "calc-dirty-rate", "data": {"calc-time": 1, -# 'sample-pages': 512} } +# {"execute": "calc-dirty-rate", "arguments": {"calc-time": 1, +# 'sample-pages': 512} } # ## { 'command': 'calc-dirty-rate', 'data': {'calc-time': 'int64', @@ -1888,7 +1890,7 @@ # Example: # # -> { "execute": "snapshot-save", -# "data": { +# "arguments": { # "job-id": "snapsave0", # "tag": "my-snap", # "vmstate": "disk0", @@ -1949,7 +1951,7 @@ # Example: # # -> { "execute": "snapshot-load", -# "data": { +# "arguments": { # "job-id": "snapload0", # "tag": "my-snap", # "vmstate": "disk0", @@ -2002,7 +2004,7 @@ # Example: # # -> { "execute": "snapshot-delete", -# "data": { +# "arguments": { # "job-id": "snapdelete0", # "tag": "my-snap", # "devices": ["disk0", "disk1"] diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 4bc45d2474..ed4a468aab 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -2,39 +2,6 @@ # vim: filetype=python # -## -# @RTC_CHANGE: -# -# Emitted when the guest changes the RTC time. -# -# @offset: offset between base RTC clock (as specified by -rtc base), and -# new RTC clock value -# -# Note: This event is rate-limited. -# -# Since: 0.13 -# -# Example: -# -# <- { "event": "RTC_CHANGE", -# "data": { "offset": 78 }, -# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } -# -## -{ 'event': 'RTC_CHANGE', - 'data': { 'offset': 'int' }, - 'if': { 'any': [ 'TARGET_ALPHA', - 'TARGET_ARM', - 'TARGET_HPPA', - 'TARGET_I386', - 'TARGET_MIPS', - 'TARGET_MIPS64', - 'TARGET_PPC', - 'TARGET_PPC64', - 'TARGET_S390X', - 'TARGET_SH4', - 'TARGET_SPARC' ] } } - ## # @rtc-reset-reinjection: # @@ -177,6 +144,8 @@ # # @cert-chain: PDH certificate chain (base64 encoded) # +# @cpu0-id: Unique ID of CPU0 (base64 encoded) (since 7.1) +# # @cbitpos: C-bit location in page table entry # # @reduced-phys-bits: Number of physical Address bit reduction when SEV is @@ -187,6 +156,7 @@ { 'struct': 'SevCapability', 'data': { 'pdh': 'str', 'cert-chain': 'str', + 'cpu0-id': 'str', 'cbitpos': 'int', 'reduced-phys-bits': 'int'}, 'if': 'TARGET_I386' } @@ -205,6 +175,7 @@ # # -> { "execute": "query-sev-capabilities" } # <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", +# "cpu0-id": "2lvmGwo+...61iEinw==", # "cbitpos": 47, "reduced-phys-bits": 5}} # ## diff --git a/qapi/misc.json b/qapi/misc.json index e8054f415b..b83cc39029 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -527,3 +527,29 @@ 'data': { '*option': 'str' }, 'returns': ['CommandLineOptionInfo'], 'allow-preconfig': true } + +## +# @RTC_CHANGE: +# +# Emitted when the guest changes the RTC time. +# +# @offset: offset in seconds between base RTC clock (as specified +# by -rtc base), and new RTC clock value +# +# @qom-path: path to the RTC object in the QOM tree +# +# Note: This event is rate-limited. +# It is not guaranteed that the RTC in the system implements +# this event, or even that the system has an RTC at all. +# +# Since: 0.13 +# +# Example: +# +# <- { "event": "RTC_CHANGE", +# "data": { "offset": 78 }, +# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +# +## +{ 'event': 'RTC_CHANGE', + 'data': { 'offset': 'int', 'qom-path': 'str' } } diff --git a/qapi/net.json b/qapi/net.json index 7fab2e7cd8..b92f3f5fb4 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -51,7 +51,7 @@ # # -> { "execute": "netdev_add", # "arguments": { "type": "user", "id": "netdev1", -# "dnssearch": "example.org" } } +# "dnssearch": [ { "str": "example.org" } ] } } # <- { "return": {} } # ## @@ -685,13 +685,15 @@ # Failover primary devices which were hidden (not hotplugged when requested) # before will now be hotplugged by the virtio-net standby device. # -# device-id: QEMU device id of the unplugged device +# @device-id: QEMU device id of the unplugged device +# # Since: 4.2 # # Example: # # <- { "event": "FAILOVER_NEGOTIATED", -# "data": "net1" } +# "data": { "device-id": "net1" }, +# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } # ## { 'event': 'FAILOVER_NEGOTIATED', diff --git a/qapi/pragma.json b/qapi/pragma.json index 3bc0335d1f..e6a021c19c 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -1,3 +1,6 @@ +# -*- Mode: Python -*- +# vim: filetype=python + { 'pragma': { 'doc-required': true } } # Whitelists to permit QAPI rule violations; think twice before you diff --git a/qapi/qapi-forward-visitor.c b/qapi/qapi-forward-visitor.c index 4ea7e0bec3..e36d9bc9ba 100644 --- a/qapi/qapi-forward-visitor.c +++ b/qapi/qapi-forward-visitor.c @@ -23,7 +23,6 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/cutils.h" -#include "qemu/option.h" struct ForwardFieldVisitor { Visitor visitor; diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c index fda7044539..63596e11c5 100644 --- a/qapi/qapi-util.c +++ b/qapi/qapi-util.c @@ -113,7 +113,7 @@ bool qapi_bool_parse(const char *name, const char *value, bool *obj, Error **err * may contain only letters, digits, hyphen and period. * The special exception for enumeration names is not implemented. * See docs/devel/qapi-code-gen.txt for more on QAPI naming rules. - * Keep this consistent with scripts/qapi.py! + * Keep this consistent with scripts/qapi-gen.py! * If @complete, the parse fails unless it consumes @str completely. * Return its length on success, -1 on failure. */ diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index d378bccac7..0990873ec8 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -21,7 +21,6 @@ #include "qapi/qmp/qjson.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" -#include "sysemu/runstate.h" #include "qapi/qmp/qbool.h" #include "qemu/coroutine.h" #include "qemu/main-loop.h" diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c index 19d3cd0038..0fe0d0a5a6 100644 --- a/qapi/qmp-event.c +++ b/qapi/qmp-event.c @@ -20,15 +20,12 @@ static void timestamp_put(QDict *qdict) { - int err; QDict *ts; - qemu_timeval tv; + int64_t rt = g_get_real_time(); - err = qemu_gettimeofday(&tv); - /* Put -1 to indicate failure of getting host time */ ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }", - err < 0 ? -1LL : (long long)tv.tv_sec, - err < 0 ? -1LL : (long long)tv.tv_usec); + (long long)rt / G_USEC_PER_SEC, + (long long)rt % G_USEC_PER_SEC); qdict_put(qdict, "timestamp", ts); } diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index f0b4c7ca9d..3e8aca6b15 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -28,7 +28,7 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/cutils.h" -#include "qemu/option.h" +#include "qemu/keyval.h" typedef struct StackObject { const char *name; /* Name of @obj in its parent, if any */ diff --git a/qapi/replay.json b/qapi/replay.json index b4d1ba253b..351898f60d 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -81,7 +81,7 @@ # # Example: # -# -> { "execute": "replay-break", "data": { "icount": 220414 } } +# -> { "execute": "replay-break", "arguments": { "icount": 220414 } } # ## { 'command': 'replay-break', 'data': { 'icount': 'int' } } @@ -117,6 +117,6 @@ # # Example: # -# -> { "execute": "replay-seek", "data": { "icount": 220414 } } +# -> { "execute": "replay-seek", "arguments": { "icount": 220414 } } ## { 'command': 'replay-seek', 'data': { 'icount': 'int' } } diff --git a/qapi/run-state.json b/qapi/run-state.json index 43d66d700f..8124220bd9 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -150,7 +150,8 @@ # # Example: # -# <- { "event": "SHUTDOWN", "data": { "guest": true }, +# <- { "event": "SHUTDOWN", +# "data": { "guest": true, "reason": "guest-shutdown" }, # "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } # ## @@ -188,7 +189,8 @@ # # Example: # -# <- { "event": "RESET", "data": { "guest": false }, +# <- { "event": "RESET", +# "data": { "guest": false, "reason": "guest-reset" }, # "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } # ## @@ -424,7 +426,8 @@ # Example: # # <- { "event": "GUEST_PANICKED", -# "data": { "action": "pause" } } +# "data": { "action": "pause" }, +# "timestamp": { "seconds": 1648245231, "microseconds": 900001 } } # ## { 'event': 'GUEST_PANICKED', @@ -444,7 +447,8 @@ # Example: # # <- { "event": "GUEST_CRASHLOADED", -# "data": { "action": "run" } } +# "data": { "action": "run" }, +# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } # ## { 'event': 'GUEST_CRASHLOADED', @@ -567,7 +571,9 @@ # <- { "event": "MEMORY_FAILURE", # "data": { "recipient": "hypervisor", # "action": "fatal", -# "flags": { 'action-required': false } } +# "flags": { "action-required": false, +# "recursive": false } }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } # ## { 'event': 'MEMORY_FAILURE', diff --git a/qapi/sockets.json b/qapi/sockets.json index 5773d9fcc4..fccc38584b 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -149,7 +149,7 @@ # # Note: This type is deprecated in favor of SocketAddress. The # difference between SocketAddressLegacy and SocketAddress is that the -# latter is has fewer {} on the wire. +# latter has fewer {} on the wire. # # Since: 1.3 ## diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c index 5506c933de..71ddc92b7b 100644 --- a/qapi/string-output-visitor.c +++ b/qapi/string-output-visitor.c @@ -14,7 +14,6 @@ #include "qemu/cutils.h" #include "qapi/string-output-visitor.h" #include "qapi/visitor-impl.h" -#include "qemu/host-utils.h" #include #include "qemu/range.h" diff --git a/qapi/trace.json b/qapi/trace.json index 119509f565..6c6982a587 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -69,7 +69,7 @@ # # -> { "execute": "trace-event-get-state", # "arguments": { "name": "qemu_memalign" } } -# <- { "return": [ { "name": "qemu_memalign", "state": "disabled" } ] } +# <- { "return": [ { "name": "qemu_memalign", "state": "disabled", "vcpu": false } ] } # ## { 'command': 'trace-event-get-state', diff --git a/qapi/ui.json b/qapi/ui.json index 9354f4c467..059302a5ef 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -10,20 +10,75 @@ { 'include': 'sockets.json' } ## -# @set_password: +# @DisplayProtocol: # -# Sets the password of a remote display session. +# Display protocols which support changing password options. +# +# Since: 7.0 +# +## +{ 'enum': 'DisplayProtocol', + 'data': [ 'vnc', 'spice' ] } + +## +# @SetPasswordAction: +# +# An action to take on changing a password on a connection with active clients. +# +# @keep: maintain existing clients +# +# @fail: fail the command if clients are connected +# +# @disconnect: disconnect existing clients +# +# Since: 7.0 +# +## +{ 'enum': 'SetPasswordAction', + 'data': [ 'keep', 'fail', 'disconnect' ] } + +## +# @SetPasswordOptions: +# +# Options for set_password. # # @protocol: - 'vnc' to modify the VNC server password # - 'spice' to modify the Spice server password # # @password: the new password # -# @connected: how to handle existing clients when changing the -# password. If nothing is specified, defaults to 'keep' -# 'fail' to fail the command if clients are connected -# 'disconnect' to disconnect existing clients -# 'keep' to maintain existing clients +# @connected: How to handle existing clients when changing the +# password. If nothing is specified, defaults to 'keep'. +# For VNC, only 'keep' is currently implemented. +# +# Since: 7.0 +# +## +{ 'union': 'SetPasswordOptions', + 'base': { 'protocol': 'DisplayProtocol', + 'password': 'str', + '*connected': 'SetPasswordAction' }, + 'discriminator': 'protocol', + 'data': { 'vnc': 'SetPasswordOptionsVnc' } } + +## +# @SetPasswordOptionsVnc: +# +# Options for set_password specific to the VNC procotol. +# +# @display: The id of the display where the password should be changed. +# Defaults to the first. +# +# Since: 7.0 +# +## +{ 'struct': 'SetPasswordOptionsVnc', + 'data': { '*display': 'str' } } + +## +# @set_password: +# +# Set the password of a remote display server. # # Returns: - Nothing on success # - If Spice is not enabled, DeviceNotFound @@ -37,15 +92,15 @@ # <- { "return": {} } # ## -{ 'command': 'set_password', - 'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} } +{ 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' } ## -# @expire_password: +# @ExpirePasswordOptions: # -# Expire the password of a remote display server. +# General options for expire_password. # -# @protocol: the name of the remote display protocol 'vnc' or 'spice' +# @protocol: - 'vnc' to modify the VNC server expiration +# - 'spice' to modify the Spice server expiration # # @time: when to expire the password. # @@ -54,16 +109,45 @@ # - '+INT' where INT is the number of seconds from now (integer) # - 'INT' where INT is the absolute time in seconds # -# Returns: - Nothing on success -# - If @protocol is 'spice' and Spice is not active, DeviceNotFound -# -# Since: 0.14 -# # Notes: Time is relative to the server and currently there is no way to # coordinate server time with client time. It is not recommended to # use the absolute time version of the @time parameter unless you're # sure you are on the same machine as the QEMU instance. # +# Since: 7.0 +# +## +{ 'union': 'ExpirePasswordOptions', + 'base': { 'protocol': 'DisplayProtocol', + 'time': 'str' }, + 'discriminator': 'protocol', + 'data': { 'vnc': 'ExpirePasswordOptionsVnc' } } + +## +# @ExpirePasswordOptionsVnc: +# +# Options for expire_password specific to the VNC procotol. +# +# @display: The id of the display where the expiration should be changed. +# Defaults to the first. +# +# Since: 7.0 +# +## + +{ 'struct': 'ExpirePasswordOptionsVnc', + 'data': { '*display': 'str' } } + +## +# @expire_password: +# +# Expire the password of a remote display server. +# +# Returns: - Nothing on success +# - If @protocol is 'spice' and Spice is not active, DeviceNotFound +# +# Since: 0.14 +# # Example: # # -> { "execute": "expire_password", "arguments": { "protocol": "vnc", @@ -71,14 +155,29 @@ # <- { "return": {} } # ## -{ 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} } +{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } + +## +# @ImageFormat: +# +# Supported image format types. +# +# @png: PNG format +# +# @ppm: PPM format +# +# Since: 7.1 +# +## +{ 'enum': 'ImageFormat', + 'data': ['ppm', 'png'] } ## # @screendump: # -# Write a PPM of the VGA screen to a file. +# Capture the contents of a screen and write it to a file. # -# @filename: the path of a new PPM file to store the image +# @filename: the path of a new file to store the image # # @device: ID of the display device that should be dumped. If this parameter # is missing, the primary display will be used. (Since 2.12) @@ -87,6 +186,8 @@ # parameter is missing, head #0 will be used. Also note that the head # can only be specified in conjunction with the device ID. (Since 2.12) # +# @format: image format for screendump. (default: ppm) (Since 7.1) +# # Returns: Nothing on success # # Since: 0.14 @@ -99,7 +200,8 @@ # ## { 'command': 'screendump', - 'data': {'filename': 'str', '*device': 'str', '*head': 'int'}, + 'data': {'filename': 'str', '*device': 'str', '*head': 'int', + '*format': 'ImageFormat'}, 'coroutine': true } ## @@ -240,8 +342,10 @@ # "enabled": true, # "auth": "spice", # "port": 5920, +# "migrated":false, # "tls-port": 5921, # "host": "0.0.0.0", +# "mouse-mode":"client", # "channels": [ # { # "port": "54924", @@ -572,6 +676,7 @@ # "host":"127.0.0.1", # "service":"50401", # "family":"ipv4" +# "websocket":false, # } # ] # } @@ -626,10 +731,10 @@ # # <- { "event": "VNC_CONNECTED", # "data": { -# "server": { "auth": "sasl", "family": "ipv4", +# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, # "service": "5901", "host": "0.0.0.0" }, # "client": { "family": "ipv4", "service": "58425", -# "host": "127.0.0.1" } }, +# "host": "127.0.0.1", "websocket": false } }, # "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } # ## @@ -654,9 +759,9 @@ # # <- { "event": "VNC_INITIALIZED", # "data": { -# "server": { "auth": "sasl", "family": "ipv4", +# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, # "service": "5901", "host": "0.0.0.0"}, -# "client": { "family": "ipv4", "service": "46089", +# "client": { "family": "ipv4", "service": "46089", "websocket": false, # "host": "127.0.0.1", "sasl_username": "luiz" } }, # "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } # @@ -681,9 +786,9 @@ # # <- { "event": "VNC_DISCONNECTED", # "data": { -# "server": { "auth": "sasl", "family": "ipv4", +# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, # "service": "5901", "host": "0.0.0.0" }, -# "client": { "family": "ipv4", "service": "58425", +# "client": { "family": "ipv4", "service": "58425", "websocket": false, # "host": "127.0.0.1", "sasl_username": "luiz" } }, # "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } # @@ -1176,6 +1281,34 @@ { 'struct' : 'DisplayCurses', 'data' : { '*charset' : 'str' } } +## +# @DisplayCocoa: +# +# Cocoa display options. +# +# @left-command-key: Enable/disable forwarding of left command key to +# guest. Allows command-tab window switching on the +# host without sending this key to the guest when +# "off". Defaults to "on" +# +# @full-grab: Capture all key presses, including system combos. This +# requires accessibility permissions, since it performs +# a global grab on key events. (default: off) +# See https://support.apple.com/en-in/guide/mac-help/mh32356/mac +# +# @swap-opt-cmd: Swap the Option and Command keys so that their key codes match +# their position on non-Mac keyboards and you can use Meta/Super +# and Alt where you expect them. (default: off) +# +# Since: 7.0 +## +{ 'struct': 'DisplayCocoa', + 'data': { + '*left-command-key': 'bool', + '*full-grab': 'bool', + '*swap-opt-cmd': 'bool' + } } + ## # @DisplayType: # @@ -1254,6 +1387,7 @@ 'discriminator' : 'type', 'data' : { 'gtk': { 'type': 'DisplayGTK', 'if': 'CONFIG_GTK' }, + 'cocoa': { 'type': 'DisplayCocoa', 'if': 'CONFIG_COCOA' }, 'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' }, 'egl-headless': { 'type': 'DisplayEGLHeadless', 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } }, @@ -1334,3 +1468,68 @@ { 'command': 'display-reload', 'data': 'DisplayReloadOptions', 'boxed' : true } + +## +# @DisplayUpdateType: +# +# Available DisplayUpdate types. +# +# @vnc: VNC display +# +# Since: 7.1 +# +## +{ 'enum': 'DisplayUpdateType', + 'data': ['vnc'] } + +## +# @DisplayUpdateOptionsVNC: +# +# Specify the VNC reload options. +# +# @addresses: If specified, change set of addresses +# to listen for connections. Addresses configured +# for websockets are not touched. +# +# Since: 7.1 +# +## +{ 'struct': 'DisplayUpdateOptionsVNC', + 'data': { '*addresses': ['SocketAddress'] } } + +## +# @DisplayUpdateOptions: +# +# Options of the display configuration reload. +# +# @type: Specify the display type. +# +# Since: 7.1 +# +## +{ 'union': 'DisplayUpdateOptions', + 'base': {'type': 'DisplayUpdateType'}, + 'discriminator': 'type', + 'data': { 'vnc': 'DisplayUpdateOptionsVNC' } } + +## +# @display-update: +# +# Update display configuration. +# +# Returns: Nothing on success. +# +# Since: 7.1 +# +# Example: +# +# -> { "execute": "display-update", +# "arguments": { "type": "vnc", "addresses": +# [ { "type": "inet", "host": "0.0.0.0", +# "port": "5901" } ] } } +# <- { "return": {} } +# +## +{ 'command': 'display-update', + 'data': 'DisplayUpdateOptions', + 'boxed' : true } diff --git a/qemu-img.c b/qemu-img.c index 6fe2466032..4cf4d2423d 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -25,7 +25,8 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" +#include "qemu/help-texts.h" +#include "qemu/qemu-progress.h" #include "qemu-version.h" #include "qapi/error.h" #include "qapi/qapi-commands-block-core.h" @@ -42,6 +43,7 @@ #include "qemu/module.h" #include "qemu/sockets.h" #include "qemu/units.h" +#include "qemu/memalign.h" #include "qom/object_interfaces.h" #include "sysemu/block-backend.h" #include "block/block_int.h" @@ -98,7 +100,8 @@ static void format_print(void *opaque, const char *name) printf(" %s", name); } -static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) +static G_NORETURN G_GNUC_PRINTF(1, 2) +void error_exit(const char *fmt, ...) { va_list ap; @@ -110,18 +113,21 @@ static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) exit(EXIT_FAILURE); } -static void QEMU_NORETURN missing_argument(const char *option) +static G_NORETURN +void missing_argument(const char *option) { error_exit("missing argument for option '%s'", option); } -static void QEMU_NORETURN unrecognized_option(const char *option) +static G_NORETURN +void unrecognized_option(const char *option) { error_exit("unrecognized option '%s'", option); } /* Please keep in synch with docs/tools/qemu-img.rst */ -static void QEMU_NORETURN help(void) +static G_NORETURN +void help(void) { const char *help_msg = QEMU_IMG_VERSION @@ -283,7 +289,7 @@ static QemuOptsList qemu_source_opts = { }, }; -static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...) +static int G_GNUC_PRINTF(2, 3) qprintf(bool quiet, const char *fmt, ...) { int ret = 0; if (!quiet) { @@ -1617,16 +1623,16 @@ static void do_dirty_bitmap_merge(const char *dst_node, const char *dst_name, const char *src_node, const char *src_name, Error **errp) { - BlockDirtyBitmapMergeSource *merge_src; - BlockDirtyBitmapMergeSourceList *list = NULL; + BlockDirtyBitmapOrStr *merge_src; + BlockDirtyBitmapOrStrList *list = NULL; - merge_src = g_new0(BlockDirtyBitmapMergeSource, 1); + merge_src = g_new0(BlockDirtyBitmapOrStr, 1); merge_src->type = QTYPE_QDICT; merge_src->u.external.node = g_strdup(src_node); merge_src->u.external.name = g_strdup(src_name); QAPI_LIST_PREPEND(list, merge_src); qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp); - qapi_free_BlockDirtyBitmapMergeSourceList(list); + qapi_free_BlockDirtyBitmapOrStrList(list); } enum ImgConvertBlockStatus { @@ -3304,11 +3310,11 @@ static int img_snapshot(int argc, char **argv) char *filename, *snapshot_name = NULL; int c, ret = 0, bdrv_oflags; int action = 0; - qemu_timeval tv; bool quiet = false; Error *err = NULL; bool image_opts = false; bool force_share = false; + int64_t rt; bdrv_oflags = BDRV_O_RDWR; /* Parse commandline parameters */ @@ -3405,9 +3411,9 @@ static int img_snapshot(int argc, char **argv) memset(&sn, 0, sizeof(sn)); pstrcpy(sn.name, sizeof(sn.name), snapshot_name); - qemu_gettimeofday(&tv); - sn.date_sec = tv.tv_sec; - sn.date_nsec = tv.tv_usec * 1000; + rt = g_get_real_time(); + sn.date_sec = rt / G_USEC_PER_SEC; + sn.date_nsec = (rt % G_USEC_PER_SEC) * 1000; ret = bdrv_snapshot_create(bs, &sn); if (ret) { @@ -5441,7 +5447,7 @@ int main(int argc, char **argv) exit(1); } trace_init_file(); - qemu_set_log(LOG_TRACE); + qemu_set_log(LOG_TRACE, &error_fatal); /* find the command */ for (cmd = img_cmds; cmd->name != NULL; cmd++) { diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 46593d632d..2f0d8ac25a 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -21,6 +21,7 @@ #include "qemu/option.h" #include "qemu/timer.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #define CMD_NOFILE_OK 0x01 @@ -603,10 +604,6 @@ static int do_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, .done = false, }; - if (bytes > INT_MAX) { - return -ERANGE; - } - co = qemu_coroutine_create(co_pwrite_zeroes_entry, &data); bdrv_coroutine_enter(blk_bs(blk), co); while (!data.done) { @@ -1160,8 +1157,9 @@ static int write_f(BlockBackend *blk, int argc, char **argv) if (count < 0) { print_cvtnum_err(count, argv[optind]); return count; - } else if (count > BDRV_REQUEST_MAX_BYTES) { - printf("length cannot exceed %" PRIu64 ", given %s\n", + } else if (count > BDRV_REQUEST_MAX_BYTES && + !(flags & BDRV_REQ_NO_FALLBACK)) { + printf("length cannot exceed %" PRIu64 " without -n, given %s\n", (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); return -EINVAL; } @@ -1993,11 +1991,9 @@ static int map_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum) { int64_t num; - int num_checked; int ret, firstret; - num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES); - ret = bdrv_is_allocated(bs, offset, num_checked, &num); + ret = bdrv_is_allocated(bs, offset, bytes, &num); if (ret < 0) { return ret; } @@ -2009,8 +2005,7 @@ static int map_is_allocated(BlockDriverState *bs, int64_t offset, offset += num; bytes -= num; - num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES); - ret = bdrv_is_allocated(bs, offset, num_checked, &num); + ret = bdrv_is_allocated(bs, offset, bytes, &num); if (ret == firstret && num) { *pnum += num; } else { diff --git a/qemu-io.c b/qemu-io.c index 3924639b92..d70d3dd4fd 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -15,7 +15,7 @@ #include #endif -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qapi/error.h" #include "qemu-io.h" #include "qemu/error-report.h" @@ -323,13 +323,13 @@ static char *get_prompt(void) static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ]; if (!prompt[0]) { - snprintf(prompt, sizeof(prompt), "%s> ", error_get_progname()); + snprintf(prompt, sizeof(prompt), "%s> ", g_get_prgname()); } return prompt; } -static void GCC_FMT_ATTR(2, 3) readline_printf_func(void *opaque, +static void G_GNUC_PRINTF(2, 3) readline_printf_func(void *opaque, const char *fmt, ...) { va_list ap; @@ -598,10 +598,10 @@ int main(int argc, char **argv) break; case 'V': printf("%s version " QEMU_FULL_VERSION "\n" - QEMU_COPYRIGHT "\n", error_get_progname()); + QEMU_COPYRIGHT "\n", g_get_prgname()); exit(0); case 'h': - usage(error_get_progname()); + usage(g_get_prgname()); exit(0); case 'U': force_share = true; @@ -613,13 +613,13 @@ int main(int argc, char **argv) imageOpts = true; break; default: - usage(error_get_progname()); + usage(g_get_prgname()); exit(1); } } if ((argc - optind) > 1) { - usage(error_get_progname()); + usage(g_get_prgname()); exit(1); } @@ -634,7 +634,7 @@ int main(int argc, char **argv) exit(1); } trace_init_file(); - qemu_set_log(LOG_TRACE); + qemu_set_log(LOG_TRACE, &error_fatal); /* initialize commands */ qemuio_add_command(&quit_cmd); diff --git a/qemu-nbd.c b/qemu-nbd.c index c6c20df68a..2382b5042a 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -21,7 +21,7 @@ #include #include -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "sysemu/block-backend.h" @@ -69,6 +69,7 @@ #define QEMU_NBD_OPT_TLSAUTHZ 264 #define QEMU_NBD_OPT_PID_FILE 265 #define QEMU_NBD_OPT_SELINUX_LABEL 266 +#define QEMU_NBD_OPT_TLSHOSTNAME 267 #define MBR_SIZE 512 @@ -542,6 +543,7 @@ int main(int argc, char **argv) { "export-name", required_argument, NULL, 'x' }, { "description", required_argument, NULL, 'D' }, { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, + { "tls-hostname", required_argument, NULL, QEMU_NBD_OPT_TLSHOSTNAME }, { "tls-authz", required_argument, NULL, QEMU_NBD_OPT_TLSAUTHZ }, { "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS }, { "trace", required_argument, NULL, 'T' }, @@ -565,9 +567,10 @@ int main(int argc, char **argv) QDict *options = NULL; const char *export_name = NULL; /* defaults to "" later for server mode */ const char *export_description = NULL; - strList *bitmaps = NULL; + BlockDirtyBitmapOrStrList *bitmaps = NULL; bool alloc_depth = false; const char *tlscredsid = NULL; + const char *tlshostname = NULL; bool imageOpts = false; bool writethrough = false; /* Client will flush as needed. */ bool fork_process = false; @@ -684,7 +687,14 @@ int main(int argc, char **argv) alloc_depth = true; break; case 'B': - QAPI_LIST_PREPEND(bitmaps, g_strdup(optarg)); + { + BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1); + *el = (BlockDirtyBitmapOrStr) { + .type = QTYPE_QSTRING, + .u.local = g_strdup(optarg), + }; + QAPI_LIST_PREPEND(bitmaps, el); + } break; case 'k': sockpath = optarg; @@ -747,6 +757,9 @@ int main(int argc, char **argv) case QEMU_NBD_OPT_TLSCREDS: tlscredsid = optarg; break; + case QEMU_NBD_OPT_TLSHOSTNAME: + tlshostname = optarg; + break; case QEMU_NBD_OPT_IMAGE_OPTS: imageOpts = true; break; @@ -798,11 +811,13 @@ int main(int argc, char **argv) exit(1); } trace_init_file(); - qemu_set_log(LOG_TRACE); + qemu_set_log(LOG_TRACE, &error_fatal); socket_activation = check_socket_activation(); if (socket_activation == 0) { - setup_address_and_port(&bindto, &port); + if (!sockpath) { + setup_address_and_port(&bindto, &port); + } } else { /* Using socket activation - check user didn't use -p etc. */ const char *err_msg = socket_activation_validate_opts(device, sockpath, @@ -823,10 +838,6 @@ int main(int argc, char **argv) } if (tlscredsid) { - if (sockpath) { - error_report("TLS is only supported with IPv4/IPv6"); - exit(EXIT_FAILURE); - } if (device) { error_report("TLS is not supported with a host device"); exit(EXIT_FAILURE); @@ -835,6 +846,10 @@ int main(int argc, char **argv) error_report("TLS authorization is incompatible with export list"); exit(EXIT_FAILURE); } + if (tlshostname && !list) { + error_report("TLS hostname is only supported with export list"); + exit(EXIT_FAILURE); + } tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err); if (local_err) { error_reportf_err(local_err, "Failed to get TLS creds: "); @@ -845,6 +860,10 @@ int main(int argc, char **argv) error_report("--tls-authz is not permitted without --tls-creds"); exit(EXIT_FAILURE); } + if (tlshostname) { + error_report("--tls-hostname is not permitted without --tls-creds"); + exit(EXIT_FAILURE); + } } if (selinux_label) { @@ -861,7 +880,8 @@ int main(int argc, char **argv) if (list) { saddr = nbd_build_socket_address(sockpath, bindto, port); - return qemu_nbd_client_list(saddr, tlscreds, bindto); + return qemu_nbd_client_list(saddr, tlscreds, + tlshostname ? tlshostname : bindto); } #if !HAVE_NBD_DEVICE @@ -889,13 +909,14 @@ int main(int argc, char **argv) if ((device && !verbose) || fork_process) { #ifndef WIN32 + g_autoptr(GError) err = NULL; int stderr_fd[2]; pid_t pid; int ret; - if (qemu_pipe(stderr_fd) < 0) { + if (!g_unix_open_pipe(stderr_fd, FD_CLOEXEC, &err)) { error_report("Error setting up communication pipe: %s", - strerror(errno)); + err->message); exit(EXIT_FAILURE); } diff --git a/qemu-options.hx b/qemu-options.hx index 094a6c1d7c..5f69b94b8e 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1916,12 +1916,18 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, #if defined(CONFIG_CURSES) "-display curses[,charset=]\n" #endif +#if defined(CONFIG_COCOA) + "-display cocoa[,full-grab=on|off][,swap-opt-cmd=on|off]\n" +#endif #if defined(CONFIG_OPENGL) "-display egl-headless[,rendernode=]\n" #endif #if defined(CONFIG_DBUS_DISPLAY) "-display dbus[,addr=]\n" " [,gl=on|core|es|off][,rendernode=]\n" +#endif +#if defined(CONFIG_COCOA) + "-display cocoa[,show-cursor=on|off][,left-command-key=on|off]\n" #endif "-display none\n" " select display backend type\n" @@ -2009,6 +2015,15 @@ SRST ``charset=CP850`` for IBM CP850 encoding. The default is ``CP437``. + ``cocoa`` + Display video output in a Cocoa window. Mac only. This interface + provides drop-down menus and other UI elements to configure and + control the VM during runtime. Valid parameters are: + + ``show-cursor=on|off`` : Force showing the mouse cursor + + ``left-command-key=on|off`` : Disable forwarding left command key to host + ``egl-headless[,rendernode=]`` Offload all OpenGL operations to a local DRI device. For any graphical display, this display needs to be paired with either @@ -2082,6 +2097,7 @@ SRST Enable SDL. ERST +#ifdef CONFIG_SPICE DEF("spice", HAS_ARG, QEMU_OPTION_spice, "-spice [port=port][,tls-port=secured-port][,x509-dir=]\n" " [,x509-key-file=][,x509-key-password=]\n" @@ -2103,6 +2119,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " enable spice\n" " at least one of {port, tls-port} is mandatory\n", QEMU_ARCH_ALL) +#endif SRST ``-spice option[,option[,...]]`` Enable the spice remote desktop protocol. Valid options are @@ -2537,6 +2554,7 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " specify SMBIOS type 3 fields\n" "-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str]\n" " [,asset=str][,part=str][,max-speed=%d][,current-speed=%d]\n" + " [,processor-id=%d]\n" " specify SMBIOS type 4 fields\n" "-smbios type=11[,value=str][,path=filename]\n" " specify SMBIOS type 11 fields\n" @@ -2562,7 +2580,7 @@ SRST ``-smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str][,sku=str]`` Specify SMBIOS type 3 fields -``-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str]`` +``-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,processor-id=%d]`` Specify SMBIOS type 4 fields ``-smbios type=11[,value=str][,path=filename]`` @@ -4158,7 +4176,9 @@ SRST ERST DEF("enable-kvm", 0, QEMU_OPTION_enable_kvm, \ - "-enable-kvm enable KVM full virtualization support\n", QEMU_ARCH_ALL) + "-enable-kvm enable KVM full virtualization support\n", + QEMU_ARCH_ARM | QEMU_ARCH_I386 | QEMU_ARCH_MIPS | QEMU_ARCH_PPC | + QEMU_ARCH_RISCV | QEMU_ARCH_S390X) SRST ``-enable-kvm`` Enable KVM full virtualization support. This option is only @@ -4604,18 +4624,14 @@ SRST ERST DEF("readconfig", HAS_ARG, QEMU_OPTION_readconfig, - "-readconfig \n", QEMU_ARCH_ALL) + "-readconfig \n" + " read config file\n", QEMU_ARCH_ALL) SRST ``-readconfig file`` Read device configuration from file. This approach is useful when you want to spawn QEMU process with many command line options but you don't want to exceed the command line character limit. ERST -DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig, - "-writeconfig \n" - " read/write config file (deprecated)\n", QEMU_ARCH_ALL) -SRST -ERST DEF("no-user-config", 0, QEMU_OPTION_nouserconfig, "-no-user-config\n" @@ -4655,16 +4671,6 @@ HXCOMM Internal use DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL) DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL) -#ifdef __linux__ -DEF("enable-fips", 0, QEMU_OPTION_enablefips, - "-enable-fips enable FIPS 140-2 compliance\n", - QEMU_ARCH_ALL) -#endif -SRST -``-enable-fips`` - Enable FIPS 140-2 compliance mode. -ERST - DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg [timestamp[=on|off]][,guest-name=[on|off]]\n" " control error message format\n" diff --git a/qemu.nsi b/qemu.nsi index c3df8c9d3b..564d617d11 100644 --- a/qemu.nsi +++ b/qemu.nsi @@ -35,6 +35,9 @@ !define OUTFILE "qemu-setup.exe" !endif +; Build a unicode installer +Unicode true + ; Use maximum compression. SetCompressor /SOLID lzma @@ -225,10 +228,7 @@ SectionEnd ; Descriptions (mouse-over). !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SectionSystem} "System emulation." - !insertmacro MUI_DESCRIPTION_TEXT ${Section_alpha} "Alpha system emulation." - !insertmacro MUI_DESCRIPTION_TEXT ${Section_alphaw} "Alpha system emulation (GUI)." - !insertmacro MUI_DESCRIPTION_TEXT ${Section_i386} "PC i386 system emulation." - !insertmacro MUI_DESCRIPTION_TEXT ${Section_i386w} "PC i386 system emulation (GUI)." +!include "${BINDIR}\system-mui-text.nsh" !insertmacro MUI_DESCRIPTION_TEXT ${SectionTools} "Tools." !ifdef DLLDIR !insertmacro MUI_DESCRIPTION_TEXT ${SectionDll} "Runtime Libraries (DLL)." diff --git a/qga/channel-posix.c b/qga/channel-posix.c index 0373975360..a996858e24 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -34,7 +34,7 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel, g_warning("error converting fd to gsocket: %s", strerror(errno)); goto out; } - qemu_set_nonblock(client_fd); + qemu_socket_set_nonblock(client_fd); ret = ga_channel_client_add(c, client_fd); if (ret) { g_warning("error setting up connection"); diff --git a/qga/commands-common.h b/qga/commands-common.h index 90785ed4bb..d0e4a9696f 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -18,4 +18,15 @@ GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp); GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh, int64_t count, Error **errp); +/** + * qga_get_host_name: + * @errp: Error object + * + * Operating system agnostic way of querying host name. + * Compared to g_get_host_name(), it doesn't cache the result. + * + * Returns allocated hostname (caller should free), NULL on failure. + */ +char *qga_get_host_name(Error **errp); + #endif diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 75dbaab68e..69f209af87 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -16,7 +16,6 @@ #include #include #include -#include "qemu-common.h" #include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" @@ -27,28 +26,17 @@ #include "qemu/base64.h" #include "qemu/cutils.h" #include "commands-common.h" +#include "block/nvme.h" #ifdef HAVE_UTMPX #include #endif -#ifndef CONFIG_HAS_ENVIRON -#ifdef __APPLE__ -#include -#define environ (*_NSGetEnviron()) -#else -extern char **environ; -#endif -#endif - #if defined(__linux__) #include #include -#include -#include -#include -#include #include +#include #ifdef CONFIG_LIBUDEV #include @@ -62,6 +50,17 @@ extern char **environ; #endif #endif +#ifdef HAVE_GETIFADDRS +#include +#include +#include +#include +#include +#ifdef CONFIG_SOLARIS +#include +#endif +#endif + static void ga_wait_child(pid_t pid, int *status, Error **errp) { pid_t rpid; @@ -88,13 +87,23 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) pid_t pid; int status; +#ifdef CONFIG_SOLARIS + const char *powerdown_flag = "-i5"; + const char *halt_flag = "-i0"; + const char *reboot_flag = "-i6"; +#else + const char *powerdown_flag = "-P"; + const char *halt_flag = "-H"; + const char *reboot_flag = "-r"; +#endif + slog("guest-shutdown called, mode: %s", mode); if (!has_mode || strcmp(mode, "powerdown") == 0) { - shutdown_flag = "-P"; + shutdown_flag = powerdown_flag; } else if (strcmp(mode, "halt") == 0) { - shutdown_flag = "-H"; + shutdown_flag = halt_flag; } else if (strcmp(mode, "reboot") == 0) { - shutdown_flag = "-r"; + shutdown_flag = reboot_flag; } else { error_setg(errp, "mode is invalid (valid values are: halt|powerdown|reboot"); @@ -109,8 +118,13 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) reopen_fd_to_null(1); reopen_fd_to_null(2); - execle("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", - "hypervisor initiated shutdown", (char *)NULL, environ); +#ifdef CONFIG_SOLARIS + execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y", + "hypervisor initiated shutdown", (char *)NULL); +#else + execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", + "hypervisor initiated shutdown", (char *)NULL); +#endif _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); @@ -136,20 +150,6 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) /* succeeded */ } -int64_t qmp_guest_get_time(Error **errp) -{ - int ret; - qemu_timeval tq; - - ret = qemu_gettimeofday(&tq); - if (ret < 0) { - error_setg_errno(errp, errno, "Failed to get time"); - return -1; - } - - return tq.tv_sec * 1000000000LL + tq.tv_usec * 1000; -} - void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { int ret; @@ -207,8 +207,7 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) /* Use '/sbin/hwclock -w' to set RTC from the system time, * or '/sbin/hwclock -s' to set the system time from RTC. */ - execle(hwclock_path, "hwclock", has_time ? "-w" : "-s", - NULL, environ); + execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL); _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); @@ -429,7 +428,11 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, /* set fd non-blocking to avoid common use cases (like reading from a * named pipe) from hanging the agent */ - qemu_set_nonblock(fileno(fh)); + if (!g_unix_set_fd_nonblocking(fileno(fh), true, NULL)) { + fclose(fh); + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return -1; + } handle = guest_file_handle_add(fh, errp); if (handle < 0) { @@ -889,7 +892,8 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, if (driver && (g_str_equal(driver, "ata_piix") || g_str_equal(driver, "sym53c8xx") || g_str_equal(driver, "virtio-pci") || - g_str_equal(driver, "ahci"))) { + g_str_equal(driver, "ahci") || + g_str_equal(driver, "nvme"))) { break; } @@ -984,6 +988,8 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, g_debug("no host for '%s' (driver '%s')", syspath, driver); goto cleanup; } + } else if (strcmp(driver, "nvme") == 0) { + disk->bus_type = GUEST_DISK_BUS_TYPE_NVME; } else { g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath); goto cleanup; @@ -1387,6 +1393,76 @@ static GuestDiskInfoList *get_disk_partitions( return ret; } +static void get_nvme_smart(GuestDiskInfo *disk) +{ + int fd; + GuestNVMeSmart *smart; + NvmeSmartLog log = {0}; + struct nvme_admin_cmd cmd = { + .opcode = NVME_ADM_CMD_GET_LOG_PAGE, + .nsid = NVME_NSID_BROADCAST, + .addr = (uintptr_t)&log, + .data_len = sizeof(log), + .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */ + | (((sizeof(log) >> 2) - 1) << 16) + }; + + fd = qemu_open_old(disk->name, O_RDONLY); + if (fd == -1) { + g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno)); + return; + } + + if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) { + g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno)); + close(fd); + return; + } + + disk->has_smart = true; + disk->smart = g_new0(GuestDiskSmart, 1); + disk->smart->type = GUEST_DISK_BUS_TYPE_NVME; + + smart = &disk->smart->u.nvme; + smart->critical_warning = log.critical_warning; + smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */ + smart->available_spare = log.available_spare; + smart->available_spare_threshold = log.available_spare_threshold; + smart->percentage_used = log.percentage_used; + smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]); + smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]); + smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]); + smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]); + smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]); + smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]); + smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]); + smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]); + smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]); + smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]); + smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]); + smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]); + smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]); + smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]); + smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]); + smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]); + smart->media_errors_lo = le64_to_cpu(log.media_errors[0]); + smart->media_errors_hi = le64_to_cpu(log.media_errors[1]); + smart->number_of_error_log_entries_lo = + le64_to_cpu(log.number_of_error_log_entries[0]); + smart->number_of_error_log_entries_hi = + le64_to_cpu(log.number_of_error_log_entries[1]); + + close(fd); +} + +static void get_disk_smart(GuestDiskInfo *disk) +{ + if (disk->has_address + && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) { + get_nvme_smart(disk); + } +} + GuestDiskInfoList *qmp_guest_get_disks(Error **errp) { GuestDiskInfoList *ret = NULL; @@ -1460,6 +1536,7 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) } get_disk_deps(disk_dir, disk); + get_disk_smart(disk); ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name); } @@ -1574,7 +1651,7 @@ static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) reopen_fd_to_null(1); reopen_fd_to_null(2); - execle(hook, hook, arg_str, NULL, environ); + execl(hook, hook, arg_str, NULL); _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); @@ -1888,7 +1965,7 @@ static int run_process_child(const char *command[], Error **errp) spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL; - success = g_spawn_sync(NULL, (char **)command, environ, spawn_flag, + success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag, NULL, NULL, NULL, NULL, &exit_status, &g_err); @@ -2154,223 +2231,6 @@ void qmp_guest_suspend_hybrid(Error **errp) guest_suspend(SUSPEND_MODE_HYBRID, errp); } -static GuestNetworkInterface * -guest_find_interface(GuestNetworkInterfaceList *head, - const char *name) -{ - for (; head; head = head->next) { - if (strcmp(head->value->name, name) == 0) { - return head->value; - } - } - - return NULL; -} - -static int guest_get_network_stats(const char *name, - GuestNetworkInterfaceStat *stats) -{ - int name_len; - char const *devinfo = "/proc/net/dev"; - FILE *fp; - char *line = NULL, *colon; - size_t n = 0; - fp = fopen(devinfo, "r"); - if (!fp) { - return -1; - } - name_len = strlen(name); - while (getline(&line, &n, fp) != -1) { - long long dummy; - long long rx_bytes; - long long rx_packets; - long long rx_errs; - long long rx_dropped; - long long tx_bytes; - long long tx_packets; - long long tx_errs; - long long tx_dropped; - char *trim_line; - trim_line = g_strchug(line); - if (trim_line[0] == '\0') { - continue; - } - colon = strchr(trim_line, ':'); - if (!colon) { - continue; - } - if (colon - name_len == trim_line && - strncmp(trim_line, name, name_len) == 0) { - if (sscanf(colon + 1, - "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld", - &rx_bytes, &rx_packets, &rx_errs, &rx_dropped, - &dummy, &dummy, &dummy, &dummy, - &tx_bytes, &tx_packets, &tx_errs, &tx_dropped, - &dummy, &dummy, &dummy, &dummy) != 16) { - continue; - } - stats->rx_bytes = rx_bytes; - stats->rx_packets = rx_packets; - stats->rx_errs = rx_errs; - stats->rx_dropped = rx_dropped; - stats->tx_bytes = tx_bytes; - stats->tx_packets = tx_packets; - stats->tx_errs = tx_errs; - stats->tx_dropped = tx_dropped; - fclose(fp); - g_free(line); - return 0; - } - } - fclose(fp); - g_free(line); - g_debug("/proc/net/dev: Interface '%s' not found", name); - return -1; -} - -/* - * Build information about guest interfaces - */ -GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) -{ - GuestNetworkInterfaceList *head = NULL, **tail = &head; - struct ifaddrs *ifap, *ifa; - - if (getifaddrs(&ifap) < 0) { - error_setg_errno(errp, errno, "getifaddrs failed"); - goto error; - } - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - GuestNetworkInterface *info; - GuestIpAddressList **address_tail; - GuestIpAddress *address_item = NULL; - GuestNetworkInterfaceStat *interface_stat = NULL; - char addr4[INET_ADDRSTRLEN]; - char addr6[INET6_ADDRSTRLEN]; - int sock; - struct ifreq ifr; - unsigned char *mac_addr; - void *p; - - g_debug("Processing %s interface", ifa->ifa_name); - - info = guest_find_interface(head, ifa->ifa_name); - - if (!info) { - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(ifa->ifa_name); - - QAPI_LIST_APPEND(tail, info); - } - - if (!info->has_hardware_address && ifa->ifa_flags & SIOCGIFHWADDR) { - /* we haven't obtained HW address yet */ - sock = socket(PF_INET, SOCK_STREAM, 0); - if (sock == -1) { - error_setg_errno(errp, errno, "failed to create socket"); - goto error; - } - - memset(&ifr, 0, sizeof(ifr)); - pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name); - if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { - error_setg_errno(errp, errno, - "failed to get MAC address of %s", - ifa->ifa_name); - close(sock); - goto error; - } - - close(sock); - mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; - - info->hardware_address = - g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x", - (int) mac_addr[0], (int) mac_addr[1], - (int) mac_addr[2], (int) mac_addr[3], - (int) mac_addr[4], (int) mac_addr[5]); - - info->has_hardware_address = true; - } - - if (ifa->ifa_addr && - ifa->ifa_addr->sa_family == AF_INET) { - /* interface with IPv4 address */ - p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { - error_setg_errno(errp, errno, "inet_ntop failed"); - goto error; - } - - address_item = g_malloc0(sizeof(*address_item)); - address_item->ip_address = g_strdup(addr4); - address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; - - if (ifa->ifa_netmask) { - /* Count the number of set bits in netmask. - * This is safe as '1' and '0' cannot be shuffled in netmask. */ - p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; - address_item->prefix = ctpop32(((uint32_t *) p)[0]); - } - } else if (ifa->ifa_addr && - ifa->ifa_addr->sa_family == AF_INET6) { - /* interface with IPv6 address */ - p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { - error_setg_errno(errp, errno, "inet_ntop failed"); - goto error; - } - - address_item = g_malloc0(sizeof(*address_item)); - address_item->ip_address = g_strdup(addr6); - address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; - - if (ifa->ifa_netmask) { - /* Count the number of set bits in netmask. - * This is safe as '1' and '0' cannot be shuffled in netmask. */ - p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; - address_item->prefix = - ctpop32(((uint32_t *) p)[0]) + - ctpop32(((uint32_t *) p)[1]) + - ctpop32(((uint32_t *) p)[2]) + - ctpop32(((uint32_t *) p)[3]); - } - } - - if (!address_item) { - continue; - } - - address_tail = &info->ip_addresses; - while (*address_tail) { - address_tail = &(*address_tail)->next; - } - QAPI_LIST_APPEND(address_tail, address_item); - - info->has_ip_addresses = true; - - if (!info->has_statistics) { - interface_stat = g_malloc0(sizeof(*interface_stat)); - if (guest_get_network_stats(info->name, interface_stat) == -1) { - info->has_statistics = false; - g_free(interface_stat); - } else { - info->statistics = interface_stat; - info->has_statistics = true; - } - } - } - - freeifaddrs(ifap); - return head; - -error: - freeifaddrs(ifap); - qapi_free_GuestNetworkInterfaceList(head); - return NULL; -} - /* Transfer online/offline status between @vcpu and the guest system. * * On input either @errp or *@errp must be NULL. @@ -2554,7 +2414,7 @@ void qmp_guest_set_user_password(const char *username, goto out; } - if (pipe(datafd) < 0) { + if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) { error_setg(errp, "cannot create pipe FDs"); goto out; } @@ -2569,9 +2429,9 @@ void qmp_guest_set_user_password(const char *username, reopen_fd_to_null(2); if (crypted) { - execle(passwd_path, "chpasswd", "-e", NULL, environ); + execl(passwd_path, "chpasswd", "-e", NULL); } else { - execle(passwd_path, "chpasswd", NULL, environ); + execl(passwd_path, "chpasswd", NULL); } _exit(EXIT_FAILURE); } else if (pid < 0) { @@ -2940,12 +2800,6 @@ void qmp_guest_suspend_hybrid(Error **errp) error_setg(errp, QERR_UNSUPPORTED); } -GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) { error_setg(errp, QERR_UNSUPPORTED); @@ -2987,6 +2841,252 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) #endif +#ifdef HAVE_GETIFADDRS +static GuestNetworkInterface * +guest_find_interface(GuestNetworkInterfaceList *head, + const char *name) +{ + for (; head; head = head->next) { + if (strcmp(head->value->name, name) == 0) { + return head->value; + } + } + + return NULL; +} + +static int guest_get_network_stats(const char *name, + GuestNetworkInterfaceStat *stats) +{ +#ifdef CONFIG_LINUX + int name_len; + char const *devinfo = "/proc/net/dev"; + FILE *fp; + char *line = NULL, *colon; + size_t n = 0; + fp = fopen(devinfo, "r"); + if (!fp) { + g_debug("failed to open network stats %s: %s", devinfo, + g_strerror(errno)); + return -1; + } + name_len = strlen(name); + while (getline(&line, &n, fp) != -1) { + long long dummy; + long long rx_bytes; + long long rx_packets; + long long rx_errs; + long long rx_dropped; + long long tx_bytes; + long long tx_packets; + long long tx_errs; + long long tx_dropped; + char *trim_line; + trim_line = g_strchug(line); + if (trim_line[0] == '\0') { + continue; + } + colon = strchr(trim_line, ':'); + if (!colon) { + continue; + } + if (colon - name_len == trim_line && + strncmp(trim_line, name, name_len) == 0) { + if (sscanf(colon + 1, + "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld", + &rx_bytes, &rx_packets, &rx_errs, &rx_dropped, + &dummy, &dummy, &dummy, &dummy, + &tx_bytes, &tx_packets, &tx_errs, &tx_dropped, + &dummy, &dummy, &dummy, &dummy) != 16) { + continue; + } + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_packets; + stats->rx_errs = rx_errs; + stats->rx_dropped = rx_dropped; + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_packets; + stats->tx_errs = tx_errs; + stats->tx_dropped = tx_dropped; + fclose(fp); + g_free(line); + return 0; + } + } + fclose(fp); + g_free(line); + g_debug("/proc/net/dev: Interface '%s' not found", name); +#else /* !CONFIG_LINUX */ + g_debug("Network stats reporting available only for Linux"); +#endif /* !CONFIG_LINUX */ + return -1; +} + +/* + * Build information about guest interfaces + */ +GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) +{ + GuestNetworkInterfaceList *head = NULL, **tail = &head; + struct ifaddrs *ifap, *ifa; + + if (getifaddrs(&ifap) < 0) { + error_setg_errno(errp, errno, "getifaddrs failed"); + goto error; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + GuestNetworkInterface *info; + GuestIpAddressList **address_tail; + GuestIpAddress *address_item = NULL; + GuestNetworkInterfaceStat *interface_stat = NULL; + char addr4[INET_ADDRSTRLEN]; + char addr6[INET6_ADDRSTRLEN]; + int sock; + struct ifreq ifr; + unsigned char *mac_addr; + void *p; + + g_debug("Processing %s interface", ifa->ifa_name); + + info = guest_find_interface(head, ifa->ifa_name); + + if (!info) { + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(ifa->ifa_name); + + QAPI_LIST_APPEND(tail, info); + } + + if (!info->has_hardware_address) { + /* we haven't obtained HW address yet */ + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) { + error_setg_errno(errp, errno, "failed to create socket"); + goto error; + } + + memset(&ifr, 0, sizeof(ifr)); + pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name); + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { + /* + * We can't get the hw addr of this interface, but that's not a + * fatal error. Don't set info->hardware_address, but keep + * going. + */ + if (errno == EADDRNOTAVAIL) { + /* The interface doesn't have a hw addr (e.g. loopback). */ + g_debug("failed to get MAC address of %s: %s", + ifa->ifa_name, strerror(errno)); + } else{ + g_warning("failed to get MAC address of %s: %s", + ifa->ifa_name, strerror(errno)); + } + + } else { +#ifdef CONFIG_SOLARIS + mac_addr = (unsigned char *) &ifr.ifr_addr.sa_data; +#else + mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#endif + info->hardware_address = + g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x", + (int) mac_addr[0], (int) mac_addr[1], + (int) mac_addr[2], (int) mac_addr[3], + (int) mac_addr[4], (int) mac_addr[5]); + + info->has_hardware_address = true; + } + close(sock); + } + + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_INET) { + /* interface with IPv4 address */ + p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { + error_setg_errno(errp, errno, "inet_ntop failed"); + goto error; + } + + address_item = g_malloc0(sizeof(*address_item)); + address_item->ip_address = g_strdup(addr4); + address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; + + if (ifa->ifa_netmask) { + /* Count the number of set bits in netmask. + * This is safe as '1' and '0' cannot be shuffled in netmask. */ + p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; + address_item->prefix = ctpop32(((uint32_t *) p)[0]); + } + } else if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_INET6) { + /* interface with IPv6 address */ + p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { + error_setg_errno(errp, errno, "inet_ntop failed"); + goto error; + } + + address_item = g_malloc0(sizeof(*address_item)); + address_item->ip_address = g_strdup(addr6); + address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; + + if (ifa->ifa_netmask) { + /* Count the number of set bits in netmask. + * This is safe as '1' and '0' cannot be shuffled in netmask. */ + p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + address_item->prefix = + ctpop32(((uint32_t *) p)[0]) + + ctpop32(((uint32_t *) p)[1]) + + ctpop32(((uint32_t *) p)[2]) + + ctpop32(((uint32_t *) p)[3]); + } + } + + if (!address_item) { + continue; + } + + address_tail = &info->ip_addresses; + while (*address_tail) { + address_tail = &(*address_tail)->next; + } + QAPI_LIST_APPEND(address_tail, address_item); + + info->has_ip_addresses = true; + + if (!info->has_statistics) { + interface_stat = g_malloc0(sizeof(*interface_stat)); + if (guest_get_network_stats(info->name, interface_stat) == -1) { + info->has_statistics = false; + g_free(interface_stat); + } else { + info->statistics = interface_stat; + info->has_statistics = true; + } + } + } + + freeifaddrs(ifap); + return head; + +error: + freeifaddrs(ifap); + qapi_free_GuestNetworkInterfaceList(head); + return NULL; +} + +#else + +GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +#endif /* HAVE_GETIFADDRS */ + #if !defined(CONFIG_FSFREEZE) GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) @@ -3049,8 +3149,7 @@ GList *ga_command_blacklist_init(GList *blacklist) { const char *list[] = { "guest-suspend-disk", "guest-suspend-ram", - "guest-suspend-hybrid", "guest-network-get-interfaces", - "guest-get-vcpus", "guest-set-vcpus", + "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus", "guest-get-memory-blocks", "guest-set-memory-blocks", "guest-get-memory-block-size", "guest-get-memory-block-info", NULL}; @@ -3062,6 +3161,11 @@ GList *ga_command_blacklist_init(GList *blacklist) } #endif +#if !defined(HAVE_GETIFADDRS) + blacklist = g_list_append(blacklist, + g_strdup("guest-network-get-interfaces")); +#endif + #if !defined(CONFIG_FSFREEZE) { const char *list[] = { @@ -3303,3 +3407,38 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) return NULL; } + +#ifndef HOST_NAME_MAX +# ifdef _POSIX_HOST_NAME_MAX +# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +# else +# define HOST_NAME_MAX 255 +# endif +#endif + +char *qga_get_host_name(Error **errp) +{ + long len = -1; + g_autofree char *hostname = NULL; + +#ifdef _SC_HOST_NAME_MAX + len = sysconf(_SC_HOST_NAME_MAX); +#endif /* _SC_HOST_NAME_MAX */ + + if (len < 0) { + len = HOST_NAME_MAX; + } + + /* Unfortunately, gethostname() below does not guarantee a + * NULL terminated string. Therefore, allocate one byte more + * to be sure. */ + hostname = g_new0(char, len + 1); + + if (gethostname(hostname, len) < 0) { + error_setg_errno(errp, errno, + "cannot get hostname"); + return NULL; + } + + return g_steal_pointer(&hostname); +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 4fbbad793f..d56b5fd2a7 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -18,10 +18,8 @@ #include #include #include -#ifdef HAVE_NTDDSCSI #include #include -#endif #include #include #include @@ -474,8 +472,6 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) } } -#ifdef HAVE_NTDDSCSI - static GuestDiskBusType win2qemu[] = { [BusTypeUnknown] = GUEST_DISK_BUS_TYPE_UNKNOWN, [BusTypeScsi] = GUEST_DISK_BUS_TYPE_SCSI, @@ -949,7 +945,7 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) } else if (last_err == ERROR_INVALID_FUNCTION) { /* Possibly CD-ROM or a shared drive. Try to pass the volume */ g_debug("volume not on disk"); - disk = g_malloc0(sizeof(GuestDiskAddress)); + disk = g_new0(GuestDiskAddress, 1); disk->has_dev = true; disk->dev = g_strdup(name); get_single_disk_info(0xffffffff, disk, &local_err); @@ -972,7 +968,7 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) /* Go through each extent */ for (i = 0; i < extents->NumberOfDiskExtents; i++) { - disk = g_malloc0(sizeof(GuestDiskAddress)); + disk = g_new0(GuestDiskAddress, 1); /* Disk numbers directly correspond to numbers used in UNCs * @@ -1076,7 +1072,7 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) sdn.DeviceNumber); g_debug(" number: %lu", sdn.DeviceNumber); - address = g_malloc0(sizeof(GuestDiskAddress)); + address = g_new0(GuestDiskAddress, 1); address->has_dev = true; address->dev = g_strdup(disk->name); get_single_disk_info(sdn.DeviceNumber, address, &local_err); @@ -1098,21 +1094,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) return ret; } -#else - -static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) -{ - return NULL; -} - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif /* HAVE_NTDDSCSI */ - static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) { DWORD info_size; @@ -1368,7 +1349,7 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) continue; } - uc_path = g_malloc(sizeof(WCHAR) * char_count); + uc_path = g_new(WCHAR, char_count); if (!GetVolumePathNamesForVolumeNameW(guid, uc_path, char_count, &char_count) || !*uc_path) { /* strange, but this condition could be faced even with size == 2 */ @@ -1751,25 +1732,6 @@ static int64_t filetime_to_ns(const FILETIME *tf) - W32_FT_OFFSET) * 100; } -int64_t qmp_guest_get_time(Error **errp) -{ - SYSTEMTIME ts = {0}; - FILETIME tf; - - GetSystemTime(&ts); - if (ts.wYear < 1601 || ts.wYear > 30827) { - error_setg(errp, "Failed to get time"); - return -1; - } - - if (!SystemTimeToFileTime(&ts, &tf)) { - error_setg(errp, "Failed to convert system time: %d", (int)GetLastError()); - return -1; - } - - return filetime_to_ns(&tf); -} - void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { Error *local_err = NULL; @@ -2557,3 +2519,16 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) } return head; } + +char *qga_get_host_name(Error **errp) +{ + wchar_t tmp[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD size = G_N_ELEMENTS(tmp); + + if (GetComputerNameW(tmp, &size) == 0) { + error_setg_win32(errp, GetLastError(), "failed close handle"); + return NULL; + } + + return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL); +} diff --git a/qga/commands.c b/qga/commands.c index 80501e4a73..7ff551d092 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -18,7 +18,6 @@ #include "qapi/qmp/qerror.h" #include "qemu/base64.h" #include "qemu/cutils.h" -#include "qemu/atomic.h" #include "commands-common.h" /* Maximum captured guest-exec out_data/err_data - 16MB */ @@ -162,13 +161,12 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) ges = g_new0(GuestExecStatus, 1); - bool finished = qatomic_mb_read(&gei->finished); + bool finished = gei->finished; /* need to wait till output channels are closed * to be sure we captured all output at this point */ if (gei->has_output) { - finished = finished && qatomic_mb_read(&gei->out.closed); - finished = finished && qatomic_mb_read(&gei->err.closed); + finished &= gei->out.closed && gei->err.closed; } ges->exited = finished; @@ -244,7 +242,7 @@ static char **guest_exec_get_args(const strList *entry, bool log) str = g_malloc(str_size); *str = 0; - args = g_malloc(count * sizeof(char *)); + args = g_new(char *, count); for (it = entry; it != NULL; it = it->next) { args[i++] = it->value; pstrcat(str, str_size, it->value); @@ -270,7 +268,7 @@ static void guest_exec_child_watch(GPid pid, gint status, gpointer data) (int32_t)gpid_to_int64(pid), (uint32_t)status); gei->status = status; - qatomic_mb_set(&gei->finished, true); + gei->finished = true; g_spawn_close_pid(pid); } @@ -326,7 +324,7 @@ static gboolean guest_exec_input_watch(GIOChannel *ch, done: g_io_channel_shutdown(ch, true, NULL); g_io_channel_unref(ch); - qatomic_mb_set(&p->closed, true); + p->closed = true; g_free(p->data); return false; @@ -380,7 +378,7 @@ static gboolean guest_exec_output_watch(GIOChannel *ch, close: g_io_channel_shutdown(ch, true, NULL); g_io_channel_unref(ch); - qatomic_mb_set(&p->closed, true); + p->closed = true; return false; } @@ -511,7 +509,7 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp) GuestHostName *qmp_guest_get_host_name(Error **errp) { GuestHostName *result = NULL; - g_autofree char *hostname = qemu_get_host_name(errp); + g_autofree char *hostname = qga_get_host_name(errp); /* * We want to avoid using g_get_host_name() because that @@ -585,3 +583,8 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, return read_data; } + +int64_t qmp_guest_get_time(Error **errp) +{ + return g_get_real_time() * 1000; +} diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index 9d01ea9c82..29cd50402f 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -36,7 +36,7 @@ void ga_command_state_free(GACommandState *cs); bool ga_logging_enabled(GAState *s); void ga_disable_logging(GAState *s); void ga_enable_logging(GAState *s); -void GCC_FMT_ATTR(1, 2) slog(const gchar *fmt, ...); +void G_GNUC_PRINTF(1, 2) slog(const gchar *fmt, ...); void ga_set_response_delimited(GAState *s); bool ga_is_frozen(GAState *s); void ga_set_frozen(GAState *s); diff --git a/qga/main.c b/qga/main.c index 15fd3a4149..3b9546c185 100644 --- a/qga/main.c +++ b/qga/main.c @@ -18,7 +18,7 @@ #include #include #endif -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" @@ -27,7 +27,6 @@ #include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "channel.h" -#include "qemu/bswap.h" #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/sockets.h" @@ -130,12 +129,12 @@ static void stop_agent(GAState *s, bool requested); static void init_dfl_pathnames(void) { + g_autofree char *state = qemu_get_local_state_dir(); + g_assert(dfl_pathnames.state_dir == NULL); g_assert(dfl_pathnames.pidfile == NULL); - dfl_pathnames.state_dir = qemu_get_local_state_pathname( - QGA_STATE_RELATIVE_DIR); - dfl_pathnames.pidfile = qemu_get_local_state_pathname( - QGA_STATE_RELATIVE_DIR G_DIR_SEPARATOR_S "qemu-ga.pid"); + dfl_pathnames.state_dir = g_build_filename(state, QGA_STATE_RELATIVE_DIR, NULL); + dfl_pathnames.pidfile = g_build_filename(state, QGA_STATE_RELATIVE_DIR, "qemu-ga.pid", NULL); } static void quit_handler(int sig) @@ -315,7 +314,6 @@ static void ga_log(const gchar *domain, GLogLevelFlags level, const gchar *msg, gpointer opaque) { GAState *s = opaque; - GTimeVal time; const char *level_str = ga_log_level_str(level); if (!ga_logging_enabled(s)) { @@ -330,9 +328,9 @@ static void ga_log(const gchar *domain, GLogLevelFlags level, #else if (level & s->log_level) { #endif - g_get_current_time(&time); - fprintf(s->log_file, - "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg); + g_autoptr(GDateTime) now = g_date_time_new_now_utc(); + g_autofree char *nowstr = g_date_time_format(now, "%s.%f"); + fprintf(s->log_file, "%s: %s: %s\n", nowstr, level_str, msg); fflush(s->log_file); } } @@ -610,7 +608,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) * host-side chardev. sleep a bit to mitigate this */ if (s->virtio) { - usleep(100 * 1000); + g_usleep(G_USEC_PER_SEC / 10); } return true; default: diff --git a/qga/meson.build b/qga/meson.build index 54f2da5b07..6d9f39bb32 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -15,7 +15,7 @@ have_qga_vss = get_option('qga_vss') \ If your Visual Studio installation doesn't have the VSS headers, Please download and install Microsoft VSS SDK: http://www.microsoft.com/en-us/download/details.aspx?id=23490 - On POSIX-systems, MinGW doesn't yet provide working headers. + On POSIX-systems, MinGW should provide headers in >=10.0 releases. you can extract the SDK headers by: $ scripts/extract-vsssdk-headers setup.exe The headers are extracted in the directory 'inc/win2003'. @@ -83,14 +83,12 @@ qga_ss = qga_ss.apply(config_host, strict: false) gen_tlb = [] qga_libs = [] if targetos == 'windows' - qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32'] + qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32', + '-lsetupapi', '-lcfgmgr32'] if have_qga_vss qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup'] subdir('vss-win32') endif - if have_ntddscsi - qga_libs += ['-lsetupapi', '-lcfgmgr32'] - endif endif qga = executable('qemu-ga', qga_ss.sources(), @@ -131,7 +129,7 @@ if targetos == 'windows' wixl, '-o', '@OUTPUT0@', '@INPUT0@', qemu_ga_msi_arch[cpu], qemu_ga_msi_vss, - '-D', 'Mingw_dlls=' + config_host['QEMU_GA_MSI_MINGW_DLL_PATH'], + '-D', 'Mingw_bin=' + config_host['QEMU_GA_MSI_MINGW_BIN_PATH'], ]) all_qga += [qga_msi] alias_target('msi', qga_msi) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 94e4aacdcc..4d8e506c9e 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -827,13 +827,14 @@ # @mmc: Win multimedia card (MMC) bus type # @virtual: Win virtual bus type # @file-backed-virtual: Win file-backed bus type +# @nvme: NVMe disks (since 7.1) # # Since: 2.2; 'Unknown' and all entries below since 2.4 ## { 'enum': 'GuestDiskBusType', 'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata', 'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi', - 'sas', 'mmc', 'virtual', 'file-backed-virtual' ] } + 'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ] } ## @@ -887,6 +888,55 @@ '*serial': 'str', '*dev': 'str', '*ccw-address': 'GuestCCWAddress'} } +## +# @GuestNVMeSmart: +# +# NVMe smart informations, based on NVMe specification, +# section +# +# Since: 7.1 +## +{ 'struct': 'GuestNVMeSmart', + 'data': {'critical-warning': 'int', + 'temperature': 'int', + 'available-spare': 'int', + 'available-spare-threshold': 'int', + 'percentage-used': 'int', + 'data-units-read-lo': 'uint64', + 'data-units-read-hi': 'uint64', + 'data-units-written-lo': 'uint64', + 'data-units-written-hi': 'uint64', + 'host-read-commands-lo': 'uint64', + 'host-read-commands-hi': 'uint64', + 'host-write-commands-lo': 'uint64', + 'host-write-commands-hi': 'uint64', + 'controller-busy-time-lo': 'uint64', + 'controller-busy-time-hi': 'uint64', + 'power-cycles-lo': 'uint64', + 'power-cycles-hi': 'uint64', + 'power-on-hours-lo': 'uint64', + 'power-on-hours-hi': 'uint64', + 'unsafe-shutdowns-lo': 'uint64', + 'unsafe-shutdowns-hi': 'uint64', + 'media-errors-lo': 'uint64', + 'media-errors-hi': 'uint64', + 'number-of-error-log-entries-lo': 'uint64', + 'number-of-error-log-entries-hi': 'uint64' } } + +## +# @GuestDiskSmart: +# +# Disk type related smart information. +# +# - @nvme: NVMe disk smart +# +# Since: 7.1 +## +{ 'union': 'GuestDiskSmart', + 'base': { 'type': 'GuestDiskBusType' }, + 'discriminator': 'type', + 'data': { 'nvme': 'GuestNVMeSmart' } } + ## # @GuestDiskInfo: # @@ -898,12 +948,14 @@ # @address: disk address information (only for non-virtual devices) # @alias: optional alias assigned to the disk, on Linux this is a name assigned # by device mapper +# @smart: disk smart information (Since 7.1) # # Since 5.2 ## { 'struct': 'GuestDiskInfo', 'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'], - '*address': 'GuestDiskAddress', '*alias': 'str'} } + '*address': 'GuestDiskAddress', '*alias': 'str', + '*smart': 'GuestDiskSmart'} } ## # @guest-get-disks: diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index efc5bb9909..b57508fbe0 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -13,7 +13,11 @@ #include "qemu/osdep.h" #include "vss-common.h" +#ifdef HAVE_VSS_SDK #include +#else +#include +#endif #include "install.h" #include #include @@ -42,7 +46,8 @@ void errmsg(DWORD err, const char *text) * If text doesn't contains '(', negative precision is given, which is * treated as though it were missing. */ - char *msg = NULL, *nul = strchr(text, '('); + char *msg = NULL; + const char *nul = strchr(text, '('); int len = nul ? nul - text : -1; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index fd187fb66f..1b885e24ee 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -12,7 +12,11 @@ #include "qemu/osdep.h" #include "vss-common.h" +#ifdef HAVE_VSS_SDK #include +#else +#include +#endif #include #define VSS_TIMEOUT_MSEC (60*1000) diff --git a/qga/vss-win32/requester.h b/qga/vss-win32/requester.h index 5a8e8faf0c..ecc5f2acb4 100644 --- a/qga/vss-win32/requester.h +++ b/qga/vss-win32/requester.h @@ -25,7 +25,7 @@ struct Error; typedef void (*ErrorSetFunc)(struct Error **errp, const char *src, int line, const char *func, int win32_err, const char *fmt, ...) - GCC_FMT_ATTR(6, 7); + G_GNUC_PRINTF(6, 7); typedef struct ErrorSet { ErrorSetFunc error_setg_win32_wrapper; struct Error **errp; /* restriction: must not be null */ diff --git a/qga/vss-win32/vss-common.h b/qga/vss-win32/vss-common.h index 54f8de8c88..0e67e7822c 100644 --- a/qga/vss-win32/vss-common.h +++ b/qga/vss-win32/vss-common.h @@ -64,12 +64,13 @@ const CLSID CLSID_QGAVSSProvider = { 0x6e6a3492, 0x8d4d, 0x440c, const TCHAR g_szClsid[] = TEXT("{6E6A3492-8D4D-440C-9619-5E5D0CC31CA8}"); const TCHAR g_szProgid[] = TEXT("QGAVSSProvider"); +#ifdef HAVE_VSS_SDK /* Enums undefined in VSS SDK 7.2 but defined in newer Windows SDK */ enum __VSS_VOLUME_SNAPSHOT_ATTRIBUTES { VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x00000002, VSS_VOLSNAP_ATTR_TXF_RECOVERY = 0x02000000 }; - +#endif /* COM pointer utility; call ->Release() when it goes out of scope */ template diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index 1487cc5dd8..4a83bda2c3 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -251,12 +251,12 @@ void qdict_array_split(QDict *src, QList **dst) if (is_subqdict) { qdict_extract_subqdict(src, &subqdict, prefix); assert(qdict_size(subqdict) > 0); + qlist_append_obj(*dst, QOBJECT(subqdict)); } else { qobject_ref(subqobj); qdict_del(src, indexstr); + qlist_append_obj(*dst, subqobj); } - - qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); } } diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 008b326fb8..d498db6e70 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -54,7 +54,7 @@ static QObject *parse_value(JSONParserContext *ctxt); /** * Error handler */ -static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, +static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserContext *ctxt, JSONToken *token, const char *msg, ...) { va_list ap; diff --git a/qobject/qbool.c b/qobject/qbool.c index 16a600abb9..c7049c0c50 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -56,3 +56,8 @@ void qbool_destroy_obj(QObject *obj) assert(obj != NULL); g_free(qobject_to(QBool, obj)); } + +void qbool_unref(QBool *q) +{ + qobject_unref(q); +} diff --git a/qobject/qdict.c b/qobject/qdict.c index 0216ca7ac1..8faff230d3 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -442,3 +442,8 @@ void qdict_destroy_obj(QObject *obj) g_free(qdict); } + +void qdict_unref(QDict *q) +{ + qobject_unref(q); +} diff --git a/qobject/qlist.c b/qobject/qlist.c index 60562a1f52..356ad946b0 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -182,3 +182,8 @@ void qlist_destroy_obj(QObject *obj) g_free(qlist); } + +void qlist_unref(QList *q) +{ + qobject_unref(q); +} diff --git a/qobject/qnull.c b/qobject/qnull.c index b26b368219..445a5db7f3 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -29,3 +29,8 @@ bool qnull_is_equal(const QObject *x, const QObject *y) { return true; } + +void qnull_unref(QNull *q) +{ + qobject_unref(q); +} diff --git a/qobject/qnum.c b/qobject/qnum.c index 5dd66938dd..2bbeaedc7b 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -239,3 +239,8 @@ void qnum_destroy_obj(QObject *obj) assert(obj != NULL); g_free(qobject_to(QNum, obj)); } + +void qnum_unref(QNum *q) +{ + qobject_unref(q); +} diff --git a/qobject/qstring.c b/qobject/qstring.c index b4613899b9..794f8c9357 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -100,3 +100,8 @@ void qstring_destroy_obj(QObject *obj) g_free((char *)qs->string); g_free(qs); } + +void qstring_unref(QString *q) +{ + qobject_unref(q); +} diff --git a/qom/object.c b/qom/object.c index 9f7a33139d..d34608558e 100644 --- a/qom/object.c +++ b/qom/object.c @@ -16,6 +16,7 @@ #include "qom/object.h" #include "qom/object_interfaces.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #include "qapi/visitor.h" #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" @@ -1167,10 +1168,14 @@ GSList *object_class_get_list_sorted(const char *implements_type, Object *object_ref(void *objptr) { Object *obj = OBJECT(objptr); + uint32_t ref; + if (!obj) { return NULL; } - qatomic_inc(&obj->ref); + ref = qatomic_fetch_inc(&obj->ref); + /* Assert waaay before the integer overflows */ + g_assert(ref < INT_MAX); return obj; } diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 3b61c195c5..f94b6c3193 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -17,6 +17,7 @@ #include "qemu/qemu-print.h" #include "qapi/opts-visitor.h" #include "qemu/config-file.h" +#include "qemu/keyval.h" bool user_creatable_complete(UserCreatable *uc, Error **errp) { diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 2d6f41ecc7..2e63a4c184 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -49,7 +49,7 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) object_property_iter_init(&iter, obj); while ((prop = object_property_iter_next(&iter))) { - ObjectPropertyInfo *value = g_malloc0(sizeof(ObjectPropertyInfo)); + ObjectPropertyInfo *value = g_new0(ObjectPropertyInfo, 1); QAPI_LIST_PREPEND(props, value); diff --git a/replay/replay-char.c b/replay/replay-char.c index dc0002367e..d2025948cf 100644 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -50,7 +50,7 @@ void replay_register_char_driver(Chardev *chr) void replay_chr_be_write(Chardev *s, uint8_t *buf, int len) { - CharEvent *event = g_malloc0(sizeof(CharEvent)); + CharEvent *event = g_new0(CharEvent, 1); event->id = find_char_driver(s); if (event->id < 0) { @@ -85,7 +85,7 @@ void replay_event_char_read_save(void *opaque) void *replay_event_char_read_load(void) { - CharEvent *event = g_malloc0(sizeof(CharEvent)); + CharEvent *event = g_new0(CharEvent, 1); event->id = replay_get_byte(); replay_get_array_alloc(&event->buf, &event->len); diff --git a/replay/replay-events.c b/replay/replay-events.c index 15983dd250..ac47c89834 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -119,7 +119,7 @@ void replay_add_event(ReplayAsyncEventKind event_kind, return; } - Event *event = g_malloc0(sizeof(Event)); + Event *event = g_new0(Event, 1); event->event_kind = event_kind; event->opaque = opaque; event->opaque2 = opaque2; @@ -243,17 +243,17 @@ static Event *replay_read_event(int checkpoint) } break; case REPLAY_ASYNC_EVENT_INPUT: - event = g_malloc0(sizeof(Event)); + event = g_new0(Event, 1); event->event_kind = replay_state.read_event_kind; event->opaque = replay_read_input_event(); return event; case REPLAY_ASYNC_EVENT_INPUT_SYNC: - event = g_malloc0(sizeof(Event)); + event = g_new0(Event, 1); event->event_kind = replay_state.read_event_kind; event->opaque = 0; return event; case REPLAY_ASYNC_EVENT_CHAR_READ: - event = g_malloc0(sizeof(Event)); + event = g_new0(Event, 1); event->event_kind = replay_state.read_event_kind; event->opaque = replay_event_char_read_load(); return event; @@ -263,7 +263,7 @@ static Event *replay_read_event(int checkpoint) } break; case REPLAY_ASYNC_EVENT_NET: - event = g_malloc0(sizeof(Event)); + event = g_new0(Event, 1); event->event_kind = replay_state.read_event_kind; event->opaque = replay_event_net_load(); return event; diff --git a/roms/Makefile.edk2 b/roms/Makefile.edk2 index fdae0b511f..485f2244b1 100644 --- a/roms/Makefile.edk2 +++ b/roms/Makefile.edk2 @@ -13,6 +13,7 @@ SHELL = /bin/bash +target = RELEASE toolchain = $(shell source ./edk2-funcs.sh && qemu_edk2_get_toolchain $(1)) licenses := \ @@ -32,6 +33,7 @@ flashdevs := \ i386-secure-code \ x86_64-code \ x86_64-secure-code \ + x86_64-microvm \ \ arm-vars \ i386-vars @@ -50,7 +52,7 @@ all: $(foreach flashdev,$(flashdevs),../pc-bios/edk2-$(flashdev).fd.bz2) \ # we're building from a tarball and that they've already been fetched by # make-release/tarball scripts. submodules: - if test -d edk2/.git; then \ + if test -e edk2/.git; then \ cd edk2 && git submodule update --init --force -- \ ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ BaseTools/Source/C/BrotliCompress/brotli \ @@ -73,7 +75,7 @@ submodules: -D NETWORK_TLS_ENABLE \ -D TPM2_ENABLE \ -D TPM2_CONFIG_ENABLE - cp edk2/Build/ArmVirtQemu-AARCH64/DEBUG_$(call toolchain,aarch64)/FV/QEMU_EFI.fd \ + cp edk2/Build/ArmVirtQemu-AARCH64/$(target)_$(call toolchain,aarch64)/FV/QEMU_EFI.fd \ $@ truncate --size=64M $@ @@ -87,7 +89,7 @@ submodules: -D NETWORK_TLS_ENABLE \ -D TPM2_ENABLE \ -D TPM2_CONFIG_ENABLE - cp edk2/Build/ArmVirtQemu-ARM/DEBUG_$(call toolchain,arm)/FV/QEMU_EFI.fd \ + cp edk2/Build/ArmVirtQemu-ARM/$(target)_$(call toolchain,arm)/FV/QEMU_EFI.fd \ $@ truncate --size=64M $@ @@ -101,7 +103,7 @@ submodules: -D NETWORK_TLS_ENABLE \ -D TPM_ENABLE \ -D TPM_CONFIG_ENABLE - cp edk2/Build/OvmfIa32/DEBUG_$(call toolchain,i386)/FV/OVMF_CODE.fd $@ + cp edk2/Build/OvmfIa32/$(target)_$(call toolchain,i386)/FV/OVMF_CODE.fd $@ ../pc-bios/edk2-i386-secure-code.fd: submodules +./edk2-build.sh \ @@ -115,7 +117,7 @@ submodules: -D TPM_CONFIG_ENABLE \ -D SECURE_BOOT_ENABLE \ -D SMM_REQUIRE - cp edk2/Build/OvmfIa32/DEBUG_$(call toolchain,i386)/FV/OVMF_CODE.fd $@ + cp edk2/Build/OvmfIa32/$(target)_$(call toolchain,i386)/FV/OVMF_CODE.fd $@ ../pc-bios/edk2-x86_64-code.fd: submodules +./edk2-build.sh \ @@ -127,7 +129,7 @@ submodules: -D NETWORK_TLS_ENABLE \ -D TPM_ENABLE \ -D TPM_CONFIG_ENABLE - cp edk2/Build/OvmfX64/DEBUG_$(call toolchain,x86_64)/FV/OVMF_CODE.fd $@ + cp edk2/Build/OvmfX64/$(target)_$(call toolchain,x86_64)/FV/OVMF_CODE.fd $@ ../pc-bios/edk2-x86_64-secure-code.fd: submodules +./edk2-build.sh \ @@ -142,15 +144,25 @@ submodules: -D TPM_CONFIG_ENABLE \ -D SECURE_BOOT_ENABLE \ -D SMM_REQUIRE - cp edk2/Build/Ovmf3264/DEBUG_$(call toolchain,x86_64)/FV/OVMF_CODE.fd $@ + cp edk2/Build/Ovmf3264/$(target)_$(call toolchain,x86_64)/FV/OVMF_CODE.fd $@ + +../pc-bios/edk2-x86_64-microvm.fd: submodules + +./edk2-build.sh \ + x86_64 \ + --arch=X64 \ + --platform=OvmfPkg/Microvm/MicrovmX64.dsc \ + -D NETWORK_IP6_ENABLE \ + -D NETWORK_HTTP_BOOT_ENABLE \ + -D NETWORK_TLS_ENABLE + cp edk2/Build/MicrovmX64/$(target)_$(call toolchain,x86_64)/FV/MICROVM.fd $@ ../pc-bios/edk2-arm-vars.fd: ../pc-bios/edk2-arm-code.fd - cp edk2/Build/ArmVirtQemu-ARM/DEBUG_$(call toolchain,arm)/FV/QEMU_VARS.fd \ + cp edk2/Build/ArmVirtQemu-ARM/$(target)_$(call toolchain,arm)/FV/QEMU_VARS.fd \ $@ truncate --size=64M $@ ../pc-bios/edk2-i386-vars.fd: ../pc-bios/edk2-i386-code.fd - cp edk2/Build/OvmfIa32/DEBUG_$(call toolchain,i386)/FV/OVMF_VARS.fd $@ + cp edk2/Build/OvmfIa32/$(target)_$(call toolchain,i386)/FV/OVMF_VARS.fd $@ # The license file accumulates several individual licenses from under edk2, # prefixing each individual license with a header (generated by "tail") that diff --git a/roms/edk2 b/roms/edk2 index 06dc822d04..b24306f15d 160000 --- a/roms/edk2 +++ b/roms/edk2 @@ -1 +1 @@ -Subproject commit 06dc822d045c2bb42e497487935485302486e151 +Subproject commit b24306f15daa2ff8510b06702114724b33895d3c diff --git a/roms/edk2-build.sh b/roms/edk2-build.sh index d5391c7637..ea79dc27a2 100755 --- a/roms/edk2-build.sh +++ b/roms/edk2-build.sh @@ -50,6 +50,6 @@ qemu_edk2_set_cross_env "$emulation_target" build \ --cmd-len=65536 \ -n "$edk2_thread_count" \ - --buildtarget=DEBUG \ + --buildtarget=RELEASE \ --tagname="$edk2_toolchain" \ "${args[@]}" diff --git a/roms/openbios b/roms/openbios index 04dfc984ac..0e0afae657 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit 04dfc984ac0ad70e2d1f58d1121e3b7670901124 +Subproject commit 0e0afae6579c1efe9f0d85505b75ffe989554133 diff --git a/roms/seabios b/roms/seabios index 6a62e0cb0d..d239552ce7 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit 6a62e0cb0dfe9cd28b70547dbea5caf76847c3a9 +Subproject commit d239552ce7220e448ae81f41515138f7b9e3c4db diff --git a/roms/skiboot b/roms/skiboot index 820d43c0a7..24a7eb3596 160000 --- a/roms/skiboot +++ b/roms/skiboot @@ -1 +1 @@ -Subproject commit 820d43c0a7751e75a8830561f35535dfffd522bd +Subproject commit 24a7eb35966d93455520bc2debdd7954314b638b diff --git a/scripts/analyze-inclusions b/scripts/analyze-inclusions index 14806e18c6..45c821de32 100644 --- a/scripts/analyze-inclusions +++ b/scripts/analyze-inclusions @@ -46,7 +46,6 @@ grep_include() { } echo Found $(find . -name "*.d" | wc -l) object files -echo $(grep_include -F 'include/qemu-common.h') files include qemu-common.h echo $(grep_include -F 'hw/hw.h') files include hw/hw.h echo $(grep_include 'target/[a-z0-9]*/cpu\.h') files include cpu.h echo $(grep_include -F 'qapi-types.h') files include qapi-types.h @@ -86,9 +85,6 @@ analyze() { echo osdep.h: analyze ../include/qemu/osdep.h -echo qemu-common.h: -analyze -include ../include/qemu/osdep.h ../include/qemu-common.h - echo hw/hw.h: analyze -include ../include/qemu/osdep.h ../include/hw/hw.h diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index ddd8148d87..4763d02ae7 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -223,11 +223,11 @@ our $Sparse = qr{ our $Attribute = qr{ const| volatile| - QEMU_NORETURN| - QEMU_WARN_UNUSED_RESULT| - QEMU_SENTINEL| + G_NORETURN| + G_GNUC_WARN_UNUSED_RESULT| + G_GNUC_NULL_TERMINATED| QEMU_PACKED| - GCC_FMT_ATTR + G_GNUC_PRINTF }x; our $Modifier; our $Inline = qr{inline}; @@ -2974,10 +2974,10 @@ sub process { ERROR("use memset() instead of bzero()\n" . $herecurr); } if ($line =~ /\bgetpagesize\(\)/) { - ERROR("use qemu_real_host_page_size instead of getpagesize()\n" . $herecurr); + ERROR("use qemu_real_host_page_size() instead of getpagesize()\n" . $herecurr); } if ($line =~ /\bsysconf\(_SC_PAGESIZE\)/) { - ERROR("use qemu_real_host_page_size instead of sysconf(_SC_PAGESIZE)\n" . $herecurr); + ERROR("use qemu_real_host_page_size() instead of sysconf(_SC_PAGESIZE)\n" . $herecurr); } my $non_exit_glib_asserts = qr{g_assert_cmpstr| g_assert_cmpint| diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure index 9850dd4444..08225ee514 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ b/scripts/ci/org.centos/stream/8/x86_64/configure @@ -142,7 +142,7 @@ --disable-virtiofsd \ --disable-vnc \ --disable-vnc-jpeg \ ---disable-vnc-png \ +--disable-png \ --disable-vnc-sasl \ --disable-vte \ --disable-vvfat \ @@ -200,7 +200,7 @@ --enable-vhost-vdpa \ --enable-vhost-vsock \ --enable-vnc \ ---enable-vnc-png \ +--enable-png \ --enable-vnc-sasl \ --enable-werror \ --enable-xkbcommon diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/build-environment.yml index 599896cc5b..9182e0c253 100644 --- a/scripts/ci/setup/build-environment.yml +++ b/scripts/ci/setup/build-environment.yml @@ -19,6 +19,13 @@ - '((ansible_version.major == 2) and (ansible_version.minor >= 8)) or (ansible_version.major >= 3)' msg: "Unsuitable ansible version, please use version 2.8.0 or later" + - name: Add armhf foreign architecture to aarch64 hosts + command: dpkg --add-architecture armhf + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['architecture'] == 'aarch64' + - ansible_facts['distribution_version'] == '20.04' + - name: Update apt cache / upgrade packages via apt apt: update_cache: yes @@ -115,6 +122,24 @@ - ansible_facts['distribution'] == 'Ubuntu' - ansible_facts['distribution_version'] == '20.04' + - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 20.04 + package: + name: + - binutils-arm-linux-gnueabihf + - gcc-arm-linux-gnueabihf + - libblkid-dev:armhf + - libc6-dev:armhf + - libffi-dev:armhf + - libglib2.0-dev:armhf + - libmount-dev:armhf + - libpcre2-dev:armhf + - libpixman-1-dev:armhf + - zlib1g-dev:armhf + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['distribution_version'] == '20.04' + - ansible_facts['architecture'] == 'aarch64' + - name: Install basic packages to build QEMU on EL8 dnf: # This list of packages start with tests/docker/dockerfiles/centos8.docker diff --git a/scripts/ci/setup/gitlab-runner.yml b/scripts/ci/setup/gitlab-runner.yml index 1127db516f..33128be85d 100644 --- a/scripts/ci/setup/gitlab-runner.yml +++ b/scripts/ci/setup/gitlab-runner.yml @@ -69,3 +69,41 @@ name: gitlab-runner state: started enabled: yes + + - name: Download secondary gitlab-runner + get_url: + dest: /usr/local/bin/gitlab-runner-arm + url: "https://s3.amazonaws.com/gitlab-runner-downloads/v{{ gitlab_runner_version }}/binaries/gitlab-runner-{{ gitlab_runner_os }}-arm" + owner: gitlab-runner + group: gitlab-runner + mode: u=rwx,g=rwx,o=rx + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['architecture'] == 'aarch64' + - ansible_facts['distribution_version'] == '20.04' + + - name: Register secondary gitlab-runner + command: "/usr/local/bin/gitlab-runner-arm register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list aarch32,{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['architecture'] == 'aarch64' + - ansible_facts['distribution_version'] == '20.04' + + - name: Install the secondary gitlab-runner service using its own functionality + command: /usr/local/bin/gitlab-runner-arm install --user gitlab-runner --working-directory /home/gitlab-runner/arm -n gitlab-runner-arm + register: gitlab_runner_install_service_result + failed_when: "gitlab_runner_install_service_result.rc != 0 and \"already exists\" not in gitlab_runner_install_service_result.stderr" + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['architecture'] == 'aarch64' + - ansible_facts['distribution_version'] == '20.04' + + - name: Enable the secondary gitlab-runner service + service: + name: gitlab-runner-arm + state: started + enabled: yes + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['architecture'] == 'aarch64' + - ansible_facts['distribution_version'] == '20.04' diff --git a/scripts/cocci-macro-file.h b/scripts/cocci-macro-file.h index 20eea6b708..d247a5086e 100644 --- a/scripts/cocci-macro-file.h +++ b/scripts/cocci-macro-file.h @@ -19,9 +19,9 @@ */ /* From qemu/compiler.h */ -#define QEMU_NORETURN __attribute__ ((__noreturn__)) -#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#define QEMU_SENTINEL __attribute__((sentinel)) +#define G_NORETURN __attribute__ ((__noreturn__)) +#define G_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#define G_GNUC_NULL_TERMINATED __attribute__((sentinel)) #if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) # define QEMU_PACKED __attribute__((gcc_struct, packed)) @@ -34,7 +34,7 @@ #define QEMU_BUILD_BUG_ON(x) \ typedef char cat2(qemu_build_bug_on__,__LINE__)[(x)?-1:1] __attribute__((unused)); -#define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) +#define G_GNUC_PRINTF(n, m) __attribute__((format(gnu_printf, n, m))) #define xglue(x, y) x ## y #define glue(x, y) xglue(x, y) diff --git a/scripts/coccinelle/use-g_new-etc.cocci b/scripts/coccinelle/use-g_new-etc.cocci new file mode 100644 index 0000000000..e2280e93b3 --- /dev/null +++ b/scripts/coccinelle/use-g_new-etc.cocci @@ -0,0 +1,75 @@ +// Use g_new() & friends where that makes obvious sense +@@ +type T; +@@ +-g_malloc(sizeof(T)) ++g_new(T, 1) +@@ +type T; +@@ +-g_try_malloc(sizeof(T)) ++g_try_new(T, 1) +@@ +type T; +@@ +-g_malloc0(sizeof(T)) ++g_new0(T, 1) +@@ +type T; +@@ +-g_try_malloc0(sizeof(T)) ++g_try_new0(T, 1) +@@ +type T; +expression n; +@@ +-g_malloc(sizeof(T) * (n)) ++g_new(T, n) +@@ +type T; +expression n; +@@ +-g_try_malloc(sizeof(T) * (n)) ++g_try_new(T, n) +@@ +type T; +expression n; +@@ +-g_malloc0(sizeof(T) * (n)) ++g_new0(T, n) +@@ +type T; +expression n; +@@ +-g_try_malloc0(sizeof(T) * (n)) ++g_try_new0(T, n) +@@ +type T; +expression p, n; +@@ +-g_realloc(p, sizeof(T) * (n)) ++g_renew(T, p, n) +@@ +type T; +expression p, n; +@@ +-g_try_realloc(p, sizeof(T) * (n)) ++g_try_renew(T, p, n) +@@ +type T; +expression n; +@@ +-(T *)g_new(T, n) ++g_new(T, n) +@@ +type T; +expression n; +@@ +-(T *)g_new0(T, n) ++g_new0(T, n) +@@ +type T; +expression p, n; +@@ +-(T *)g_renew(T, p, n) ++g_renew(T, p, n) diff --git a/scripts/coverity-scan/model.c b/scripts/coverity-scan/model.c index 9d4fba53d9..686d1a3008 100644 --- a/scripts/coverity-scan/model.c +++ b/scripts/coverity-scan/model.c @@ -356,7 +356,8 @@ int g_poll (GPollFD *fds, unsigned nfds, int timeout) typedef struct _GIOChannel GIOChannel; GIOChannel *g_io_channel_unix_new(int fd) { - GIOChannel *c = g_malloc0(sizeof(GIOChannel)); + /* cannot use incomplete type, the actual struct is roughly this size. */ + GIOChannel *c = g_malloc0(20 * sizeof(void *)); __coverity_escape__(fd); return c; } diff --git a/scripts/coverity-scan/run-coverity-scan b/scripts/coverity-scan/run-coverity-scan index 181bdcb263..129672c86f 100755 --- a/scripts/coverity-scan/run-coverity-scan +++ b/scripts/coverity-scan/run-coverity-scan @@ -394,7 +394,7 @@ echo "Configuring..." --enable-opengl --enable-vte --enable-gnutls \ --enable-nettle --enable-curses --enable-curl \ --audio-drv-list=oss,alsa,sdl,pa --enable-virtfs \ - --enable-vnc --enable-vnc-sasl --enable-vnc-jpeg --enable-vnc-png \ + --enable-vnc --enable-vnc-sasl --enable-vnc-jpeg --enable-png \ --enable-xen --enable-brlapi \ --enable-linux-aio --enable-attr \ --enable-cap-ng --enable-trace-backends=log --enable-spice --enable-rbd \ diff --git a/scripts/cpu-x86-uarch-abi.py b/scripts/cpu-x86-uarch-abi.py index c262d2f027..82ff07582f 100644 --- a/scripts/cpu-x86-uarch-abi.py +++ b/scripts/cpu-x86-uarch-abi.py @@ -6,7 +6,7 @@ # compatibility levels for each CPU model. # -from qemu.aqmp.legacy import QEMUMonitorProtocol +from qemu.qmp.legacy import QEMUMonitorProtocol import sys if len(sys.argv) != 2: diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 7fbd99158b..4bfc68c008 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -36,7 +36,7 @@ from itertools import chain sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) from qemu.machine import QEMUMachine -from qemu.aqmp import ConnectError +from qemu.qmp import ConnectError logger = logging.getLogger('device-crash-test') dbg = logger.debug @@ -517,7 +517,7 @@ def main(): # Async QMP, when in use, is chatty about connection failures. # This script knowingly generates a ton of connection errors. # Silence this logger. - logging.getLogger('qemu.aqmp.qmp_client').setLevel(logging.CRITICAL) + logging.getLogger('qemu.qmp.qmp_client').setLevel(logging.CRITICAL) fatal_failures = [] wl_stats = {} diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index 6fe66d5f57..f140040104 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -249,6 +249,7 @@ controls = [ bits = { 0: 'Execute-only EPT translations', 6: 'Page-walk length 4', + 7: 'Page-walk length 5', 8: 'Paging-structure memory type UC', 14: 'Paging-structure memory type WB', 16: '2MB EPT pages', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 9ee684ef03..21366b2102 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -19,8 +19,9 @@ meson_options_help() { printf "%s\n" ' --disable-install-blobs install provided firmware blobs' printf "%s\n" ' --enable-malloc=CHOICE choose memory allocator to use [system] (choices:' printf "%s\n" ' jemalloc/system/tcmalloc)' + printf "%s\n" ' --enable-module-upgrades try to load modules from alternate paths for' + printf "%s\n" ' upgrades' printf "%s\n" ' --enable-profiler profiler support' - printf "%s\n" ' --enable-qga-vss build QGA VSS support' printf "%s\n" ' --enable-qom-cast-debug cast debugging support' printf "%s\n" ' --enable-rng-none dummy RNG, avoid using /dev/(u)random and' printf "%s\n" ' getrandom()' @@ -60,6 +61,7 @@ meson_options_help() { printf "%s\n" ' fuse-lseek SEEK_HOLE/SEEK_DATA support for FUSE exports' printf "%s\n" ' gcrypt libgcrypt cryptography support' printf "%s\n" ' gettext Localization of the GTK+ user interface' + printf "%s\n" ' gio use libgio for D-Bus support' printf "%s\n" ' glusterfs Glusterfs block device driver' printf "%s\n" ' gnutls GNUTLS cryptography support' printf "%s\n" ' gtk GTK+ user interface' @@ -69,6 +71,7 @@ meson_options_help() { printf "%s\n" ' hvf HVF acceleration support' printf "%s\n" ' iconv Font glyph conversion support' printf "%s\n" ' jack JACK sound support' + printf "%s\n" ' keyring Linux keyring support' printf "%s\n" ' kvm KVM acceleration support' printf "%s\n" ' l2tpv3 l2tpv3 network backend support' printf "%s\n" ' libdaxctl libdaxctl support' @@ -92,12 +95,17 @@ meson_options_help() { printf "%s\n" ' nettle nettle cryptography support' printf "%s\n" ' numa libnuma support' printf "%s\n" ' nvmm NVMM acceleration support' + printf "%s\n" ' opengl OpenGL support' printf "%s\n" ' oss OSS sound support' printf "%s\n" ' pa PulseAudio sound support' printf "%s\n" ' parallels parallels image format support' + printf "%s\n" ' png PNG support with libpng' + printf "%s\n" ' pvrdma Enable PVRDMA support' printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qed qed image format support' + printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)' printf "%s\n" ' rbd Ceph block device driver' + printf "%s\n" ' rdma Enable RDMA-based migration' printf "%s\n" ' replication replication support' printf "%s\n" ' sdl SDL user interface' printf "%s\n" ' sdl-image SDL Image support for icons' @@ -123,7 +131,6 @@ meson_options_help() { printf "%s\n" ' virtiofsd build virtiofs daemon (virtiofsd)' printf "%s\n" ' vnc VNC server' printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server' - printf "%s\n" ' vnc-png PNG compression for VNC server' printf "%s\n" ' vnc-sasl SASL authentication for VNC server' printf "%s\n" ' vte vte support for the gtk UI' printf "%s\n" ' vvfat vvfat image format support' @@ -204,6 +211,8 @@ _meson_option_parse() { --disable-gcrypt) printf "%s" -Dgcrypt=disabled ;; --enable-gettext) printf "%s" -Dgettext=enabled ;; --disable-gettext) printf "%s" -Dgettext=disabled ;; + --enable-gio) printf "%s" -Dgio=enabled ;; + --disable-gio) printf "%s" -Dgio=disabled ;; --enable-glusterfs) printf "%s" -Dglusterfs=enabled ;; --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; @@ -226,6 +235,8 @@ _meson_option_parse() { --disable-install-blobs) printf "%s" -Dinstall_blobs=false ;; --enable-jack) printf "%s" -Djack=enabled ;; --disable-jack) printf "%s" -Djack=disabled ;; + --enable-keyring) printf "%s" -Dkeyring=enabled ;; + --disable-keyring) printf "%s" -Dkeyring=disabled ;; --enable-kvm) printf "%s" -Dkvm=enabled ;; --disable-kvm) printf "%s" -Dkvm=disabled ;; --enable-l2tpv3) printf "%s" -Dl2tpv3=enabled ;; @@ -259,6 +270,8 @@ _meson_option_parse() { --disable-malloc-trim) printf "%s" -Dmalloc_trim=disabled ;; --enable-membarrier) printf "%s" -Dmembarrier=enabled ;; --disable-membarrier) printf "%s" -Dmembarrier=disabled ;; + --enable-module-upgrades) printf "%s" -Dmodule_upgrades=true ;; + --disable-module-upgrades) printf "%s" -Dmodule_upgrades=false ;; --enable-mpath) printf "%s" -Dmpath=enabled ;; --disable-mpath) printf "%s" -Dmpath=disabled ;; --enable-multiprocess) printf "%s" -Dmultiprocess=enabled ;; @@ -271,14 +284,20 @@ _meson_option_parse() { --disable-numa) printf "%s" -Dnuma=disabled ;; --enable-nvmm) printf "%s" -Dnvmm=enabled ;; --disable-nvmm) printf "%s" -Dnvmm=disabled ;; + --enable-opengl) printf "%s" -Dopengl=enabled ;; + --disable-opengl) printf "%s" -Dopengl=disabled ;; --enable-oss) printf "%s" -Doss=enabled ;; --disable-oss) printf "%s" -Doss=disabled ;; --enable-pa) printf "%s" -Dpa=enabled ;; --disable-pa) printf "%s" -Dpa=disabled ;; --enable-parallels) printf "%s" -Dparallels=enabled ;; --disable-parallels) printf "%s" -Dparallels=disabled ;; + --enable-png) printf "%s" -Dpng=enabled ;; + --disable-png) printf "%s" -Dpng=disabled ;; --enable-profiler) printf "%s" -Dprofiler=true ;; --disable-profiler) printf "%s" -Dprofiler=false ;; + --enable-pvrdma) printf "%s" -Dpvrdma=enabled ;; + --disable-pvrdma) printf "%s" -Dpvrdma=disabled ;; --enable-qcow1) printf "%s" -Dqcow1=enabled ;; --disable-qcow1) printf "%s" -Dqcow1=disabled ;; --enable-qed) printf "%s" -Dqed=enabled ;; @@ -289,6 +308,8 @@ _meson_option_parse() { --disable-qom-cast-debug) printf "%s" -Dqom_cast_debug=false ;; --enable-rbd) printf "%s" -Drbd=enabled ;; --disable-rbd) printf "%s" -Drbd=disabled ;; + --enable-rdma) printf "%s" -Drdma=enabled ;; + --disable-rdma) printf "%s" -Drdma=disabled ;; --enable-replication) printf "%s" -Dreplication=enabled ;; --disable-replication) printf "%s" -Dreplication=disabled ;; --enable-rng-none) printf "%s" -Drng_none=true ;; @@ -347,8 +368,6 @@ _meson_option_parse() { --disable-vnc) printf "%s" -Dvnc=disabled ;; --enable-vnc-jpeg) printf "%s" -Dvnc_jpeg=enabled ;; --disable-vnc-jpeg) printf "%s" -Dvnc_jpeg=disabled ;; - --enable-vnc-png) printf "%s" -Dvnc_png=enabled ;; - --disable-vnc-png) printf "%s" -Dvnc_png=disabled ;; --enable-vnc-sasl) printf "%s" -Dvnc_sasl=enabled ;; --disable-vnc-sasl) printf "%s" -Dvnc_sasl=disabled ;; --enable-vte) printf "%s" -Dvte=enabled ;; diff --git a/scripts/modinfo-collect.py b/scripts/modinfo-collect.py index 61b90688c6..4e7584df66 100755 --- a/scripts/modinfo-collect.py +++ b/scripts/modinfo-collect.py @@ -18,13 +18,8 @@ def find_command(src, target, compile_commands): def process_command(src, command): skip = False - arg = False out = [] for item in shlex.split(command): - if arg: - out.append(x) - arg = False - continue if skip: skip = False continue diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index 4d542e8aaa..304634b71e 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py @@ -101,10 +101,6 @@ targets = {t['id']: [os.path.relpath(f) for f in t['filename']] testsuites = defaultdict(Suite) for test in introspect['tests']: process_tests(test, targets, testsuites) -# HACK: check-block is a separate target so that it runs with --verbose; -# only write the dependencies -emit_suite_deps('block', testsuites['block'], 'check') -del testsuites['block'] emit_prolog(testsuites, 'check') for name, suite in testsuites.items(): emit_suite(name, suite, 'check') diff --git a/scripts/nsis.py b/scripts/nsis.py index 5135a05831..462d6cac3b 100644 --- a/scripts/nsis.py +++ b/scripts/nsis.py @@ -33,10 +33,12 @@ def main(): subprocess.run(["make", "install", "DESTDIR=" + destdir + os.path.sep]) with open( os.path.join(destdir + args.prefix, "system-emulations.nsh"), "w" - ) as nsh: - for exe in glob.glob( + ) as nsh, open( + os.path.join(destdir + args.prefix, "system-mui-text.nsh"), "w" + ) as muinsh: + for exe in sorted(glob.glob( os.path.join(destdir + args.prefix, "qemu-system-*.exe") - ): + )): exe = os.path.basename(exe) arch = exe[12:-4] nsh.write( @@ -49,6 +51,15 @@ def main(): arch, exe ) ) + if arch.endswith('w'): + desc = arch[:-1] + " emulation (GUI)." + else: + desc = arch + " emulation." + + muinsh.write( + """ + !insertmacro MUI_DESCRIPTION_TEXT ${{Section_{0}}} "{1}" + """.format(arch, desc)) for exe in glob.glob(os.path.join(destdir + args.prefix, "*.exe")): signcode(exe) diff --git a/scripts/oss-fuzz/output_reproducer.py b/scripts/oss-fuzz/output_reproducer.py index 3608b0600e..e8ef76b341 100755 --- a/scripts/oss-fuzz/output_reproducer.py +++ b/scripts/oss-fuzz/output_reproducer.py @@ -36,7 +36,7 @@ def c_header(owner): #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" """.format(date=date.today().year, owner=owner) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 869d799ed2..38ca38a7b9 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -25,8 +25,8 @@ from .gen import ( QAPIGenC, QAPISchemaModularCVisitor, build_params, - ifcontext, gen_special_features, + ifcontext, ) from .schema import ( QAPISchema, diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index 3cb389e875..48578e1698 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -554,7 +554,7 @@ def check_alternate(expr: _JSONObject, info: QAPISourceInfo) -> None: check_name_lower(key, info, source) check_keys(value, info, source, ['type'], ['if']) check_if(value, info, source) - check_type(value['type'], info, source) + check_type(value['type'], info, source, allow_array=True) def check_command(expr: _JSONObject, info: QAPISourceInfo) -> None: diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index b259531a72..a724628203 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -34,16 +34,12 @@ disable=fixme, [BASIC] -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - ex, - Run, - _, - fp, # fp = open(...) - fd, # fd = os.open(...) - ch, +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted. +# +# Suppress complaints about short names. PEP-8 is cool with them, +# and so are we. +good-names-rgxs=^[_a-z][_a-z0-9]?$ [VARIABLES] diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index b7b3fc0ce4..3728340c37 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -243,6 +243,7 @@ class QAPISchemaType(QAPISchemaEntity): 'number': 'QTYPE_QNUM', 'int': 'QTYPE_QNUM', 'boolean': 'QTYPE_QBOOL', + 'array': 'QTYPE_QLIST', 'object': 'QTYPE_QDICT' } return json2qtype.get(self.json_type()) @@ -1069,6 +1070,9 @@ class QAPISchema: None)) def _make_variant(self, case, typ, ifcond, info): + if isinstance(typ, list): + assert len(typ) == 1 + typ = self._make_array_type(typ[0], info) return QAPISchemaVariant(case, info, typ, ifcond) def _def_union_type(self, expr, info, doc): diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 3013329c24..477d027001 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -16,7 +16,11 @@ This work is licensed under the terms of the GNU GPL, version 2. from typing import List, Optional from .common import c_enum_const, c_name, mcgen -from .gen import QAPISchemaModularCVisitor, gen_special_features, ifcontext +from .gen import ( + QAPISchemaModularCVisitor, + gen_special_features, + ifcontext, +) from .schema import ( QAPISchema, QAPISchemaEnumMember, diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index e13bbe4292..380fa197f5 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -21,7 +21,11 @@ from .common import ( indent, mcgen, ) -from .gen import QAPISchemaModularCVisitor, gen_special_features, ifcontext +from .gen import ( + QAPISchemaModularCVisitor, + gen_special_features, + ifcontext, +) from .schema import ( QAPISchema, QAPISchemaEnumMember, diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index e9bfeb94d3..9cb723f443 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -60,28 +60,28 @@ m68k_family=m68k # FIXME: We could use the other endianness on a MIPS host. -mips_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08' -mips_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +mips_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' +mips_mask='\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' mips_family=mips -mipsel_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00' -mipsel_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +mipsel_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' +mipsel_mask='\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00' mipsel_family=mips -mipsn32_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08' -mipsn32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +mipsn32_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' +mipsn32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' mipsn32_family=mips -mipsn32el_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00' -mipsn32el_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +mipsn32el_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00' +mipsn32el_mask='\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00' mipsn32el_family=mips mips64_magic='\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08' -mips64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +mips64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' mips64_family=mips mips64el_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00' -mips64el_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +mips64el_mask='\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' mips64el_family=mips sh4_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00' diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 31b19d73e2..4a20f97db7 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -4,7 +4,7 @@ import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) -from qemu.aqmp import qmp_shell +from qemu.qmp import qmp_shell if __name__ == '__main__': diff --git a/scripts/qmp/qmp-shell-wrap b/scripts/qmp/qmp-shell-wrap new file mode 100755 index 0000000000..9e94da114f --- /dev/null +++ b/scripts/qmp/qmp-shell-wrap @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +import os +import sys + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu.qmp import qmp_shell + + +if __name__ == '__main__': + qmp_shell.main_wrap() diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py index b33fb70d5e..8f731a5cfe 100755 --- a/scripts/render_block_graph.py +++ b/scripts/render_block_graph.py @@ -25,8 +25,8 @@ import json from graphviz import Digraph sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) -from qemu.aqmp import QMPError -from qemu.aqmp.legacy import QEMUMonitorProtocol +from qemu.qmp import QMPError +from qemu.qmp.legacy import QEMUMonitorProtocol def perm(arr): diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py index a403c35b08..56191db44b 100755 --- a/scripts/simplebench/bench_block_job.py +++ b/scripts/simplebench/bench_block_job.py @@ -27,8 +27,7 @@ import json sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu.machine import QEMUMachine -from qemu.qmp import QMPConnectError -from qemu.aqmp import ConnectError +from qemu.qmp import ConnectError def bench_block_job(cmd, cmd_args, qemu_args): @@ -50,7 +49,7 @@ def bench_block_job(cmd, cmd_args, qemu_args): vm.launch() except OSError as e: return {'error': 'popen failed: ' + str(e)} - except (QMPConnectError, ConnectError, socket.timeout): + except (ConnectError, socket.timeout): return {'error': 'qemu failed: ' + str(vm.get_log())} try: diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py index 6ce559f6cc..b99fe6896b 100644 --- a/scripts/tracetool/format/ust_events_h.py +++ b/scripts/tracetool/format/ust_events_h.py @@ -29,8 +29,8 @@ def generate(events, backend, group): '#undef TRACEPOINT_PROVIDER', '#define TRACEPOINT_PROVIDER qemu', '', - '#undef TRACEPOINT_INCLUDE_FILE', - '#define TRACEPOINT_INCLUDE_FILE ./%s' % include, + '#undef TRACEPOINT_INCLUDE', + '#define TRACEPOINT_INCLUDE "./%s"' % include, '', '#if !defined (TRACE_%s_GENERATED_UST_H) || \\' % group.upper(), ' defined(TRACEPOINT_HEADER_MULTI_READ)', diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index fe850763c5..839a5ec614 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -214,7 +214,8 @@ for i in "$tmpdir"/include/linux/*virtio*.h \ "$tmpdir/include/linux/const.h" \ "$tmpdir/include/linux/kernel.h" \ "$tmpdir/include/linux/vhost_types.h" \ - "$tmpdir/include/linux/sysinfo.h"; do + "$tmpdir/include/linux/sysinfo.h" \ + "$tmpdir/include/misc/pvpanic.h"; do cp_portable "$i" "$output/include/standard-headers/linux" done mkdir -p "$output/include/standard-headers/drm" diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index f281daeced..196b78c00d 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -36,7 +36,7 @@ #include #endif -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" @@ -77,8 +77,10 @@ static int gid = -1; static void compute_default_paths(void) { - socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); - pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); + g_autofree char *state = qemu_get_local_state_dir(); + + socket_path = g_build_filename(state, "run", "qemu-pr-helper.sock", NULL); + pidfile = g_build_filename(state, "run", "qemu-pr-helper.pid", NULL); } static void usage(const char *name) @@ -1001,7 +1003,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } trace_init_file(); - qemu_set_log(LOG_TRACE); + qemu_set_log(LOG_TRACE, &error_fatal); #ifdef CONFIG_MPATH dm_init(); diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 37963becae..7a51fd0737 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -44,6 +44,7 @@ #define COMMON_SEMI_HEAP_SIZE (128 * 1024 * 1024) #else #include "qemu/cutils.h" +#include "hw/loader.h" #ifdef TARGET_ARM #include "hw/arm/boot.h" #endif @@ -144,33 +145,69 @@ typedef struct GuestFD { static GArray *guestfd_array; #ifndef CONFIG_USER_ONLY -#include "exec/address-spaces.h" -/* - * Find the base of a RAM region containing the specified address + +/** + * common_semi_find_bases: find information about ram and heap base + * + * This function attempts to provide meaningful numbers for RAM and + * HEAP base addresses. The rambase is simply the lowest addressable + * RAM position. For the heapbase we ask the loader to scan the + * address space and the largest available gap by querying the "ROM" + * regions. + * + * Returns: a structure with the numbers we need. */ -static inline hwaddr -common_semi_find_region_base(hwaddr addr) + +typedef struct LayoutInfo { + target_ulong rambase; + size_t ramsize; + hwaddr heapbase; + hwaddr heaplimit; +} LayoutInfo; + +static bool find_ram_cb(Int128 start, Int128 len, const MemoryRegion *mr, + hwaddr offset_in_region, void *opaque) { - MemoryRegion *subregion; + LayoutInfo *info = (LayoutInfo *) opaque; + uint64_t size = int128_get64(len); + + if (!mr->ram || mr->readonly) { + return false; + } + + if (size > info->ramsize) { + info->rambase = int128_get64(start); + info->ramsize = size; + } + + /* search exhaustively for largest RAM */ + return false; +} + +static LayoutInfo common_semi_find_bases(CPUState *cs) +{ + FlatView *fv; + LayoutInfo info = { 0, 0, 0, 0 }; + + RCU_READ_LOCK_GUARD(); + + fv = address_space_to_flatview(cs->as); + flatview_for_each_range(fv, find_ram_cb, &info); /* - * Find the chunk of R/W memory containing the address. This is - * used for the SYS_HEAPINFO semihosting call, which should - * probably be using information from the loaded application. + * If we have found the RAM lets iterate through the ROM blobs to + * work out the best place for the remainder of RAM and split it + * equally between stack and heap. */ - QTAILQ_FOREACH(subregion, &get_system_memory()->subregions, - subregions_link) { - if (subregion->ram && !subregion->readonly) { - Int128 top128 = int128_add(int128_make64(subregion->addr), - subregion->size); - Int128 addr128 = int128_make64(addr); - if (subregion->addr <= addr && int128_lt(addr128, top128)) { - return subregion->addr; - } - } + if (info.rambase || info.ramsize > 0) { + RomGap gap = rom_find_largest_gap_between(info.rambase, info.ramsize); + info.heapbase = gap.base; + info.heaplimit = gap.base + gap.size; } - return 0; + + return info; } + #endif #ifdef TARGET_ARM @@ -204,28 +241,6 @@ common_semi_sys_exit_extended(CPUState *cs, int nr) return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); } -#ifndef CONFIG_USER_ONLY -#include "hw/arm/boot.h" -static inline target_ulong -common_semi_rambase(CPUState *cs) -{ - CPUArchState *env = cs->env_ptr; - const struct arm_boot_info *info = env->boot_info; - target_ulong sp; - - if (info) { - return info->loader_start; - } - - if (is_a64(env)) { - sp = env->xregs[31]; - } else { - sp = env->regs[13]; - } - return common_semi_find_region_base(sp); -} -#endif - #endif /* TARGET_ARM */ #ifdef TARGET_RISCV @@ -251,17 +266,6 @@ common_semi_sys_exit_extended(CPUState *cs, int nr) return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); } -#ifndef CONFIG_USER_ONLY - -static inline target_ulong -common_semi_rambase(CPUState *cs) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - return common_semi_find_region_base(env->gpr[xSP]); -} -#endif - #endif /* @@ -1165,12 +1169,12 @@ target_ulong do_common_semihosting(CPUState *cs) case TARGET_SYS_HEAPINFO: { target_ulong retvals[4]; - target_ulong limit; int i; #ifdef CONFIG_USER_ONLY TaskState *ts = cs->opaque; + target_ulong limit; #else - target_ulong rambase = common_semi_rambase(cs); + LayoutInfo info = common_semi_find_bases(cs); #endif GET_ARG(0); @@ -1201,12 +1205,10 @@ target_ulong do_common_semihosting(CPUState *cs) retvals[2] = ts->stack_base; retvals[3] = 0; /* Stack limit. */ #else - limit = current_machine->ram_size; - /* TODO: Make this use the limit of the loaded application. */ - retvals[0] = rambase + limit / 2; - retvals[1] = rambase + limit; - retvals[2] = rambase + limit; /* Stack base */ - retvals[3] = rambase; /* Stack limit. */ + retvals[0] = info.heapbase; /* Heap Base */ + retvals[1] = info.heaplimit; /* Heap Limit */ + retvals[2] = info.heaplimit; /* Stack base */ + retvals[3] = info.heapbase; /* Stack limit. */ #endif for (i = 0; i < ARRAY_SIZE(retvals); i++) { diff --git a/semihosting/config.c b/semihosting/config.c index 137171b717..50d82108e6 100644 --- a/semihosting/config.c +++ b/semihosting/config.c @@ -51,7 +51,7 @@ typedef struct SemihostingConfig { bool enabled; SemihostingTarget target; Chardev *chardev; - const char **argv; + char **argv; int argc; const char *cmdline; /* concatenated argv */ } SemihostingConfig; @@ -98,8 +98,8 @@ static int add_semihosting_arg(void *opaque, if (strcmp(name, "arg") == 0) { s->argc++; /* one extra element as g_strjoinv() expects NULL-terminated array */ - s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *)); - s->argv[s->argc - 1] = val; + s->argv = g_renew(char *, s->argv, s->argc + 1); + s->argv[s->argc - 1] = g_strdup(val); s->argv[s->argc] = NULL; } return 0; diff --git a/softmmu/arch_init.c b/softmmu/arch_init.c index 8919405c7b..79716f959b 100644 --- a/softmmu/arch_init.c +++ b/softmmu/arch_init.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/module.h" #include "sysemu/arch_init.h" #ifdef TARGET_SPARC @@ -39,3 +40,11 @@ int graphic_depth = 32; #endif const uint32_t arch_type = QEMU_ARCH; + +void qemu_init_arch_modules(void) +{ +#ifdef CONFIG_MODULES + module_init_info(qemu_modinfo); + module_allow_arch(TARGET_NAME); +#endif +} diff --git a/softmmu/bootdevice.c b/softmmu/bootdevice.c index add4e3d2d1..c0713bfa9f 100644 --- a/softmmu/bootdevice.c +++ b/softmmu/bootdevice.c @@ -166,7 +166,7 @@ void add_boot_device_path(int32_t bootindex, DeviceState *dev, del_boot_device_path(dev, suffix); - node = g_malloc0(sizeof(FWBootEntry)); + node = g_new0(FWBootEntry, 1); node->bootindex = bootindex; node->suffix = g_strdup(suffix); node->dev = dev; @@ -367,7 +367,7 @@ void add_boot_device_lchs(DeviceState *dev, const char *suffix, assert(dev != NULL || suffix != NULL); - node = g_malloc0(sizeof(FWLCHSEntry)); + node = g_new0(FWLCHSEntry, 1); node->suffix = g_strdup(suffix); node->dev = dev; node->lcyls = lcyls; diff --git a/softmmu/cpu-throttle.c b/softmmu/cpu-throttle.c index 8c2144ab95..d9bb30a223 100644 --- a/softmmu/cpu-throttle.c +++ b/softmmu/cpu-throttle.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/thread.h" #include "hw/core/cpu.h" #include "qemu/main-loop.h" diff --git a/softmmu/cpu-timers.c b/softmmu/cpu-timers.c index 34ddfa02f1..117408cb83 100644 --- a/softmmu/cpu-timers.c +++ b/softmmu/cpu-timers.c @@ -23,12 +23,10 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "exec/exec-all.h" #include "sysemu/cpus.h" #include "qemu/main-loop.h" #include "qemu/option.h" diff --git a/softmmu/cpus.c b/softmmu/cpus.c index 035395ae13..23b30484b2 100644 --- a/softmmu/cpus.c +++ b/softmmu/cpus.c @@ -23,8 +23,8 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "monitor/monitor.h" +#include "qemu/coroutine-tls.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" @@ -32,7 +32,7 @@ #include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" #include "sysemu/hw_accel.h" -#include "exec/exec-all.h" +#include "exec/cpu-common.h" #include "qemu/thread.h" #include "qemu/plugin.h" #include "sysemu/cpus.h" @@ -66,6 +66,11 @@ static QemuMutex qemu_global_mutex; +/* + * The chosen accelerator is supposed to register this. + */ +static const AccelOpsClass *cpus_accel; + bool cpu_is_stopped(CPUState *cpu) { return cpu->stopped || !runstate_is_running(); @@ -84,10 +89,12 @@ bool cpu_thread_is_idle(CPUState *cpu) if (cpu_is_stopped(cpu)) { return true; } - if (!cpu->halted || cpu_has_work(cpu) || - kvm_halt_in_kernel() || whpx_apic_in_platform()) { + if (!cpu->halted || cpu_has_work(cpu)) { return false; } + if (cpus_accel->cpu_thread_is_idle) { + return cpus_accel->cpu_thread_is_idle(cpu); + } return true; } @@ -121,11 +128,6 @@ void hw_error(const char *fmt, ...) abort(); } -/* - * The chosen accelerator is supposed to register this. - */ -static const AccelOpsClass *cpus_accel; - void cpu_synchronize_all_states(void) { CPUState *cpu; @@ -192,7 +194,10 @@ void cpu_synchronize_pre_loadvm(CPUState *cpu) bool cpus_are_resettable(void) { - return cpu_check_are_resettable(); + if (cpus_accel->cpus_are_resettable) { + return cpus_accel->cpus_are_resettable(); + } + return true; } int64_t cpus_get_virtual_clock(void) @@ -473,11 +478,16 @@ bool qemu_in_vcpu_thread(void) return current_cpu && qemu_cpu_is_self(current_cpu); } -static __thread bool iothread_locked = false; +QEMU_DEFINE_STATIC_CO_TLS(bool, iothread_locked) bool qemu_mutex_iothread_locked(void) { - return iothread_locked; + return get_iothread_locked(); +} + +bool qemu_in_main_thread(void) +{ + return qemu_mutex_iothread_locked(); } /* @@ -490,13 +500,13 @@ void qemu_mutex_lock_iothread_impl(const char *file, int line) g_assert(!qemu_mutex_iothread_locked()); bql_lock(&qemu_global_mutex, file, line); - iothread_locked = true; + set_iothread_locked(true); } void qemu_mutex_unlock_iothread(void) { g_assert(qemu_mutex_iothread_locked()); - iothread_locked = false; + set_iothread_locked(false); qemu_mutex_unlock(&qemu_global_mutex); } @@ -662,7 +672,7 @@ int vm_stop(RunState state) * Returns -1 if the vCPUs are not to be restarted (e.g. if they are already * running or in case of an error condition), 0 otherwise. */ -int vm_prepare_start(void) +int vm_prepare_start(bool step_pending) { RunState requested; @@ -682,6 +692,14 @@ int vm_prepare_start(void) return -1; } + /* + * WHPX accelerator needs to know whether we are going to step + * any CPUs, before starting the first one. + */ + if (cpus_accel->synchronize_pre_resume) { + cpus_accel->synchronize_pre_resume(step_pending); + } + /* We are sending this now, but the CPUs will be resumed shortly later */ qapi_event_send_resume(); @@ -693,7 +711,7 @@ int vm_prepare_start(void) void vm_start(void) { - if (!vm_prepare_start()) { + if (!vm_prepare_start(false)) { resume_all_vcpus(); } } @@ -717,14 +735,6 @@ int vm_stop_force_state(RunState state) } } -void list_cpus(const char *optarg) -{ - /* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) - cpu_list(); -#endif -} - void qmp_memsave(int64_t addr, int64_t size, const char *filename, bool has_cpu, int64_t cpu_index, Error **errp) { diff --git a/softmmu/datadir.c b/softmmu/datadir.c index 504c4665be..160cac999a 100644 --- a/softmmu/datadir.c +++ b/softmmu/datadir.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/cutils.h" #include "trace.h" diff --git a/softmmu/dma-helpers.c b/softmmu/dma-helpers.c index 160095e4ba..7820fec54c 100644 --- a/softmmu/dma-helpers.c +++ b/softmmu/dma-helpers.c @@ -29,7 +29,7 @@ MemTxResult dma_memory_set(AddressSpace *as, dma_addr_t addr, void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint, AddressSpace *as) { - qsg->sg = g_malloc(alloc_hint * sizeof(ScatterGatherEntry)); + qsg->sg = g_new(ScatterGatherEntry, alloc_hint); qsg->nsg = 0; qsg->nalloc = alloc_hint; qsg->size = 0; @@ -42,7 +42,7 @@ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len) { if (qsg->nsg == qsg->nalloc) { qsg->nalloc = 2 * qsg->nalloc + 1; - qsg->sg = g_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry)); + qsg->sg = g_renew(ScatterGatherEntry, qsg->sg, qsg->nalloc); } qsg->sg[qsg->nsg].base = base; qsg->sg[qsg->nsg].len = len; diff --git a/softmmu/globals.c b/softmmu/globals.c index 7d0fc81183..3ebd718e35 100644 --- a/softmmu/globals.c +++ b/softmmu/globals.c @@ -25,8 +25,6 @@ #include "qemu/osdep.h" #include "exec/cpu-common.h" #include "hw/display/vga.h" -#include "hw/i386/pc.h" -#include "hw/i386/x86.h" #include "hw/loader.h" #include "hw/xen/xen.h" #include "net/net.h" diff --git a/softmmu/icount.c b/softmmu/icount.c index 21341a4ce4..5ca271620d 100644 --- a/softmmu/icount.c +++ b/softmmu/icount.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "migration/vmstate.h" #include "qapi/error.h" diff --git a/softmmu/main.c b/softmmu/main.c index 639c67ff48..c00432ff09 100644 --- a/softmmu/main.c +++ b/softmmu/main.c @@ -23,28 +23,14 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu-main.h" #include "sysemu/sysemu.h" #ifdef CONFIG_SDL -#if defined(__APPLE__) || defined(main) #include -static int qemu_main(int argc, char **argv, char **envp); -int main(int argc, char **argv) -{ - return qemu_main(argc, argv, NULL); -} -#undef main -#define main qemu_main #endif -#endif /* CONFIG_SDL */ -#ifdef CONFIG_COCOA -#undef main -#define main qemu_main -#endif /* CONFIG_COCOA */ - -int main(int argc, char **argv, char **envp) +int qemu_main(int argc, char **argv, char **envp) { qemu_init(argc, argv, envp); qemu_main_loop(); @@ -52,3 +38,10 @@ int main(int argc, char **argv, char **envp) return 0; } + +#ifndef CONFIG_COCOA +int main(int argc, char **argv) +{ + return qemu_main(argc, argv, NULL); +} +#endif diff --git a/softmmu/memory.c b/softmmu/memory.c index 8060c6de78..7ba2048836 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -350,7 +350,7 @@ static void flatview_simplify(FlatView *view) static bool memory_region_big_endian(MemoryRegion *mr) { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN return mr->ops->endianness != DEVICE_LITTLE_ENDIAN; #else return mr->ops->endianness == DEVICE_BIG_ENDIAN; @@ -2626,8 +2626,7 @@ static void memory_region_readd_subregion(MemoryRegion *mr) memory_region_transaction_begin(); memory_region_ref(mr); memory_region_del_subregion(container, mr); - mr->container = container; - memory_region_update_container_subregions(mr); + memory_region_add_subregion_common(container, mr->addr, mr); memory_region_unref(mr); memory_region_transaction_commit(); } diff --git a/softmmu/memory_mapping.c b/softmmu/memory_mapping.c index a62eaa49cc..f6f0a829fd 100644 --- a/softmmu/memory_mapping.c +++ b/softmmu/memory_mapping.c @@ -17,6 +17,7 @@ #include "sysemu/memory_mapping.h" #include "exec/memory.h" #include "exec/address-spaces.h" +#include "hw/core/cpu.h" //#define DEBUG_GUEST_PHYS_REGION_ADD @@ -41,7 +42,7 @@ static void create_new_memory_mapping(MemoryMappingList *list, { MemoryMapping *memory_mapping; - memory_mapping = g_malloc(sizeof(MemoryMapping)); + memory_mapping = g_new(MemoryMapping, 1); memory_mapping->phys_addr = phys_addr; memory_mapping->virt_addr = virt_addr; memory_mapping->length = length; diff --git a/softmmu/meson.build b/softmmu/meson.build index 39f766ce7c..8138248661 100644 --- a/softmmu/meson.build +++ b/softmmu/meson.build @@ -1,20 +1,9 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( 'arch_init.c', - 'balloon.c', - 'cpus.c', - 'cpu-throttle.c', - 'datadir.c', - 'globals.c', - 'physmem.c', 'ioport.c', - 'rtc.c', - 'runstate.c', 'memory.c', - 'memory_mapping.c', + 'physmem.c', 'qtest.c', - 'vl.c', - 'cpu-timers.c', - 'runstate-action.c', )]) specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files( @@ -22,9 +11,20 @@ specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files( )]) softmmu_ss.add(files( + 'balloon.c', 'bootdevice.c', + 'cpus.c', + 'cpu-throttle.c', + 'cpu-timers.c', + 'datadir.c', 'dma-helpers.c', + 'globals.c', + 'memory_mapping.c', 'qdev-monitor.c', + 'rtc.c', + 'runstate-action.c', + 'runstate.c', + 'vl.c', ), sdl, libpmem, libdaxctl) if have_tpm diff --git a/softmmu/physmem.c b/softmmu/physmem.c index a13289a594..657841eed0 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "exec/page-vary.h" #include "qapi/error.h" #include "qemu/cutils.h" @@ -42,6 +42,8 @@ #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" +#include "qemu/log.h" +#include "qemu/memalign.h" #include "exec/memory.h" #include "exec/ioport.h" #include "sysemu/dma.h" @@ -61,7 +63,6 @@ #include "exec/memory-internal.h" #include "exec/ram_addr.h" -#include "exec/log.h" #include "qemu/pmem.h" @@ -1382,11 +1383,11 @@ long qemu_maxrampagesize(void) #else long qemu_minrampagesize(void) { - return qemu_real_host_page_size; + return qemu_real_host_page_size(); } long qemu_maxrampagesize(void) { - return qemu_real_host_page_size; + return qemu_real_host_page_size(); } #endif @@ -2162,7 +2163,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, new_block->max_length = max_size; assert(max_size >= size); new_block->fd = -1; - new_block->page_size = qemu_real_host_page_size; + new_block->page_size = qemu_real_host_page_size(); new_block->host = host; new_block->flags = ram_flags; ram_block_add(new_block, &local_err); @@ -2760,6 +2761,33 @@ static bool prepare_mmio_access(MemoryRegion *mr) return release_lock; } +/** + * flatview_access_allowed + * @mr: #MemoryRegion to be accessed + * @attrs: memory transaction attributes + * @addr: address within that memory region + * @len: the number of bytes to access + * + * Check if a memory transaction is allowed. + * + * Returns: true if transaction is allowed, false if denied. + */ +static bool flatview_access_allowed(MemoryRegion *mr, MemTxAttrs attrs, + hwaddr addr, hwaddr len) +{ + if (likely(!attrs.memory)) { + return true; + } + if (memory_region_is_ram(mr)) { + return true; + } + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to non-RAM device at " + "addr 0x%" HWADDR_PRIX ", size %" HWADDR_PRIu ", " + "region '%s'\n", addr, len, memory_region_name(mr)); + return false; +} + /* Called within RCU critical section. */ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, @@ -2774,7 +2802,10 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, const uint8_t *buf = ptr; for (;;) { - if (!memory_access_is_direct(mr, true)) { + if (!flatview_access_allowed(mr, attrs, addr1, l)) { + result |= MEMTX_ACCESS_ERROR; + /* Keep going. */ + } else if (!memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); l = memory_access_size(mr, l, addr1); /* XXX: could force current_cpu to NULL to avoid @@ -2816,14 +2847,14 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, hwaddr l; hwaddr addr1; MemoryRegion *mr; - MemTxResult result = MEMTX_OK; l = len; mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); - result = flatview_write_continue(fv, addr, attrs, buf, len, - addr1, l, mr); - - return result; + if (!flatview_access_allowed(mr, attrs, addr, len)) { + return MEMTX_ACCESS_ERROR; + } + return flatview_write_continue(fv, addr, attrs, buf, len, + addr1, l, mr); } /* Called within RCU critical section. */ @@ -2840,7 +2871,10 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, fuzz_dma_read_cb(addr, len, mr); for (;;) { - if (!memory_access_is_direct(mr, false)) { + if (!flatview_access_allowed(mr, attrs, addr1, l)) { + result |= MEMTX_ACCESS_ERROR; + /* Keep going. */ + } else if (!memory_access_is_direct(mr, false)) { /* I/O case */ release_lock |= prepare_mmio_access(mr); l = memory_access_size(mr, l, addr1); @@ -2883,6 +2917,9 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, l = len; mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); + if (!flatview_access_allowed(mr, attrs, addr, len)) { + return MEMTX_ACCESS_ERROR; + } return flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); } @@ -3139,12 +3176,10 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) { FlatView *fv; - bool result; RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); - result = flatview_access_valid(fv, addr, len, is_write, attrs); - return result; + return flatview_access_valid(fv, addr, len, is_write, attrs); } static hwaddr @@ -3436,11 +3471,11 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, #include "memory_ldst.c.inc" /* virtual memory access for debug (includes writing to ROM) */ -int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - void *ptr, target_ulong len, bool is_write) +int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, + void *ptr, size_t len, bool is_write) { hwaddr phys_addr; - target_ulong l, page; + vaddr l, page; uint8_t *buf = ptr; cpu_synchronize_state(cpu); diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 01f3834db5..12fe60c467 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -83,6 +83,8 @@ static const QDevAlias qdev_alias_table[] = { { "virtio-gpu-device", "virtio-gpu", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_VIRTIO_CCW }, { "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_VIRTIO_PCI }, + { "virtio-gpu-gl-device", "virtio-gpu-gl", QEMU_ARCH_VIRTIO_MMIO }, + { "virtio-gpu-gl-pci", "virtio-gpu-gl", QEMU_ARCH_VIRTIO_PCI }, { "virtio-input-host-device", "virtio-input-host", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_VIRTIO_CCW }, { "virtio-input-host-pci", "virtio-input-host", QEMU_ARCH_VIRTIO_PCI }, @@ -971,6 +973,8 @@ BlockBackend *blk_by_qdev_id(const char *id, Error **errp) DeviceState *dev; BlockBackend *blk; + GLOBAL_STATE_CODE(); + dev = find_device_state(id, errp); if (dev == NULL) { return NULL; @@ -1034,6 +1038,13 @@ int qemu_global_option(const char *str) if (!opts) { return -1; } + if (!qemu_opt_get(opts, "driver") + || !qemu_opt_get(opts, "property") + || !qemu_opt_get(opts, "value")) { + error_report("options 'driver', 'property', and 'value'" + " are required"); + return -1; + } return 0; } diff --git a/softmmu/qtest.c b/softmmu/qtest.c index 8b7cb6aa8e..f8acef2628 100644 --- a/softmmu/qtest.c +++ b/softmmu/qtest.c @@ -58,12 +58,12 @@ static FILE *qtest_log_fp; static QTest *qtest; static GString *inbuf; static int irq_levels[MAX_IRQ]; -static qemu_timeval start_time; +static GTimer *timer; static bool qtest_opened; static void (*qtest_server_send)(void*, const char*); static void *qtest_server_send_opaque; -#define FMT_timeval "%ld.%06ld" +#define FMT_timeval "%.06f" /** * DOC: QTest Protocol @@ -264,31 +264,16 @@ static int hex2nib(char ch) } } -static void qtest_get_time(qemu_timeval *tv) -{ - qemu_gettimeofday(tv); - tv->tv_sec -= start_time.tv_sec; - tv->tv_usec -= start_time.tv_usec; - if (tv->tv_usec < 0) { - tv->tv_usec += 1000000; - tv->tv_sec -= 1; - } -} - static void qtest_send_prefix(CharBackend *chr) { - qemu_timeval tv; - if (!qtest_log_fp || !qtest_opened) { return; } - qtest_get_time(&tv); - fprintf(qtest_log_fp, "[S +" FMT_timeval "] ", - (long) tv.tv_sec, (long) tv.tv_usec); + fprintf(qtest_log_fp, "[S +" FMT_timeval "] ", g_timer_elapsed(timer, NULL)); } -static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...) +static void G_GNUC_PRINTF(1, 2) qtest_log_send(const char *fmt, ...) { va_list ap; @@ -318,7 +303,7 @@ static void qtest_send(CharBackend *chr, const char *str) qtest_server_send(qtest_server_send_opaque, str); } -static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr, +static void G_GNUC_PRINTF(2, 3) qtest_sendf(CharBackend *chr, const char *fmt, ...) { va_list ap; @@ -386,12 +371,9 @@ static void qtest_process_command(CharBackend *chr, gchar **words) command = words[0]; if (qtest_log_fp) { - qemu_timeval tv; int i; - qtest_get_time(&tv); - fprintf(qtest_log_fp, "[R +" FMT_timeval "]", - (long) tv.tv_sec, (long) tv.tv_usec); + fprintf(qtest_log_fp, "[R +" FMT_timeval "]", g_timer_elapsed(timer, NULL)); for (i = 0; words[i]; i++) { fprintf(qtest_log_fp, " %s", words[i]); } @@ -732,7 +714,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "endianness") == 0) { qtest_send_prefix(chr); -#if defined(TARGET_WORDS_BIGENDIAN) +#if TARGET_BIG_ENDIAN qtest_sendf(chr, "OK big\n"); #else qtest_sendf(chr, "OK little\n"); @@ -846,21 +828,20 @@ static void qtest_event(void *opaque, QEMUChrEvent event) for (i = 0; i < ARRAY_SIZE(irq_levels); i++) { irq_levels[i] = 0; } - qemu_gettimeofday(&start_time); + + g_clear_pointer(&timer, g_timer_destroy); + timer = g_timer_new(); qtest_opened = true; if (qtest_log_fp) { - fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n", - (long) start_time.tv_sec, (long) start_time.tv_usec); + fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n", g_timer_elapsed(timer, NULL)); } break; case CHR_EVENT_CLOSED: qtest_opened = false; if (qtest_log_fp) { - qemu_timeval tv; - qtest_get_time(&tv); - fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n", - (long) tv.tv_sec, (long) tv.tv_usec); + fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n", g_timer_elapsed(timer, NULL)); } + g_clear_pointer(&timer, g_timer_destroy); break; default: break; diff --git a/softmmu/runstate.c b/softmmu/runstate.c index e0d869b21a..fac7b63259 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -40,7 +40,6 @@ #include "qapi/error.h" #include "qapi/qapi-commands-run-state.h" #include "qapi/qapi-events-run-state.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/job.h" diff --git a/softmmu/vl.c b/softmmu/vl.c index 1fe028800f..c2919579fd 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -23,10 +23,11 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "exec/cpu-common.h" +#include "exec/page-vary.h" #include "hw/qdev-properties.h" #include "qapi/compat-policy.h" #include "qapi/error.h" @@ -124,9 +125,11 @@ #include "qapi/qapi-visit-qom.h" #include "qapi/qapi-commands-ui.h" #include "qapi/qmp/qdict.h" +#include "block/qdict.h" #include "qapi/qmp/qerror.h" #include "sysemu/iothread.h" #include "qemu/guest-random.h" +#include "qemu/keyval.h" #include "config-host.h" @@ -836,7 +839,7 @@ static void help(int exitcode) version(); printf("usage: %s [options] [disk_image]\n\n" "'disk_image' is a raw hard disk image for IDE hard disk 0\n\n", - error_get_progname()); + g_get_prgname()); #define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ if ((arch_mask) & arch_type) \ @@ -1961,7 +1964,7 @@ static void qemu_create_early_backends(void) object_option_foreach_add(object_create_early); /* spice needs the timers to be initialized by this point */ - /* spice must initialize before audio as it changes the default auiodev */ + /* spice must initialize before audio as it changes the default audiodev */ /* spice must initialize before chardevs (for spicevmc and spiceport) */ qemu_spice.init(); @@ -2559,19 +2562,16 @@ static void qemu_process_early_options(void) #endif /* Open the logfile at this point and set the log mask if necessary. */ - if (log_file) { - qemu_set_log_filename(log_file, &error_fatal); - } - if (log_mask) { - int mask; - mask = qemu_str_to_log_mask(log_mask); - if (!mask) { - qemu_print_log_usage(stdout); - exit(1); + { + int mask = 0; + if (log_mask) { + mask = qemu_str_to_log_mask(log_mask); + if (!mask) { + qemu_print_log_usage(stdout); + exit(1); + } } - qemu_set_log(mask); - } else { - qemu_set_log(0); + qemu_set_log_filename_flags(log_file, mask, &error_fatal); } qemu_add_default_firmwarepath(); @@ -2815,10 +2815,7 @@ void qemu_init(int argc, char **argv, char **envp) error_init(argv[0]); qemu_init_exec_dir(argv[0]); -#ifdef CONFIG_MODULES - module_init_info(qemu_modinfo); - module_allow_arch(TARGET_NAME); -#endif + qemu_init_arch_modules(); qemu_init_subsystems(); @@ -3540,6 +3537,7 @@ void qemu_init(int argc, char **argv, char **envp) case QEMU_OPTION_readconfig: qemu_read_config_file(optarg, qemu_parse_config_group, &error_fatal); break; +#ifdef CONFIG_SPICE case QEMU_OPTION_spice: olist = qemu_find_opts_err("spice", NULL); if (!olist) { @@ -3552,26 +3550,7 @@ void qemu_init(int argc, char **argv, char **envp) } display_remote++; break; - case QEMU_OPTION_writeconfig: - { - FILE *fp; - warn_report("-writeconfig is deprecated and will go away without a replacement"); - if (strcmp(optarg, "-") == 0) { - fp = stdout; - } else { - fp = fopen(optarg, "w"); - if (fp == NULL) { - error_report("open %s: %s", optarg, - strerror(errno)); - exit(1); - } - } - qemu_config_write(fp); - if (fp != stdout) { - fclose(fp); - } - break; - } +#endif case QEMU_OPTION_qtest: qtest_chrdev = optarg; break; diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index 504d33aa91..9b8b17f52e 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -42,7 +42,7 @@ #include "qapi/qmp/qstring.h" #include "qapi/qobject-input-visitor.h" -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qemu-version.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -93,6 +93,9 @@ static void help(void) " --chardev configure a character device backend\n" " (see the qemu(1) man page for possible options)\n" "\n" +" --daemonize daemonize the process, and have the parent exit\n" +" once startup is complete\n" +"\n" " --export [type=]nbd,id=,node-name=[,name=]\n" " [,writable=on|off][,bitmap=]\n" " export the specified block node over NBD\n" @@ -138,12 +141,13 @@ static void help(void) " --pidfile write process ID to a file after startup\n" "\n" QEMU_HELP_BOTTOM "\n", - error_get_progname()); + g_get_prgname()); } enum { OPTION_BLOCKDEV = 256, OPTION_CHARDEV, + OPTION_DAEMONIZE, OPTION_EXPORT, OPTION_MONITOR, OPTION_NBD_SERVER, @@ -177,13 +181,30 @@ static int getopt_set_loc(int argc, char **argv, const char *optstring, return c; } -static void process_options(int argc, char *argv[]) +/** + * Process QSD command-line arguments. + * + * This is done in two passes: + * + * First (@pre_init_pass is true), we do a pass where all global + * arguments pertaining to the QSD process (like --help or --daemonize) + * are processed. This pass is done before most of the QEMU-specific + * initialization steps (e.g. initializing the block layer or QMP), and + * so must only process arguments that are not really QEMU-specific. + * + * Second (@pre_init_pass is false), we (sequentially) process all + * QEMU/QSD-specific arguments. Many of these arguments are effectively + * translated to QMP commands (like --blockdev for blockdev-add, or + * --export for block-export-add). + */ +static void process_options(int argc, char *argv[], bool pre_init_pass) { int c; static const struct option long_options[] = { {"blockdev", required_argument, NULL, OPTION_BLOCKDEV}, {"chardev", required_argument, NULL, OPTION_CHARDEV}, + {"daemonize", no_argument, NULL, OPTION_DAEMONIZE}, {"export", required_argument, NULL, OPTION_EXPORT}, {"help", no_argument, NULL, 'h'}, {"monitor", required_argument, NULL, OPTION_MONITOR}, @@ -196,11 +217,27 @@ static void process_options(int argc, char *argv[]) }; /* - * In contrast to the system emulator, options are processed in the order - * they are given on the command lines. This means that things must be - * defined first before they can be referenced in another option. + * In contrast to the system emulator, QEMU-specific options are processed + * in the order they are given on the command lines. This means that things + * must be defined first before they can be referenced in another option. */ + optind = 1; while ((c = getopt_set_loc(argc, argv, "-hT:V", long_options)) != -1) { + bool handle_option_pre_init; + + /* Should this argument be processed in the pre-init pass? */ + handle_option_pre_init = + c == '?' || + c == 'h' || + c == 'V' || + c == OPTION_DAEMONIZE || + c == OPTION_PIDFILE; + + /* Process every option only in its respective pass */ + if (pre_init_pass != handle_option_pre_init) { + continue; + } + switch (c) { case '?': exit(EXIT_FAILURE); @@ -246,6 +283,12 @@ static void process_options(int argc, char *argv[]) qemu_opts_del(opts); break; } + case OPTION_DAEMONIZE: + if (os_set_daemonize(true) < 0) { + error_report("--daemonize not supported in this build"); + exit(EXIT_FAILURE); + } + break; case OPTION_EXPORT: { Visitor *v; @@ -334,6 +377,10 @@ int main(int argc, char *argv[]) qemu_init_exec_dir(argv[0]); os_setup_signal_handling(); + process_options(argc, argv, true); + + os_daemonize(); + module_call_init(MODULE_INIT_QOM); module_call_init(MODULE_INIT_TRACE); qemu_add_opts(&qemu_trace_opts); @@ -345,10 +392,10 @@ int main(int argc, char *argv[]) if (!trace_init_backends()) { return EXIT_FAILURE; } - qemu_set_log(LOG_TRACE); + qemu_set_log(LOG_TRACE, &error_fatal); qemu_init_main_loop(&error_fatal); - process_options(argc, argv); + process_options(argc, argv, false); /* * Write the pid file after creating chardevs, exports, and NBD servers but @@ -356,6 +403,7 @@ int main(int argc, char *argv[]) * it. */ pid_file_init(); + os_setup_post(); while (!exit_requested) { main_loop_wait(false); diff --git a/stubs/error-printf.c b/stubs/error-printf.c index a2f61521a1..0e326d8010 100644 --- a/stubs/error-printf.c +++ b/stubs/error-printf.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "monitor/monitor.h" int error_vprintf(const char *fmt, va_list ap) { diff --git a/stubs/get-vm-name.c b/stubs/get-vm-name.c index fa990136b0..0906303f73 100644 --- a/stubs/get-vm-name.c +++ b/stubs/get-vm-name.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "sysemu/sysemu.h" const char *qemu_get_vm_name(void) { diff --git a/stubs/iothread-lock-block.c b/stubs/iothread-lock-block.c new file mode 100644 index 0000000000..c88ed70462 --- /dev/null +++ b/stubs/iothread-lock-block.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" +#include "qemu/main-loop.h" + +bool qemu_in_main_thread(void) +{ + return qemu_get_current_aio_context() == qemu_get_aio_context(); +} + diff --git a/stubs/meson.build b/stubs/meson.build index d359cbe1ad..6f80fec761 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -17,6 +17,9 @@ if linux_io_uring.found() stub_ss.add(files('io_uring.c')) endif stub_ss.add(files('iothread-lock.c')) +if have_block + stub_ss.add(files('iothread-lock-block.c')) +endif stub_ss.add(files('isa-bus.c')) stub_ss.add(files('is-daemonized.c')) if libaio.found() diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c index d058a2a00d..afa477aae6 100644 --- a/stubs/monitor-core.c +++ b/stubs/monitor-core.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" #include "monitor/monitor.h" -#include "qemu-common.h" #include "qapi/qapi-emit-events.h" Monitor *monitor_cur(void) diff --git a/subprojects/libvhost-user/include/compiler.h b/subprojects/libvhost-user/include/compiler.h new file mode 120000 index 0000000000..de7b70697c --- /dev/null +++ b/subprojects/libvhost-user/include/compiler.h @@ -0,0 +1 @@ +../../../include/qemu/compiler.h \ No newline at end of file diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index 7bb9173c57..1f200724b6 100644 --- a/target/alpha/cpu-qom.h +++ b/target/alpha/cpu-qom.h @@ -25,8 +25,7 @@ #define TYPE_ALPHA_CPU "alpha-cpu" -OBJECT_DECLARE_TYPE(AlphaCPU, AlphaCPUClass, - ALPHA_CPU) +OBJECT_DECLARE_CPU_TYPE(AlphaCPU, AlphaCPUClass, ALPHA_CPU) /** * AlphaCPUClass: diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index e819211503..d0abc949a8 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" /* Alpha processors have a weak memory model */ #define TCG_GUEST_DEFAULT_MO (0) @@ -197,9 +198,7 @@ enum { #define MMU_USER_IDX 1 #define MMU_PHYS_IDX 2 -typedef struct CPUAlphaState CPUAlphaState; - -struct CPUAlphaState { +typedef struct CPUArchState { uint64_t ir[31]; float64 fir[31]; uint64_t pc; @@ -251,7 +250,7 @@ struct CPUAlphaState { uint32_t features; uint32_t amask; int implver; -}; +} CPUAlphaState; /** * AlphaCPU: @@ -259,7 +258,7 @@ struct CPUAlphaState { * * An Alpha CPU. */ -struct AlphaCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -285,9 +284,6 @@ int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #define cpu_list alpha_cpu_list -typedef CPUAlphaState CPUArchState; -typedef AlphaCPU ArchCPU; - #include "exec/cpu-all.h" enum { @@ -438,8 +434,8 @@ void alpha_translate_init(void); #define CPU_RESOLVING_TYPE TYPE_ALPHA_CPU void alpha_cpu_list(void); -void QEMU_NORETURN dynamic_excp(CPUAlphaState *, uintptr_t, int, int); -void QEMU_NORETURN arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t); +G_NORETURN void dynamic_excp(CPUAlphaState *, uintptr_t, int, int); +G_NORETURN void arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t); uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env); void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val); @@ -456,9 +452,9 @@ void alpha_cpu_record_sigbus(CPUState *cs, vaddr address, bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); void alpha_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, diff --git a/target/alpha/helper.c b/target/alpha/helper.c index dcaa2d03ad..a5a389b5a3 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -514,7 +514,7 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags) /* This should only be called from translate, via gen_excp. We expect that ENV->PC has already been updated. */ -void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error) +G_NORETURN void helper_excp(CPUAlphaState *env, int excp, int error) { CPUState *cs = env_cpu(env); @@ -524,8 +524,8 @@ void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error) } /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */ -void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr, - int excp, int error) +G_NORETURN void dynamic_excp(CPUAlphaState *env, uintptr_t retaddr, + int excp, int error) { CPUState *cs = env_cpu(env); @@ -539,8 +539,8 @@ void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr, cpu_loop_exit(cs); } -void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr, - int exc, uint64_t mask) +G_NORETURN void arith_excp(CPUAlphaState *env, uintptr_t retaddr, + int exc, uint64_t mask) { env->trap_arg0 = exc; env->trap_arg1 = mask; diff --git a/target/alpha/translate.c b/target/alpha/translate.c index ca78a0faed..9af1627079 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" #include "disas/disas.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" @@ -236,7 +235,7 @@ static TCGv dest_fpr(DisasContext *ctx, unsigned reg) static int get_flag_ofs(unsigned shift) { int ofs = offsetof(CPUAlphaState, flags); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN ofs += 3 - (shift / 8); #else ofs += shift / 8; @@ -3028,10 +3027,11 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void alpha_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void alpha_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps alpha_tr_ops = { diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 7f38d33b8e..b59d505761 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -10,8 +10,8 @@ #ifdef TARGET_AARCH64 # define TARGET_LONG_BITS 64 -# define TARGET_PHYS_ADDR_SPACE_BITS 48 -# define TARGET_VIRT_ADDR_SPACE_BITS 48 +# define TARGET_PHYS_ADDR_SPACE_BITS 52 +# define TARGET_VIRT_ADDR_SPACE_BITS 52 #else # define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 40 diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index a22bd506d0..64c44cef2d 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -27,8 +27,7 @@ struct arm_boot_info; #define TYPE_ARM_CPU "arm-cpu" -OBJECT_DECLARE_TYPE(ARMCPU, ARMCPUClass, - ARM_CPU) +OBJECT_DECLARE_CPU_TYPE(ARMCPU, ARMCPUClass, ARM_CPU) #define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c085dc10ee..e46a766d77 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -21,7 +21,8 @@ #include "qemu/osdep.h" #include "qemu/qemu-print.h" #include "qemu/timer.h" -#include "qemu-common.h" +#include "qemu/log.h" +#include "exec/page-vary.h" #include "target/arm/idau.h" #include "qemu/module.h" #include "qapi/error.h" @@ -50,7 +51,7 @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value) if (is_a64(env)) { env->pc = value; - env->thumb = 0; + env->thumb = false; } else { env->regs[15] = value & ~1; env->thumb = value & 1; @@ -188,7 +189,7 @@ static void arm_cpu_reset(DeviceState *dev) if (arm_feature(env, ARM_FEATURE_AARCH64)) { /* 64 bit CPUs always start in 64 bit mode */ - env->aarch64 = 1; + env->aarch64 = true; #if defined(CONFIG_USER_ONLY) env->pstate = PSTATE_MODE_EL0t; /* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */ @@ -206,10 +207,11 @@ static void arm_cpu_reset(DeviceState *dev) aarch64_sve_zcr_get_valid_len(cpu, cpu->sve_default_vq - 1); } /* + * Enable 48-bit address space (TODO: take reserved_va into account). * Enable TBI0 but not TBI1. * Note that this must match useronly_clean_ptr. */ - env->cp15.tcr_el[1].raw_tcr = (1ULL << 37); + env->cp15.tcr_el[1].raw_tcr = 5 | (1ULL << 37); /* Enable MTE */ if (cpu_isar_feature(aa64_mte, cpu)) { @@ -234,7 +236,10 @@ static void arm_cpu_reset(DeviceState *dev) } else { env->pstate = PSTATE_MODE_EL1h; } - env->pc = cpu->rvbar; + + /* Sample rvbar at reset. */ + env->cp15.rvbar = cpu->rvbar_prop; + env->pc = env->cp15.rvbar; #endif } else { #if defined(CONFIG_USER_ONLY) @@ -365,6 +370,10 @@ static void arm_cpu_reset(DeviceState *dev) initial_pc = ldl_phys(s->as, vecbase + 4); } + qemu_log_mask(CPU_LOG_INT, + "Loaded reset SP 0x%x PC 0x%x from vector table\n", + initial_msp, initial_pc); + env->regs[13] = initial_msp & 0xFFFFFFFC; env->regs[15] = initial_pc & ~1; env->thumb = initial_pc & 1; @@ -685,6 +694,16 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ }; + if (!arm_feature(env, ARM_FEATURE_EL2) && + (irq == ARM_CPU_VIRQ || irq == ARM_CPU_VFIQ)) { + /* + * The GIC might tell us about VIRQ and VFIQ state, but if we don't + * have EL2 support we don't care. (Unless the guest is doing something + * silly this will only be calls saying "level is still 0".) + */ + return; + } + if (level) { env->irq_line_state |= mask[irq]; } else { @@ -693,11 +712,9 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) switch (irq) { case ARM_CPU_VIRQ: - assert(arm_feature(env, ARM_FEATURE_EL2)); arm_cpu_update_virq(cpu); break; case ARM_CPU_VFIQ: - assert(arm_feature(env, ARM_FEATURE_EL2)); arm_cpu_update_vfiq(cpu); break; case ARM_CPU_IRQ: @@ -803,7 +820,7 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) sctlr_b = arm_sctlr_b(env); if (bswap_code(sctlr_b)) { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN info->endian = BFD_ENDIAN_LITTLE; #else info->endian = BFD_ENDIAN_BIG; @@ -1110,11 +1127,12 @@ static void arm_cpu_initfn(Object *obj) * picky DTB consumer will also provide a helpful error message. */ cpu->dtb_compatible = "qemu,unknown"; - cpu->psci_version = 1; /* By default assume PSCI v0.1 */ + cpu->psci_version = QEMU_PSCI_VERSION_0_1; /* By default assume PSCI v0.1 */ cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; if (tcg_enabled() || hvf_enabled()) { - cpu->psci_version = 2; /* TCG and HVF implement PSCI 0.2 */ + /* TCG and HVF implement PSCI 1.1 */ + cpu->psci_version = QEMU_PSCI_VERSION_1_1; } } @@ -1128,9 +1146,6 @@ static Property arm_cpu_reset_cbar_property = static Property arm_cpu_reset_hivecs_property = DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); -static Property arm_cpu_rvbar_property = - DEFINE_PROP_UINT64("rvbar", ARMCPU, rvbar, 0); - #ifndef CONFIG_USER_ONLY static Property arm_cpu_has_el2_property = DEFINE_PROP_BOOL("has_el2", ARMCPU, has_el2, true); @@ -1233,7 +1248,9 @@ void arm_cpu_post_init(Object *obj) } if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_rvbar_property); + object_property_add_uint64_ptr(obj, "rvbar", + &cpu->rvbar_prop, + OBJ_PROP_FLAG_READWRITE); } #ifndef CONFIG_USER_ONLY @@ -1390,6 +1407,12 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) error_propagate(errp, local_err); return; } + + arm_cpu_lpa2_finalize(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } } if (kvm_enabled()) { @@ -1564,6 +1587,12 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) unset_feature(env, ARM_FEATURE_NEON); t = cpu->isar.id_aa64isar0; + t = FIELD_DP64(t, ID_AA64ISAR0, AES, 0); + t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 0); + t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 0); + t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 0); + t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 0); + t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 0); t = FIELD_DP64(t, ID_AA64ISAR0, DP, 0); cpu->isar.id_aa64isar0 = t; @@ -1578,6 +1607,9 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->isar.id_aa64pfr0 = t; u = cpu->isar.id_isar5; + u = FIELD_DP32(u, ID_ISAR5, AES, 0); + u = FIELD_DP32(u, ID_ISAR5, SHA1, 0); + u = FIELD_DP32(u, ID_ISAR5, SHA2, 0); u = FIELD_DP32(u, ID_ISAR5, RDM, 0); u = FIELD_DP32(u, ID_ISAR5, VCMA, 0); cpu->isar.id_isar5 = u; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c6a4d50e82..db8ff04449 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -21,6 +21,7 @@ #define ARM_CPU_H #include "kvm-consts.h" +#include "qemu/cpu-float.h" #include "hw/registerfields.h" #include "cpu-qom.h" #include "exec/cpu-defs.h" @@ -95,7 +96,7 @@ enum { * therefore useful to be able to pass TCG the offset of the least * significant half of a uint64_t struct member. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define offsetoflow32(S, M) (offsetof(S, M) + sizeof(uint32_t)) #define offsetofhigh32(S, M) offsetof(S, M) #else @@ -204,10 +205,12 @@ typedef struct { # define ARM_MAX_VQ 16 void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp); void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp); +void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp); #else # define ARM_MAX_VQ 1 static inline void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { } static inline void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) { } +static inline void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) { } #endif typedef struct ARMVectorReg { @@ -232,7 +235,7 @@ typedef struct CPUARMTBFlags { target_ulong flags2; } CPUARMTBFlags; -typedef struct CPUARMState { +typedef struct CPUArchState { /* Regs for current mode. */ uint32_t regs[16]; @@ -256,7 +259,8 @@ typedef struct CPUARMState { * all other bits are stored in their correct places in env->pstate */ uint32_t pstate; - uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */ + bool aarch64; /* True if CPU is in aarch64 state; inverse of PSTATE.nRW */ + bool thumb; /* True if CPU is in thumb mode; cpsr[5] */ /* Cached TBFLAGS state. See below for which bits are included. */ CPUARMTBFlags hflags; @@ -283,7 +287,6 @@ typedef struct CPUARMState { uint32_t ZF; /* Z set if zero. */ uint32_t QF; /* 0 or 1 */ uint32_t GE; /* cpsr[19:16] */ - uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */ uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ uint32_t btype; /* BTI branch type. spsr[11:10]. */ uint64_t daif; /* exception masks, in the bits they are in PSTATE */ @@ -380,7 +383,7 @@ typedef struct CPUARMState { union { /* Fault address registers. */ struct { uint64_t _unused_far0; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint32_t ifar_ns; uint32_t dfar_ns; uint32_t ifar_s; @@ -417,7 +420,7 @@ typedef struct CPUARMState { uint64_t c9_pminten; /* perf monitor interrupt enables */ union { /* Memory attribute redirection */ struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint64_t _unused_mair_0; uint32_t mair1_ns; uint32_t mair0_ns; @@ -445,6 +448,7 @@ typedef struct CPUARMState { uint64_t vbar_el[4]; }; uint32_t mvbar; /* (monitor) vector base address register */ + uint64_t rvbar; /* rvbar sampled from rvbar property at reset */ struct { /* FCSE PID. */ uint32_t fcseidr_ns; uint32_t fcseidr_s; @@ -774,7 +778,7 @@ typedef struct ARMISARegisters ARMISARegisters; * * An ARM CPU core. */ -struct ARMCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -975,14 +979,15 @@ struct ARMCPU { /* * Intermediate values used during property parsing. - * Once finalized, the values should be read from ID_AA64ISAR1. + * Once finalized, the values should be read from ID_AA64*. */ bool prop_pauth; bool prop_pauth_impdef; + bool prop_lpa2; /* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */ uint32_t dcz_blocksize; - uint64_t rvbar; + uint64_t rvbar_prop; /* Property/input signals. */ /* Configurable aspects of GIC cpu interface (which is part of the CPU) */ int gic_num_lrs; /* number of list registers */ @@ -1089,7 +1094,7 @@ void aarch64_add_pauth_properties(Object *obj); */ static inline uint64_t *sve_bswap64(uint64_t *dst, uint64_t *src, int nr) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN int i; for (i = 0; i < nr; ++i) { @@ -1228,6 +1233,20 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_ATA0 (1ULL << 42) /* v8.5-MemTag */ #define SCTLR_ATA (1ULL << 43) /* v8.5-MemTag */ #define SCTLR_DSSBS_64 (1ULL << 44) /* v8.5, AArch64 only */ +#define SCTLR_TWEDEn (1ULL << 45) /* FEAT_TWED */ +#define SCTLR_TWEDEL MAKE_64_MASK(46, 4) /* FEAT_TWED */ +#define SCTLR_TMT0 (1ULL << 50) /* FEAT_TME */ +#define SCTLR_TMT (1ULL << 51) /* FEAT_TME */ +#define SCTLR_TME0 (1ULL << 52) /* FEAT_TME */ +#define SCTLR_TME (1ULL << 53) /* FEAT_TME */ +#define SCTLR_EnASR (1ULL << 54) /* FEAT_LS64_V */ +#define SCTLR_EnAS0 (1ULL << 55) /* FEAT_LS64_ACCDATA */ +#define SCTLR_EnALS (1ULL << 56) /* FEAT_LS64 */ +#define SCTLR_EPAN (1ULL << 57) /* FEAT_PAN3 */ +#define SCTLR_EnTP2 (1ULL << 60) /* FEAT_SME */ +#define SCTLR_NMI (1ULL << 61) /* FEAT_NMI */ +#define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */ +#define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */ #define CPTR_TCPAC (1U << 31) #define CPTR_TTA (1U << 20) @@ -1540,6 +1559,18 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_FIEN (1U << 21) #define SCR_ENSCXT (1U << 25) #define SCR_ATA (1U << 26) +#define SCR_FGTEN (1U << 27) +#define SCR_ECVEN (1U << 28) +#define SCR_TWEDEN (1U << 29) +#define SCR_TWEDEL MAKE_64BIT_MASK(30, 4) +#define SCR_TME (1ULL << 34) +#define SCR_AMVOFFEN (1ULL << 35) +#define SCR_ENAS0 (1ULL << 36) +#define SCR_ADEN (1ULL << 37) +#define SCR_HXEN (1ULL << 38) +#define SCR_TRNDR (1ULL << 40) +#define SCR_ENTP2 (1ULL << 41) +#define SCR_GPF (1ULL << 48) #define HSTR_TTEE (1 << 16) #define HSTR_TJDBX (1 << 17) @@ -1929,6 +1960,7 @@ FIELD(ID_MMFR4, CCIDX, 24, 4) FIELD(ID_MMFR4, EVT, 28, 4) FIELD(ID_MMFR5, ETS, 0, 4) +FIELD(ID_MMFR5, NTLBPA, 4, 4) FIELD(ID_PFR0, STATE0, 0, 4) FIELD(ID_PFR0, STATE1, 4, 4) @@ -1981,6 +2013,16 @@ FIELD(ID_AA64ISAR1, SPECRES, 40, 4) FIELD(ID_AA64ISAR1, BF16, 44, 4) FIELD(ID_AA64ISAR1, DGH, 48, 4) FIELD(ID_AA64ISAR1, I8MM, 52, 4) +FIELD(ID_AA64ISAR1, XS, 56, 4) +FIELD(ID_AA64ISAR1, LS64, 60, 4) + +FIELD(ID_AA64ISAR2, WFXT, 0, 4) +FIELD(ID_AA64ISAR2, RPRES, 4, 4) +FIELD(ID_AA64ISAR2, GPA3, 8, 4) +FIELD(ID_AA64ISAR2, APA3, 12, 4) +FIELD(ID_AA64ISAR2, MOPS, 16, 4) +FIELD(ID_AA64ISAR2, BC, 20, 4) +FIELD(ID_AA64ISAR2, PAC_FRAC, 24, 4) FIELD(ID_AA64PFR0, EL0, 0, 4) FIELD(ID_AA64PFR0, EL1, 4, 4) @@ -2003,6 +2045,10 @@ FIELD(ID_AA64PFR1, SSBS, 4, 4) FIELD(ID_AA64PFR1, MTE, 8, 4) FIELD(ID_AA64PFR1, RAS_FRAC, 12, 4) FIELD(ID_AA64PFR1, MPAM_FRAC, 16, 4) +FIELD(ID_AA64PFR1, SME, 24, 4) +FIELD(ID_AA64PFR1, RNDR_TRAP, 28, 4) +FIELD(ID_AA64PFR1, CSV2_FRAC, 32, 4) +FIELD(ID_AA64PFR1, NMI, 36, 4) FIELD(ID_AA64MMFR0, PARANGE, 0, 4) FIELD(ID_AA64MMFR0, ASIDBITS, 4, 4) @@ -2029,6 +2075,11 @@ FIELD(ID_AA64MMFR1, SPECSEI, 24, 4) FIELD(ID_AA64MMFR1, XNX, 28, 4) FIELD(ID_AA64MMFR1, TWED, 32, 4) FIELD(ID_AA64MMFR1, ETS, 36, 4) +FIELD(ID_AA64MMFR1, HCX, 40, 4) +FIELD(ID_AA64MMFR1, AFP, 44, 4) +FIELD(ID_AA64MMFR1, NTLBPA, 48, 4) +FIELD(ID_AA64MMFR1, TIDCP1, 52, 4) +FIELD(ID_AA64MMFR1, CMOW, 56, 4) FIELD(ID_AA64MMFR2, CNP, 0, 4) FIELD(ID_AA64MMFR2, UAO, 4, 4) @@ -2055,7 +2106,10 @@ FIELD(ID_AA64DFR0, CTX_CMPS, 28, 4) FIELD(ID_AA64DFR0, PMSVER, 32, 4) FIELD(ID_AA64DFR0, DOUBLELOCK, 36, 4) FIELD(ID_AA64DFR0, TRACEFILT, 40, 4) +FIELD(ID_AA64DFR0, TRACEBUFFER, 44, 4) FIELD(ID_AA64DFR0, MTPMU, 48, 4) +FIELD(ID_AA64DFR0, BRBE, 52, 4) +FIELD(ID_AA64DFR0, HPMN0, 60, 4) FIELD(ID_AA64ZFR0, SVEVER, 0, 4) FIELD(ID_AA64ZFR0, AES, 4, 4) @@ -2077,6 +2131,7 @@ FIELD(ID_DFR0, PERFMON, 24, 4) FIELD(ID_DFR0, TRACEFILT, 28, 4) FIELD(ID_DFR1, MTPMU, 0, 4) +FIELD(ID_DFR1, HPMN0, 4, 4) FIELD(DBGDIDR, SE_IMP, 12, 1) FIELD(DBGDIDR, NSUHD_IMP, 14, 1) @@ -2752,11 +2807,6 @@ typedef enum CPAccessResult { /* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */ CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5, CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6, - /* Access fails and results in an exception syndrome for an FP access, - * trapped directly to EL2 or EL3 - */ - CP_ACCESS_TRAP_FP_EL2 = 7, - CP_ACCESS_TRAP_FP_EL3 = 8, } CPAccessResult; /* Access functions for coprocessor registers. These cannot fail and @@ -3410,9 +3460,6 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) } } -typedef CPUARMState CPUArchState; -typedef ARMCPU ArchCPU; - #include "exec/cpu-all.h" /* @@ -3548,12 +3595,12 @@ static inline int cpu_mmu_index(CPUARMState *env, bool ifetch) static inline bool bswap_code(bool sctlr_b) { #ifdef CONFIG_USER_ONLY - /* BE8 (SCTLR.B = 0, TARGET_WORDS_BIGENDIAN = 1) is mixed endian. - * The invalid combination SCTLR.B=1/CPSR.E=1/TARGET_WORDS_BIGENDIAN=0 + /* BE8 (SCTLR.B = 0, TARGET_BIG_ENDIAN = 1) is mixed endian. + * The invalid combination SCTLR.B=1/CPSR.E=1/TARGET_BIG_ENDIAN=0 * would also end up as a mixed-endian mode with BE code, LE data. */ return -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN 1 ^ #endif sctlr_b; @@ -3569,7 +3616,7 @@ static inline bool bswap_code(bool sctlr_b) static inline bool arm_cpu_bswap_data(CPUARMState *env) { return -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN 1 ^ #endif arm_cpu_data_is_big_endian(env); @@ -4284,11 +4331,38 @@ static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; } +static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; +} + +static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; +} + +static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); +} + static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; } +static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; +} + static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 1171ab16b9..2974cbc0d3 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -688,6 +688,29 @@ void aarch64_add_pauth_properties(Object *obj) } } +static Property arm_cpu_lpa2_property = + DEFINE_PROP_BOOL("lpa2", ARMCPU, prop_lpa2, true); + +void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) +{ + uint64_t t; + + /* + * We only install the property for tcg -cpu max; this is the + * only situation in which the cpu field can be true. + */ + if (!cpu->prop_lpa2) { + return; + } + + t = cpu->isar.id_aa64mmfr0; + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 2); /* 16k pages w/ LPA2 */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4, 1); /* 4k pages w/ LPA2 */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 3); /* 16k stage2 w/ LPA2 */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 3); /* 4k stage2 w/ LPA2 */ + cpu->isar.id_aa64mmfr0 = t; +} + static void aarch64_host_initfn(Object *obj) { #if defined(CONFIG_KVM) @@ -795,7 +818,11 @@ static void aarch64_max_initfn(Object *obj) cpu->isar.id_aa64pfr1 = t; t = cpu->isar.id_aa64mmfr0; - t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 5); /* PARange: 48 bits */ + t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* 16k pages supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* 16k stage2 supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* 64k stage2 supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */ cpu->isar.id_aa64mmfr0 = t; t = cpu->isar.id_aa64mmfr1; @@ -811,6 +838,9 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* TTCNP */ t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* TTST */ + t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ + t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* FEAT_TTL */ + t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* FEAT_BBM at level 2 */ cpu->isar.id_aa64mmfr2 = t; t = cpu->isar.id_aa64zfr0; @@ -892,6 +922,7 @@ static void aarch64_max_initfn(Object *obj) aarch64_add_sve_properties(obj); object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq, cpu_max_set_sve_max_vq, NULL, NULL); + qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property); } static void aarch64_a64fx_initfn(Object *obj) diff --git a/target/arm/crypto_helper.c b/target/arm/crypto_helper.c index 28a84c2dbd..d28690321f 100644 --- a/target/arm/crypto_helper.c +++ b/target/arm/crypto_helper.c @@ -15,6 +15,7 @@ #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "crypto/aes.h" +#include "crypto/sm4.h" #include "vec_internal.h" union CRYPTO_STATE { @@ -23,7 +24,7 @@ union CRYPTO_STATE { uint64_t l[2]; }; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define CR_ST_BYTE(state, i) ((state).bytes[(15 - (i)) ^ 8]) #define CR_ST_WORD(state, i) ((state).words[(3 - (i)) ^ 2]) #else @@ -694,41 +695,6 @@ DO_SM3TT(crypto_sm3tt2b, 3) #undef DO_SM3TT -static uint8_t const sm4_sbox[] = { - 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, - 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, - 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, - 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, - 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, - 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62, - 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, - 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, - 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, - 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, - 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, - 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, - 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, - 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, - 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, - 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, - 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, - 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, - 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, - 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, - 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, - 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, - 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, - 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, - 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, - 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, - 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, - 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, - 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, - 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84, - 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, - 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48, -}; - static void do_crypto_sm4e(uint64_t *rd, uint64_t *rn, uint64_t *rm) { union CRYPTO_STATE d = { .l = { rn[0], rn[1] } }; diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 32f3caec23..46893697cc 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -143,9 +143,9 @@ static bool bp_wp_matches(ARMCPU *cpu, int n, bool is_wp) * Non-Secure to simplify the code slightly compared to the full * table in the ARM ARM. */ - pac = extract64(cr, 1, 2); - hmc = extract64(cr, 13, 1); - ssc = extract64(cr, 14, 2); + pac = FIELD_EX64(cr, DBGWCR, PAC); + hmc = FIELD_EX64(cr, DBGWCR, HMC); + ssc = FIELD_EX64(cr, DBGWCR, SSC); switch (ssc) { case 0: @@ -184,8 +184,8 @@ static bool bp_wp_matches(ARMCPU *cpu, int n, bool is_wp) g_assert_not_reached(); } - wt = extract64(cr, 20, 1); - lbn = extract64(cr, 16, 4); + wt = FIELD_EX64(cr, DBGWCR, WT); + lbn = FIELD_EX64(cr, DBGWCR, LBN); if (wt && !linked_bp_matches(cpu, lbn)) { return false; diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 7cf953b1e6..77a8502b6b 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -952,7 +952,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) qemu_mutex_unlock_iothread(); if (!return_to_aa64) { - env->aarch64 = 0; + env->aarch64 = false; /* We do a raw CPSR write because aarch64_sync_64_to_32() * will sort the register banks out for us, and we've already * caught all the bad-mode cases in el_from_spsr(). @@ -975,7 +975,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) } else { int tbii; - env->aarch64 = 1; + env->aarch64 = true; spsr &= aarch64_pstate_valid_mask(&env_archcpu(env)->isar); pstate_write(env, spsr); if (!arm_singlestep_active(env)) { diff --git a/target/arm/helper.c b/target/arm/helper.c index 7bf50fdd76..5a244c3ed9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -27,7 +27,6 @@ #include "sysemu/cpus.h" #include "sysemu/cpu-timers.h" #include "sysemu/kvm.h" -#include "sysemu/tcg.h" #include "qemu/range.h" #include "qapi/qapi-commands-machine-target.h" #include "qapi/error.h" @@ -4511,70 +4510,73 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, } #ifdef TARGET_AARCH64 -static uint64_t tlbi_aa64_range_get_length(CPUARMState *env, - uint64_t value) -{ - unsigned int page_shift; - unsigned int page_size_granule; - uint64_t num; - uint64_t scale; - uint64_t exponent; +typedef struct { + uint64_t base; uint64_t length; +} TLBIRange; + +static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, + uint64_t value) +{ + unsigned int page_size_granule, page_shift, num, scale, exponent; + /* Extract one bit to represent the va selector in use. */ + uint64_t select = sextract64(value, 36, 1); + ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true); + TLBIRange ret = { }; - num = extract64(value, 39, 5); - scale = extract64(value, 44, 2); page_size_granule = extract64(value, 46, 2); - if (page_size_granule == 0) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid page size granule %d\n", + /* The granule encoded in value must match the granule in use. */ + if (page_size_granule != (param.using64k ? 3 : param.using16k ? 2 : 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", page_size_granule); - return 0; + return ret; } page_shift = (page_size_granule - 1) * 2 + 12; - + num = extract64(value, 39, 5); + scale = extract64(value, 44, 2); exponent = (5 * scale) + 1; - length = (num + 1) << (exponent + page_shift); - return length; -} + ret.length = (num + 1) << (exponent + page_shift); -static uint64_t tlbi_aa64_range_get_base(CPUARMState *env, uint64_t value, - bool two_ranges) -{ - /* TODO: ARMv8.7 FEAT_LPA2 */ - uint64_t pageaddr; - - if (two_ranges) { - pageaddr = sextract64(value, 0, 37) << TARGET_PAGE_BITS; + if (param.select) { + ret.base = sextract64(value, 0, 37); } else { - pageaddr = extract64(value, 0, 37) << TARGET_PAGE_BITS; + ret.base = extract64(value, 0, 37); } + if (param.ds) { + /* + * With DS=1, BaseADDR is always shifted 16 so that it is able + * to address all 52 va bits. The input address is perforce + * aligned on a 64k boundary regardless of translation granule. + */ + page_shift = 16; + } + ret.base <<= page_shift; - return pageaddr; + return ret; } static void do_rvae_write(CPUARMState *env, uint64_t value, int idxmap, bool synced) { ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap); - bool two_ranges = regime_has_2_ranges(one_idx); - uint64_t baseaddr, length; + TLBIRange range; int bits; - baseaddr = tlbi_aa64_range_get_base(env, value, two_ranges); - length = tlbi_aa64_range_get_length(env, value); - bits = tlbbits_for_regime(env, one_idx, baseaddr); + range = tlbi_aa64_get_range(env, one_idx, value); + bits = tlbbits_for_regime(env, one_idx, range.base); if (synced) { tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env), - baseaddr, - length, + range.base, + range.length, idxmap, bits); } else { - tlb_flush_range_by_mmuidx(env_cpu(env), baseaddr, - length, idxmap, bits); + tlb_flush_range_by_mmuidx(env_cpu(env), range.base, + range.length, idxmap, bits); } } @@ -4782,18 +4784,6 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, } } -static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if ((env->cp15.cptr_el[2] & CPTR_TFP) && arm_current_el(env) == 2) { - return CP_ACCESS_TRAP_FP_EL2; - } - if (env->cp15.cptr_el[3] & CPTR_TFP) { - return CP_ACCESS_TRAP_FP_EL3; - } - return CP_ACCESS_OK; -} - static void sdcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -5095,9 +5085,8 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write }, { .name = "FPEXC32_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 3, .opc2 = 0, - .type = ARM_CP_ALIAS, - .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]), - .access = PL2_RW, .accessfn = fpexc32_access }, + .access = PL2_RW, .type = ARM_CP_ALIAS | ARM_CP_FPU, + .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]) }, { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, .access = PL2_RW, .resetvalue = 0, @@ -6331,12 +6320,12 @@ void hw_watchpoint_update(ARMCPU *cpu, int n) env->cpu_watchpoint[n] = NULL; } - if (!extract64(wcr, 0, 1)) { + if (!FIELD_EX64(wcr, DBGWCR, E)) { /* E bit clear : watchpoint disabled */ return; } - switch (extract64(wcr, 3, 2)) { + switch (FIELD_EX64(wcr, DBGWCR, LSC)) { case 0: /* LSC 00 is reserved and must behave as if the wp is disabled */ return; @@ -6355,7 +6344,7 @@ void hw_watchpoint_update(ARMCPU *cpu, int n) * CONSTRAINED UNPREDICTABLE; we opt to ignore BAS in this case, * thus generating a watchpoint for every byte in the masked region. */ - mask = extract64(wcr, 24, 4); + mask = FIELD_EX64(wcr, DBGWCR, MASK); if (mask == 1 || mask == 2) { /* Reserved values of MASK; we must act as if the mask value was * some non-reserved value, or as if the watchpoint were disabled. @@ -6372,7 +6361,7 @@ void hw_watchpoint_update(ARMCPU *cpu, int n) wvr &= ~(len - 1); } else { /* Watchpoint covers bytes defined by the byte address select bits */ - int bas = extract64(wcr, 5, 8); + int bas = FIELD_EX64(wcr, DBGWCR, BAS); int basstart; if (extract64(wvr, 2, 1)) { @@ -6423,11 +6412,18 @@ static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = env_archcpu(env); int i = ri->crm; - /* Bits [63:49] are hardwired to the value of bit [48]; that is, the - * register reads and behaves as if values written are sign extended. + /* * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether [63:49] ([63:53] with FEAT_LVA) + * are hardwired to the value of bit [48] ([52] with FEAT_LVA), or if + * they contain the value written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * + * Therefore we are allowed to compare the entire register, which lets + * us avoid considering whether or not FEAT_LVA is actually enabled. */ - value = sextract64(value, 0, 49) & ~3ULL; + value &= ~3ULL; raw_write(env, ri, value); hw_watchpoint_update(cpu, i); @@ -6473,10 +6469,19 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) case 0: /* unlinked address match */ case 1: /* linked address match */ { - /* Bits [63:49] are hardwired to the value of bit [48]; that is, - * we behave as if the register was sign extended. Bits [1:0] are - * RES0. The BAS field is used to allow setting breakpoints on 16 - * bit wide instructions; it is CONSTRAINED UNPREDICTABLE whether + /* + * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether bits [63:49] + * ([63:53] for FEAT_LVA) are hardwired to a copy of the sign bit + * of the VA field ([48] or [52] for FEAT_LVA), or whether the + * value is read as written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * Therefore we are allowed to compare the entire register, which + * lets us avoid considering whether FEAT_LVA is actually enabled. + * + * The BAS field is used to allow setting breakpoints on 16-bit + * wide instructions; it is CONSTRAINED UNPREDICTABLE whether * a bp will fire if the addresses covered by the bp and the addresses * covered by the insn overlap but the insn doesn't start at the * start of the bp address range. We choose to require the insn and @@ -6489,7 +6494,7 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) * See also figure D2-3 in the v8 ARM ARM (DDI0487A.c). */ int bas = extract64(bcr, 5, 4); - addr = sextract64(bvr, 0, 49) & ~3ULL; + addr = bvr & ~3ULL; if (bas == 0) { return; } @@ -7157,7 +7162,7 @@ static CPAccessResult access_mte(CPUARMState *env, const ARMCPRegInfo *ri, { int el = arm_current_el(env); - if (el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { + if (el < 2 && arm_is_el2_enabled(env)) { uint64_t hcr = arm_hcr_el2_eff(env); if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) { return CP_ACCESS_TRAP_EL2; @@ -7948,7 +7953,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo rvbar = { .name = "RVBAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, - .type = ARM_CP_CONST, .access = PL1_R, .resetvalue = cpu->rvbar + .access = PL1_R, + .fieldoffset = offsetof(CPUARMState, cp15.rvbar), }; define_one_arm_cp_reg(cpu, &rvbar); } @@ -7992,7 +7998,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo rvbar = { .name = "RVBAR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 1, - .type = ARM_CP_CONST, .access = PL2_R, .resetvalue = cpu->rvbar + .access = PL2_R, + .fieldoffset = offsetof(CPUARMState, cp15.rvbar), }; define_one_arm_cp_reg(cpu, &rvbar); } @@ -8029,7 +8036,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo el3_regs[] = { { .name = "RVBAR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 1, - .type = ARM_CP_CONST, .access = PL3_R, .resetvalue = cpu->rvbar }, + .access = PL3_R, + .fieldoffset = offsetof(CPUARMState, cp15.rvbar), + }, { .name = "SCTLR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 0, .opc2 = 0, .access = PL3_RW, @@ -8619,7 +8628,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->cp = 15; } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN if (r2->fieldoffset) { r2->fieldoffset += sizeof(uint32_t); } @@ -10159,7 +10168,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } pstate_write(env, PSTATE_DAIF | new_mode); - env->aarch64 = 1; + env->aarch64 = true; aarch64_restore_sp(env, new_el); helper_rebuild_hflags_a64(env, new_el); @@ -11065,13 +11074,18 @@ do_fault: * false otherwise. */ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, - int inputsize, int stride) + int inputsize, int stride, int outputsize) { const int grainsize = stride + 3; int startsizecheck; - /* Negative levels are never allowed. */ - if (level < 0) { + /* + * Negative levels are usually not allowed... + * Except for FEAT_LPA2, 4k page table, 52-bit address space, which + * begins with level -1. Note that previous feature tests will have + * eliminated this combination if it is not enabled. + */ + if (level < (inputsize == 52 && stride == 9 ? -1 : 0)) { return false; } @@ -11081,22 +11095,19 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, } if (is_aa64) { - CPUARMState *env = &cpu->env; - unsigned int pamax = arm_pamax(cpu); - switch (stride) { case 13: /* 64KB Pages. */ - if (level == 0 || (level == 1 && pamax <= 42)) { + if (level == 0 || (level == 1 && outputsize <= 42)) { return false; } break; case 11: /* 16KB Pages. */ - if (level == 0 || (level == 1 && pamax <= 40)) { + if (level == 0 || (level == 1 && outputsize <= 40)) { return false; } break; case 9: /* 4KB Pages. */ - if (level == 0 && pamax <= 42) { + if (level == 0 && outputsize <= 42) { return false; } break; @@ -11105,8 +11116,8 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, } /* Inputsize checks. */ - if (inputsize > pamax && - (arm_el_is_aa64(env, 1) || inputsize > 40)) { + if (inputsize > outputsize && + (arm_el_is_aa64(&cpu->env, 1) || inputsize > 40)) { /* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */ return false; } @@ -11152,6 +11163,31 @@ static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs) } #endif /* !CONFIG_USER_ONLY */ +/* This mapping is common between ID_AA64MMFR0.PARANGE and TCR_ELx.{I}PS. */ +static const uint8_t pamax_map[] = { + [0] = 32, + [1] = 36, + [2] = 40, + [3] = 42, + [4] = 44, + [5] = 48, + [6] = 52, +}; + +/* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */ +unsigned int arm_pamax(ARMCPU *cpu) +{ + unsigned int parange = + FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + + /* + * id_aa64mmfr0 is a read-only register so values outside of the + * supported mappings can be considered an implementation error. + */ + assert(parange < ARRAY_SIZE(pamax_map)); + return pamax_map[parange]; +} + static int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx) { if (regime_has_2_ranges(mmu_idx)) { @@ -11190,8 +11226,9 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, ARMMMUIdx mmu_idx, bool data) { uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; - bool epd, hpd, using16k, using64k; - int select, tsz, tbi, max_tsz; + bool epd, hpd, using16k, using64k, tsz_oob, ds; + int select, tsz, tbi, max_tsz, min_tsz, ps, sh; + ARMCPU *cpu = env_archcpu(env); if (!regime_has_2_ranges(mmu_idx)) { select = 0; @@ -11205,6 +11242,9 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, hpd = extract32(tcr, 24, 1); } epd = false; + sh = extract32(tcr, 12, 2); + ps = extract32(tcr, 16, 3); + ds = extract64(tcr, 32, 1); } else { /* * Bit 55 is always between the two regions, and is canonical for @@ -11214,6 +11254,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, if (!select) { tsz = extract32(tcr, 0, 6); epd = extract32(tcr, 7, 1); + sh = extract32(tcr, 12, 2); using64k = extract32(tcr, 14, 1); using16k = extract32(tcr, 15, 1); hpd = extract64(tcr, 41, 1); @@ -11223,18 +11264,61 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, using64k = tg == 3; tsz = extract32(tcr, 16, 6); epd = extract32(tcr, 23, 1); + sh = extract32(tcr, 28, 2); hpd = extract64(tcr, 42, 1); } + ps = extract64(tcr, 32, 3); + ds = extract64(tcr, 59, 1); } - if (cpu_isar_feature(aa64_st, env_archcpu(env))) { + if (cpu_isar_feature(aa64_st, cpu)) { max_tsz = 48 - using64k; } else { max_tsz = 39; } - tsz = MIN(tsz, max_tsz); - tsz = MAX(tsz, 16); /* TODO: ARMv8.2-LVA */ + /* + * DS is RES0 unless FEAT_LPA2 is supported for the given page size; + * adjust the effective value of DS, as documented. + */ + min_tsz = 16; + if (using64k) { + if (cpu_isar_feature(aa64_lva, cpu)) { + min_tsz = 12; + } + ds = false; + } else if (ds) { + switch (mmu_idx) { + case ARMMMUIdx_Stage2: + case ARMMMUIdx_Stage2_S: + if (using16k) { + ds = cpu_isar_feature(aa64_tgran16_2_lpa2, cpu); + } else { + ds = cpu_isar_feature(aa64_tgran4_2_lpa2, cpu); + } + break; + default: + if (using16k) { + ds = cpu_isar_feature(aa64_tgran16_lpa2, cpu); + } else { + ds = cpu_isar_feature(aa64_tgran4_lpa2, cpu); + } + break; + } + if (ds) { + min_tsz = 12; + } + } + + if (tsz > max_tsz) { + tsz = max_tsz; + tsz_oob = true; + } else if (tsz < min_tsz) { + tsz = min_tsz; + tsz_oob = true; + } else { + tsz_oob = false; + } /* Present TBI as a composite with TBID. */ tbi = aa64_va_parameter_tbi(tcr, mmu_idx); @@ -11245,12 +11329,16 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, return (ARMVAParameters) { .tsz = tsz, + .ps = ps, + .sh = sh, .select = select, .tbi = tbi, .epd = epd, .hpd = hpd, .using16k = using16k, .using64k = using64k, + .tsz_oob = tsz_oob, + .ds = ds, }; } @@ -11361,7 +11449,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, target_ulong page_size; uint32_t attrs; int32_t stride; - int addrsize, inputsize; + int addrsize, inputsize, outputsize; TCR *tcr = regime_tcr(env, mmu_idx); int ap, ns, xn, pxn; uint32_t el = regime_el(env, mmu_idx); @@ -11371,16 +11459,44 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, /* TODO: This code does not support shareability levels. */ if (aarch64) { + int ps; + param = aa64_va_parameters(env, address, mmu_idx, access_type != MMU_INST_FETCH); level = 0; + + /* + * If TxSZ is programmed to a value larger than the maximum, + * or smaller than the effective minimum, it is IMPLEMENTATION + * DEFINED whether we behave as if the field were programmed + * within bounds, or if a level 0 Translation fault is generated. + * + * With FEAT_LVA, fault on less than minimum becomes required, + * so our choice is to always raise the fault. + */ + if (param.tsz_oob) { + fault_type = ARMFault_Translation; + goto do_fault; + } + addrsize = 64 - 8 * param.tbi; inputsize = 64 - param.tsz; + + /* + * Bound PS by PARANGE to find the effective output address size. + * ID_AA64MMFR0 is a read-only register so values outside of the + * supported mappings can be considered an implementation error. + */ + ps = FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + ps = MIN(ps, param.ps); + assert(ps < ARRAY_SIZE(pamax_map)); + outputsize = pamax_map[ps]; } else { param = aa32_va_parameters(env, address, mmu_idx); level = 1; addrsize = (mmu_idx == ARMMMUIdx_Stage2 ? 40 : 32); inputsize = addrsize - param.tsz; + outputsize = 40; } /* @@ -11448,10 +11564,19 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, * VTCR_EL2.SL0 field (whose interpretation depends on the page size) */ uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2); + uint32_t sl2 = extract64(tcr->raw_tcr, 33, 1); uint32_t startlevel; bool ok; - if (!aarch64 || stride == 9) { + /* SL2 is RES0 unless DS=1 & 4kb granule. */ + if (param.ds && stride == 9 && sl2) { + if (sl0 != 0) { + level = 0; + fault_type = ARMFault_Translation; + goto do_fault; + } + startlevel = -1; + } else if (!aarch64 || stride == 9) { /* AArch32 or 4KB pages */ startlevel = 2 - sl0; @@ -11465,7 +11590,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, /* Check that the starting level is valid. */ ok = check_s2_mmu_setup(cpu, aarch64, startlevel, - inputsize, stride); + inputsize, stride, outputsize); if (!ok) { fault_type = ARMFault_Translation; goto do_fault; @@ -11473,24 +11598,49 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, level = startlevel; } - indexmask_grainsize = (1ULL << (stride + 3)) - 1; - indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1; + indexmask_grainsize = MAKE_64BIT_MASK(0, stride + 3); + indexmask = MAKE_64BIT_MASK(0, inputsize - (stride * (4 - level))); /* Now we can extract the actual base address from the TTBR */ descaddr = extract64(ttbr, 0, 48); + + /* + * For FEAT_LPA and PS=6, bits [51:48] of descaddr are in [5:2] of TTBR. + * + * Otherwise, if the base address is out of range, raise AddressSizeFault. + * In the pseudocode, this is !IsZero(baseregister<47:outputsize>), + * but we've just cleared the bits above 47, so simplify the test. + */ + if (outputsize > 48) { + descaddr |= extract64(ttbr, 2, 4) << 48; + } else if (descaddr >> outputsize) { + level = 0; + fault_type = ARMFault_AddressSize; + goto do_fault; + } + /* * We rely on this masking to clear the RES0 bits at the bottom of the TTBR * and also to mask out CnP (bit 0) which could validly be non-zero. */ descaddr &= ~indexmask; - /* The address field in the descriptor goes up to bit 39 for ARMv7 - * but up to bit 47 for ARMv8, but we use the descaddrmask - * up to bit 39 for AArch32, because we don't need other bits in that case - * to construct next descriptor address (anyway they should be all zeroes). + /* + * For AArch32, the address field in the descriptor goes up to bit 39 + * for both v7 and v8. However, for v8 the SBZ bits [47:40] must be 0 + * or an AddressSize fault is raised. So for v8 we extract those SBZ + * bits as part of the address, which will be checked via outputsize. + * For AArch64, the address field goes up to bit 47, or 49 with FEAT_LPA2; + * the highest bits of a 52-bit output are placed elsewhere. */ - descaddrmask = ((1ull << (aarch64 ? 48 : 40)) - 1) & - ~indexmask_grainsize; + if (param.ds) { + descaddrmask = MAKE_64BIT_MASK(0, 50); + } else if (arm_feature(env, ARM_FEATURE_V8)) { + descaddrmask = MAKE_64BIT_MASK(0, 48); + } else { + descaddrmask = MAKE_64BIT_MASK(0, 40); + } + descaddrmask &= ~indexmask_grainsize; /* Secure accesses start with the page table in secure memory and * can be downgraded to non-secure at any step. Non-secure accesses @@ -11515,8 +11665,26 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, /* Invalid, or the Reserved level 3 encoding */ goto do_fault; } + descaddr = descriptor & descaddrmask; + /* + * For FEAT_LPA and PS=6, bits [51:48] of descaddr are in [15:12] + * of descriptor. For FEAT_LPA2 and effective DS, bits [51:50] of + * descaddr are in [9:8]. Otherwise, if descaddr is out of range, + * raise AddressSizeFault. + */ + if (outputsize > 48) { + if (param.ds) { + descaddr |= extract64(descriptor, 8, 2) << 50; + } else { + descaddr |= extract64(descriptor, 12, 4) << 48; + } + } else if (descaddr >> outputsize) { + fault_type = ARMFault_AddressSize; + goto do_fault; + } + if ((descriptor & 2) && (level < 3)) { /* Table entry. The top five bits are attributes which may * propagate down through lower levels of the table (and @@ -11528,11 +11696,17 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, indexmask = indexmask_grainsize; continue; } - /* Block entry at level 1 or 2, or page entry at level 3. + /* + * Block entry at level 1 or 2, or page entry at level 3. * These are basically the same thing, although the number - * of bits we pull in from the vaddr varies. + * of bits we pull in from the vaddr varies. Note that although + * descaddrmask masks enough of the low bits of the descriptor + * to give a correct page or table address, the address field + * in a block descriptor is smaller; so we need to explicitly + * clear the lower bits here before ORing in the low vaddr bits. */ page_size = (1ULL << ((stride * (4 - level)) + 3)); + descaddr &= ~(page_size - 1); descaddr |= (address & (page_size - 1)); /* Extract attributes from the descriptor */ attrs = extract64(descriptor, 2, 10) @@ -11605,7 +11779,17 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, assert(attrindx <= 7); cacheattrs->attrs = extract64(mair, attrindx * 8, 8); } - cacheattrs->shareability = extract32(attrs, 6, 2); + + /* + * For FEAT_LPA2 and effective DS, the SH field in the attributes + * was re-purposed for output address bits. The SH attribute in + * that case comes from TCR_ELx, which we extracted earlier. + */ + if (param.ds) { + cacheattrs->shareability = param.sh; + } else { + cacheattrs->shareability = extract32(attrs, 6, 2); + } *phys_ptr = descaddr; *page_size_ptr = page_size; @@ -12446,6 +12630,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, hwaddr ipa; int s2_prot; int ret; + bool ipa_secure; ARMCacheAttrs cacheattrs2 = {}; ARMMMUIdx s2_mmu_idx; bool is_el0; @@ -12459,6 +12644,17 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, return ret; } + ipa_secure = attrs->secure; + if (arm_is_secure_below_el3(env)) { + if (ipa_secure) { + attrs->secure = !(env->cp15.vstcr_el2.raw_tcr & VSTCR_SW); + } else { + attrs->secure = !(env->cp15.vtcr_el2.raw_tcr & VTCR_NSW); + } + } else { + assert(!ipa_secure); + } + s2_mmu_idx = attrs->secure ? ARMMMUIdx_Stage2_S : ARMMMUIdx_Stage2; is_el0 = mmu_idx == ARMMMUIdx_E10_0 || mmu_idx == ARMMMUIdx_SE10_0; @@ -12493,13 +12689,13 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, /* Check if IPA translates to secure or non-secure PA space. */ if (arm_is_secure_below_el3(env)) { - if (attrs->secure) { + if (ipa_secure) { attrs->secure = !(env->cp15.vstcr_el2.raw_tcr & (VSTCR_SA | VSTCR_SW)); } else { attrs->secure = !((env->cp15.vtcr_el2.raw_tcr & (VTCR_NSA | VTCR_NSW)) - || (env->cp15.vstcr_el2.raw_tcr & VSTCR_SA)); + || (env->cp15.vstcr_el2.raw_tcr & (VSTCR_SA | VSTCR_SW))); } } return 0; diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 4d4ddab348..b11a8b9a18 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -10,7 +10,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "sysemu/runstate.h" @@ -565,7 +564,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) hv_return_t ret; int i; - env->aarch64 = 1; + env->aarch64 = true; asm volatile("mrs %0, cntfrq_el0" : "=r"(arm_cpu->gt_cntfrq_hz)); /* Allocate enough space for our sysreg sync */ @@ -678,7 +677,7 @@ static bool hvf_handle_psci_call(CPUState *cpu) switch (param[0]) { case QEMU_PSCI_0_2_FN_PSCI_VERSION: - ret = QEMU_PSCI_0_2_RET_VERSION_0_2; + ret = QEMU_PSCI_VERSION_1_1; break; case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */ @@ -746,6 +745,31 @@ static bool hvf_handle_psci_call(CPUState *cpu) case QEMU_PSCI_0_2_FN_MIGRATE: ret = QEMU_PSCI_RET_NOT_SUPPORTED; break; + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + switch (param[1]) { + case QEMU_PSCI_0_2_FN_PSCI_VERSION: + case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: + case QEMU_PSCI_0_2_FN_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN64_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN_SYSTEM_RESET: + case QEMU_PSCI_0_2_FN_SYSTEM_OFF: + case QEMU_PSCI_0_1_FN_CPU_ON: + case QEMU_PSCI_0_2_FN_CPU_ON: + case QEMU_PSCI_0_2_FN64_CPU_ON: + case QEMU_PSCI_0_1_FN_CPU_OFF: + case QEMU_PSCI_0_2_FN_CPU_OFF: + case QEMU_PSCI_0_1_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN64_CPU_SUSPEND: + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + ret = 0; + break; + case QEMU_PSCI_0_1_FN_MIGRATE: + case QEMU_PSCI_0_2_FN_MIGRATE: + default: + ret = QEMU_PSCI_RET_NOT_SUPPORTED; + } + break; default: return false; } diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index ea238cff83..9a9d1a0bf5 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -13,6 +13,6 @@ #include "cpu.h" -void hvf_arm_set_cpu_features_from_host(struct ARMCPU *cpu); +void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); #endif diff --git a/target/arm/internals.h b/target/arm/internals.h index 3f05748ea4..255833479d 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -81,6 +81,18 @@ FIELD(V7M_EXCRET, RES1, 7, 25) /* including the must-be-1 prefix */ */ #define FNC_RETURN_MIN_MAGIC 0xfefffffe +/* Bit definitions for DBGWCRn and DBGWCRn_EL1 */ +FIELD(DBGWCR, E, 0, 1) +FIELD(DBGWCR, PAC, 1, 2) +FIELD(DBGWCR, LSC, 3, 2) +FIELD(DBGWCR, BAS, 5, 8) +FIELD(DBGWCR, HMC, 13, 1) +FIELD(DBGWCR, SSC, 14, 2) +FIELD(DBGWCR, LBN, 16, 4) +FIELD(DBGWCR, WT, 20, 1) +FIELD(DBGWCR, MASK, 24, 5) +FIELD(DBGWCR, SSCE, 29, 1) + /* We use a few fake FSR values for internal purposes in M profile. * M profile cores don't have A/R format FSRs, but currently our * get_phys_addr() code assumes A/R profile and reports failures via @@ -102,13 +114,13 @@ FIELD(V7M_EXCRET, RES1, 7, 25) /* including the must-be-1 prefix */ * and target exception level. This should be called from helper functions, * and never returns because we will longjump back up to the CPU main loop. */ -void QEMU_NORETURN raise_exception(CPUARMState *env, uint32_t excp, - uint32_t syndrome, uint32_t target_el); +G_NORETURN void raise_exception(CPUARMState *env, uint32_t excp, + uint32_t syndrome, uint32_t target_el); /* * Similarly, but also use unwinding to restore cpu state. */ -void QEMU_NORETURN raise_exception_ra(CPUARMState *env, uint32_t excp, +G_NORETURN void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, uint32_t target_el, uintptr_t ra); @@ -243,24 +255,7 @@ static inline void update_spsel(CPUARMState *env, uint32_t imm) * Returns the implementation defined bit-width of physical addresses. * The ARMv8 reference manuals refer to this as PAMax(). */ -static inline unsigned int arm_pamax(ARMCPU *cpu) -{ - static const unsigned int pamax_map[] = { - [0] = 32, - [1] = 36, - [2] = 40, - [3] = 42, - [4] = 44, - [5] = 48, - }; - unsigned int parange = - FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); - - /* id_aa64mmfr0 is a read-only register so values outside of the - * supported mappings can be considered an implementation error. */ - assert(parange < ARRAY_SIZE(pamax_map)); - return pamax_map[parange]; -} +unsigned int arm_pamax(ARMCPU *cpu); /* Return true if extended addresses are enabled. * This is always the case if our translation regime is 64 bit, @@ -479,28 +474,51 @@ static inline uint32_t arm_fi_to_lfsc(ARMMMUFaultInfo *fi) case ARMFault_None: return 0; case ARMFault_AddressSize: - fsc = fi->level & 3; + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b101001; + } else { + fsc = fi->level; + } break; case ARMFault_AccessFlag: - fsc = (fi->level & 3) | (0x2 << 2); + assert(fi->level >= 0 && fi->level <= 3); + fsc = 0b001000 | fi->level; break; case ARMFault_Permission: - fsc = (fi->level & 3) | (0x3 << 2); + assert(fi->level >= 0 && fi->level <= 3); + fsc = 0b001100 | fi->level; break; case ARMFault_Translation: - fsc = (fi->level & 3) | (0x1 << 2); + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b101011; + } else { + fsc = 0b000100 | fi->level; + } break; case ARMFault_SyncExternal: fsc = 0x10 | (fi->ea << 12); break; case ARMFault_SyncExternalOnWalk: - fsc = (fi->level & 3) | (0x5 << 2) | (fi->ea << 12); + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b010011; + } else { + fsc = 0b010100 | fi->level; + } + fsc |= fi->ea << 12; break; case ARMFault_SyncParity: fsc = 0x18; break; case ARMFault_SyncParityOnWalk: - fsc = (fi->level & 3) | (0x7 << 2); + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b011011; + } else { + fsc = 0b011100 | fi->level; + } break; case ARMFault_AsyncParity: fsc = 0x19; @@ -600,9 +618,9 @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate); bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx); /* Raise a data fault alignment exception for the specified virtual address */ -void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); /* arm_cpu_do_transaction_failed: handle a memory system error response * (eg "no device/memory present at address") by raising an external abort @@ -1049,12 +1067,16 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id) */ typedef struct ARMVAParameters { unsigned tsz : 8; + unsigned ps : 3; + unsigned sh : 2; unsigned select : 1; bool tbi : 1; bool epd : 1; bool hpd : 1; bool using16k : 1; bool using64k : 1; + bool tsz_oob : 1; /* tsz has been clamped to legal range */ + bool ds : 1; } ARMVAParameters; ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, @@ -1084,7 +1106,7 @@ static inline bool allocation_tag_access_enabled(CPUARMState *env, int el, && !(env->cp15.scr_el3 & SCR_ATA)) { return false; } - if (el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { + if (el < 2 && arm_is_el2_enabled(env)) { uint64_t hcr = arm_hcr_el2_eff(env); if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) { return false; diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index 580f1c1fee..faacf96fdc 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -77,6 +77,8 @@ MISMATCH_CHECK(QEMU_PSCI_0_1_FN_MIGRATE, KVM_PSCI_FN_MIGRATE); #define QEMU_PSCI_0_2_FN64_AFFINITY_INFO QEMU_PSCI_0_2_FN64(4) #define QEMU_PSCI_0_2_FN64_MIGRATE QEMU_PSCI_0_2_FN64(5) +#define QEMU_PSCI_1_0_FN_PSCI_FEATURES QEMU_PSCI_0_2_FN(10) + MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_SUSPEND, PSCI_0_2_FN_CPU_SUSPEND); MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_OFF, PSCI_0_2_FN_CPU_OFF); MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_ON, PSCI_0_2_FN_CPU_ON); @@ -84,18 +86,22 @@ MISMATCH_CHECK(QEMU_PSCI_0_2_FN_MIGRATE, PSCI_0_2_FN_MIGRATE); MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_SUSPEND, PSCI_0_2_FN64_CPU_SUSPEND); MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_ON, PSCI_0_2_FN64_CPU_ON); MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE, PSCI_0_2_FN64_MIGRATE); +MISMATCH_CHECK(QEMU_PSCI_1_0_FN_PSCI_FEATURES, PSCI_1_0_FN_PSCI_FEATURES); /* PSCI v0.2 return values used by TCG emulation of PSCI */ /* No Trusted OS migration to worry about when offlining CPUs */ #define QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED 2 -/* We implement version 0.2 only */ -#define QEMU_PSCI_0_2_RET_VERSION_0_2 2 +#define QEMU_PSCI_VERSION_0_1 0x00001 +#define QEMU_PSCI_VERSION_0_2 0x00002 +#define QEMU_PSCI_VERSION_1_0 0x10000 +#define QEMU_PSCI_VERSION_1_1 0x10001 MISMATCH_CHECK(QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED, PSCI_0_2_TOS_MP); -MISMATCH_CHECK(QEMU_PSCI_0_2_RET_VERSION_0_2, - (PSCI_VERSION_MAJOR(0) | PSCI_VERSION_MINOR(2))); +/* We don't bother to check every possible version value */ +MISMATCH_CHECK(QEMU_PSCI_VERSION_0_2, PSCI_VERSION(0, 2)); +MISMATCH_CHECK(QEMU_PSCI_VERSION_1_1, PSCI_VERSION(1, 1)); /* PSCI return values (inclusive of all PSCI versions) */ #define QEMU_PSCI_RET_SUCCESS 0 diff --git a/target/arm/kvm.c b/target/arm/kvm.c index bbf1ce7ba3..5fc37ac10a 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -13,7 +13,6 @@ #include -#include "qemu-common.h" #include "qemu/timer.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 71c3ca6971..b8cfaf5782 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -16,7 +16,6 @@ #include #include -#include "qemu-common.h" #include "qapi/error.h" #include "cpu.h" #include "qemu/timer.h" @@ -209,7 +208,7 @@ static int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type) { HWWatchpoint wp = { - .wcr = 1, /* E=1, enable */ + .wcr = R_DBGWCR_E_MASK, /* E=1, enable */ .wvr = addr & (~0x7ULL), .details = { .vaddr = addr, .len = len } }; @@ -222,19 +221,19 @@ static int insert_hw_watchpoint(target_ulong addr, * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state, * valid whether EL3 is implemented or not */ - wp.wcr = deposit32(wp.wcr, 1, 2, 3); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3); switch (type) { case GDB_WATCHPOINT_READ: - wp.wcr = deposit32(wp.wcr, 3, 2, 1); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1); wp.details.flags = BP_MEM_READ; break; case GDB_WATCHPOINT_WRITE: - wp.wcr = deposit32(wp.wcr, 3, 2, 2); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2); wp.details.flags = BP_MEM_WRITE; break; case GDB_WATCHPOINT_ACCESS: - wp.wcr = deposit32(wp.wcr, 3, 2, 3); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3); wp.details.flags = BP_MEM_ACCESS; break; default: @@ -253,8 +252,8 @@ static int insert_hw_watchpoint(target_ulong addr, int bits = ctz64(len); wp.wvr &= ~((1 << bits) - 1); - wp.wcr = deposit32(wp.wcr, 24, 4, bits); - wp.wcr = deposit32(wp.wcr, 5, 8, 0xff); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff); } else { return -ENOBUFS; } @@ -849,6 +848,7 @@ int kvm_arch_init_vcpu(CPUState *cs) uint64_t mpidr; ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + uint64_t psciver; if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || !object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) { @@ -864,7 +864,7 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; } if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) { - cpu->psci_version = 2; + cpu->psci_version = QEMU_PSCI_VERSION_0_2; cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; } if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { @@ -904,6 +904,17 @@ int kvm_arch_init_vcpu(CPUState *cs) } } + /* + * KVM reports the exact PSCI version it is implementing via a + * special sysreg. If it is present, use its contents to determine + * what to report to the guest in the dtb (it is the PSCI version, + * in the same 15-bits major 16-bits minor format that PSCI_VERSION + * returns). + */ + if (!kvm_get_one_reg(cs, KVM_REG_ARM_PSCI_VERSION, &psciver)) { + cpu->psci_version = psciver; + } + /* * When KVM is in use, PSCI is emulated in-kernel and not by qemu. * Currently KVM has its own idea about MPIDR assignment, so we @@ -1011,7 +1022,7 @@ static int kvm_arch_put_fpsimd(CPUState *cs) for (i = 0; i < 32; i++) { uint64_t *q = aa64_vfp_qreg(env, i); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint64_t fp_val[2] = { q[1], q[0] }; reg.addr = (uintptr_t)fp_val; #else @@ -1230,7 +1241,7 @@ static int kvm_arch_get_fpsimd(CPUState *cs) if (ret) { return ret; } else { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint64_t t; t = q[0], q[0] = q[1], q[1] = t; #endif diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 648a3b3fc1..a740c3e160 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -564,7 +564,7 @@ void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; } switch_v7m_security_state(env, dest & 1); - env->thumb = 1; + env->thumb = true; env->regs[15] = dest & ~1; arm_rebuild_hflags(env); } @@ -590,7 +590,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) * except that the low bit doesn't indicate Thumb/not. */ env->regs[14] = nextinst; - env->thumb = 1; + env->thumb = true; env->regs[15] = dest & ~1; return; } @@ -626,7 +626,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) } env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; switch_v7m_security_state(env, 0); - env->thumb = 1; + env->thumb = true; env->regs[15] = dest; arm_rebuild_hflags(env); } @@ -679,6 +679,10 @@ static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure, ARMMMUIdx mmu_idx; bool exc_secure; + qemu_log_mask(CPU_LOG_INT, + "...loading from element %d of %s vector table at 0x%x\n", + exc, targets_secure ? "secure" : "non-secure", addr); + mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targets_secure, true); /* @@ -719,6 +723,7 @@ static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure, goto load_fail; } *pvec = vector_entry; + qemu_log_mask(CPU_LOG_INT, "...loaded new PC 0x%x\n", *pvec); return true; load_fail: @@ -2267,7 +2272,13 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) * Note that for M profile we don't have a guest facing FSR, but * the env->exception.fsr will be populated by the code that * raises the fault, in the A profile short-descriptor format. + * + * Log the exception.vaddress now regardless of subtype, because + * logging below only logs it when it goes into a guest visible + * register. */ + qemu_log_mask(CPU_LOG_INT, "...at fault address 0x%x\n", + (uint32_t)env->exception.vaddress); switch (env->exception.fsr & 0xf) { case M_FAKE_FSR_NSC_EXEC: /* diff --git a/target/arm/neon_helper.c b/target/arm/neon_helper.c index 338b9189d5..bc6c4a54e9 100644 --- a/target/arm/neon_helper.c +++ b/target/arm/neon_helper.c @@ -23,7 +23,7 @@ typedef struct \ { \ type v1; \ } neon_##name; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define NEON_TYPE2(name, type) \ typedef struct \ { \ diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 70b42b55fd..2b87e8808b 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -691,19 +691,6 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, target_el = 3; syndrome = syn_uncategorized(); break; - case CP_ACCESS_TRAP_FP_EL2: - target_el = 2; - /* Since we are an implementation that takes exceptions on a trapped - * conditional insn only if the insn has passed its condition code - * check, we take the IMPDEF choice to always report CV=1 COND=0xe - * (which is also the required value for AArch64 traps). - */ - syndrome = syn_fp_access_trap(1, 0xe, false); - break; - case CP_ACCESS_TRAP_FP_EL3: - target_el = 3; - syndrome = syn_fp_access_trap(1, 0xe, false); - break; default: g_assert_not_reached(); } diff --git a/target/arm/pauth_helper.c b/target/arm/pauth_helper.c index cd6df18150..d0483bf051 100644 --- a/target/arm/pauth_helper.c +++ b/target/arm/pauth_helper.c @@ -382,15 +382,15 @@ static uint64_t pauth_strip(CPUARMState *env, uint64_t ptr, bool data) return pauth_original_ptr(ptr, param); } -static void QEMU_NORETURN pauth_trap(CPUARMState *env, int target_el, - uintptr_t ra) +static G_NORETURN +void pauth_trap(CPUARMState *env, int target_el, uintptr_t ra) { raise_exception_ra(env, EXCP_UDEF, syn_pactrap(), target_el, ra); } static void pauth_check_trap(CPUARMState *env, int el, uintptr_t ra) { - if (el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { + if (el < 2 && arm_is_el2_enabled(env)) { uint64_t hcr = arm_hcr_el2_eff(env); bool trap = !(hcr & HCR_API); if (el == 0) { diff --git a/target/arm/psci.c b/target/arm/psci.c index b279c0b9a4..6c1239bb96 100644 --- a/target/arm/psci.c +++ b/target/arm/psci.c @@ -57,7 +57,7 @@ void arm_handle_psci_call(ARMCPU *cpu) { /* * This function partially implements the logic for dispatching Power State - * Coordination Interface (PSCI) calls (as described in ARM DEN 0022B.b), + * Coordination Interface (PSCI) calls (as described in ARM DEN 0022D.b), * to the extent required for bringing up and taking down secondary cores, * and for handling reset and poweroff requests. * Additional information about the calling convention used is available in @@ -80,7 +80,7 @@ void arm_handle_psci_call(ARMCPU *cpu) } if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) { - ret = QEMU_PSCI_RET_INVALID_PARAMS; + ret = QEMU_PSCI_RET_NOT_SUPPORTED; goto err; } @@ -89,7 +89,7 @@ void arm_handle_psci_call(ARMCPU *cpu) ARMCPU *target_cpu; case QEMU_PSCI_0_2_FN_PSCI_VERSION: - ret = QEMU_PSCI_0_2_RET_VERSION_0_2; + ret = QEMU_PSCI_VERSION_1_1; break; case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */ @@ -170,6 +170,35 @@ void arm_handle_psci_call(ARMCPU *cpu) } helper_wfi(env, 4); break; + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + switch (param[1]) { + case QEMU_PSCI_0_2_FN_PSCI_VERSION: + case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: + case QEMU_PSCI_0_2_FN_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN64_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN_SYSTEM_RESET: + case QEMU_PSCI_0_2_FN_SYSTEM_OFF: + case QEMU_PSCI_0_1_FN_CPU_ON: + case QEMU_PSCI_0_2_FN_CPU_ON: + case QEMU_PSCI_0_2_FN64_CPU_ON: + case QEMU_PSCI_0_1_FN_CPU_OFF: + case QEMU_PSCI_0_2_FN_CPU_OFF: + case QEMU_PSCI_0_1_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN64_CPU_SUSPEND: + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + if (!(param[1] & QEMU_PSCI_0_2_64BIT) || is_a64(env)) { + ret = 0; + break; + } + /* fallthrough */ + case QEMU_PSCI_0_1_FN_MIGRATE: + case QEMU_PSCI_0_2_FN_MIGRATE: + default: + ret = QEMU_PSCI_RET_NOT_SUPPORTED; + break; + } + break; case QEMU_PSCI_0_1_FN_MIGRATE: case QEMU_PSCI_0_2_FN_MIGRATE: default: diff --git a/target/arm/sve.decode b/target/arm/sve.decode index c60b9f0fec..0388cce3bd 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -1575,10 +1575,9 @@ USDOT_zzzz 01000100 .. 0 ..... 011 110 ..... ..... @rda_rn_rm ### SVE2 Memory Gather Load Group -# SVE2 64-bit gather non-temporal load -# (scalar plus unpacked 32-bit unscaled offsets) +# SVE2 64-bit gather non-temporal load (scalar plus 64-bit unscaled offsets) LDNT1_zprz 1100010 msz:2 00 rm:5 1 u:1 0 pg:3 rn:5 rd:5 \ - &rprr_gather_load xs=0 esz=3 scale=0 ff=0 + &rprr_gather_load xs=2 esz=3 scale=0 ff=0 # SVE2 32-bit gather non-temporal load (scalar plus 32-bit unscaled offsets) LDNT1_zprz 1000010 msz:2 00 rm:5 10 u:1 pg:3 rn:5 rd:5 \ diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 07be55b7e1..e0f9aa9983 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2802,7 +2802,7 @@ static void swap_memmove(void *vd, void *vs, size_t n) uintptr_t o = (d | s | n) & 7; size_t i; -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN o = 0; #endif switch (o) { @@ -2864,7 +2864,7 @@ static void swap_memzero(void *vd, size_t n) return; } -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN o = 0; #endif switch (o) { @@ -6734,7 +6734,11 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, if (mtedesc && arm_tlb_mte_tagged(&info.attrs)) { mte_check(env, mtedesc, addr, retaddr); } - host_fn(&scratch, reg_off, info.host); + if (unlikely(info.flags & TLB_MMIO)) { + tlb_fn(env, &scratch, reg_off, addr, retaddr); + } else { + host_fn(&scratch, reg_off, info.host); + } } else { /* Element crosses the page boundary. */ sve_probe_page(&info2, false, env, addr + in_page, 0, @@ -7112,7 +7116,9 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, if (likely(in_page >= msize)) { sve_probe_page(&info, false, env, addr, 0, MMU_DATA_STORE, mmu_idx, retaddr); - host[i] = info.host; + if (!(info.flags & TLB_MMIO)) { + host[i] = info.host; + } } else { /* * Element crosses the page boundary. diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c index b79004e0cc..6421e16202 100644 --- a/target/arm/tlb_helper.c +++ b/target/arm/tlb_helper.c @@ -79,9 +79,10 @@ static uint32_t compute_fsr_fsc(CPUARMState *env, ARMMMUFaultInfo *fi, return fsr; } -static void QEMU_NORETURN arm_deliver_fault(ARMCPU *cpu, vaddr addr, - MMUAccessType access_type, - int mmu_idx, ARMMMUFaultInfo *fi) +static G_NORETURN +void arm_deliver_fault(ARMCPU *cpu, vaddr addr, + MMUAccessType access_type, + int mmu_idx, ARMMMUFaultInfo *fi) { CPUARMState *env = &cpu->env; int target_el; diff --git a/target/arm/translate-a32.h b/target/arm/translate-a32.h index 5be4b9b834..09010ad2da 100644 --- a/target/arm/translate-a32.h +++ b/target/arm/translate-a32.h @@ -61,17 +61,14 @@ static inline TCGv_i32 load_cpu_offset(int offset) #define load_cpu_field(name) load_cpu_offset(offsetof(CPUARMState, name)) -static inline void store_cpu_offset(TCGv_i32 var, int offset) -{ - tcg_gen_st_i32(var, cpu_env, offset); - tcg_temp_free_i32(var); -} +void store_cpu_offset(TCGv_i32 var, int offset, int size); -#define store_cpu_field(var, name) \ - store_cpu_offset(var, offsetof(CPUARMState, name)) +#define store_cpu_field(var, name) \ + store_cpu_offset(var, offsetof(CPUARMState, name), \ + sizeof_field(CPUARMState, name)) #define store_cpu_field_constant(val, name) \ - tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, offsetof(CPUARMState, name)) + store_cpu_field(tcg_constant_i32(val), name) /* Create a new temporary and set it to the value of a CPU register. */ static inline TCGv_i32 load_reg(DisasContext *s, int reg) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index ae3c41029b..cf2219375d 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -128,29 +128,28 @@ static int get_a64_user_mem_index(DisasContext *s) return arm_to_core_mmu_idx(useridx); } -static void reset_btype(DisasContext *s) +static void set_btype_raw(int val) { - if (s->btype != 0) { - TCGv_i32 zero = tcg_const_i32(0); - tcg_gen_st_i32(zero, cpu_env, offsetof(CPUARMState, btype)); - tcg_temp_free_i32(zero); - s->btype = 0; - } + tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, + offsetof(CPUARMState, btype)); } static void set_btype(DisasContext *s, int val) { - TCGv_i32 tcg_val; - /* BTYPE is a 2-bit field, and 0 should be done with reset_btype. */ tcg_debug_assert(val >= 1 && val <= 3); - - tcg_val = tcg_const_i32(val); - tcg_gen_st_i32(tcg_val, cpu_env, offsetof(CPUARMState, btype)); - tcg_temp_free_i32(tcg_val); + set_btype_raw(val); s->btype = -1; } +static void reset_btype(DisasContext *s) +{ + if (s->btype != 0) { + set_btype_raw(0); + s->btype = 0; + } +} + void gen_a64_set_pc_im(uint64_t val) { tcg_gen_movi_i64(cpu_pc, val); @@ -241,14 +240,10 @@ static void gen_address_with_allocation_tag0(TCGv_i64 dst, TCGv_i64 src) static void gen_probe_access(DisasContext *s, TCGv_i64 ptr, MMUAccessType acc, int log2_size) { - TCGv_i32 t_acc = tcg_const_i32(acc); - TCGv_i32 t_idx = tcg_const_i32(get_mem_index(s)); - TCGv_i32 t_size = tcg_const_i32(1 << log2_size); - - gen_helper_probe_access(cpu_env, ptr, t_acc, t_idx, t_size); - tcg_temp_free_i32(t_acc); - tcg_temp_free_i32(t_idx); - tcg_temp_free_i32(t_size); + gen_helper_probe_access(cpu_env, ptr, + tcg_constant_i32(acc), + tcg_constant_i32(get_mem_index(s)), + tcg_constant_i32(1 << log2_size)); } /* @@ -263,7 +258,6 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, int core_idx) { if (tag_checked && s->mte_active[is_unpriv]) { - TCGv_i32 tcg_desc; TCGv_i64 ret; int desc = 0; @@ -272,11 +266,9 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << log2_size) - 1); - tcg_desc = tcg_const_i32(desc); ret = new_tmp_a64(s); - gen_helper_mte_check(ret, cpu_env, tcg_desc, addr); - tcg_temp_free_i32(tcg_desc); + gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); return ret; } @@ -297,7 +289,6 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, bool tag_checked, int size) { if (tag_checked && s->mte_active[0]) { - TCGv_i32 tcg_desc; TCGv_i64 ret; int desc = 0; @@ -306,11 +297,9 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); desc = FIELD_DP32(desc, MTEDESC, SIZEM1, size - 1); - tcg_desc = tcg_const_i32(desc); ret = new_tmp_a64(s); - gen_helper_mte_check(ret, cpu_env, tcg_desc, addr); - tcg_temp_free_i32(tcg_desc); + gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); return ret; } @@ -342,13 +331,15 @@ static void a64_free_cc(DisasCompare64 *c64) tcg_temp_free_i64(c64->value); } +static void gen_rebuild_hflags(DisasContext *s) +{ + gen_helper_rebuild_hflags_a64(cpu_env, tcg_constant_i32(s->current_el)); +} + static void gen_exception_internal(int excp) { - TCGv_i32 tcg_excp = tcg_const_i32(excp); - assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_excp); - tcg_temp_free_i32(tcg_excp); + gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); } static void gen_exception_internal_insn(DisasContext *s, uint64_t pc, int excp) @@ -360,12 +351,8 @@ static void gen_exception_internal_insn(DisasContext *s, uint64_t pc, int excp) static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syndrome) { - TCGv_i32 tcg_syn; - gen_a64_set_pc_im(s->pc_curr); - tcg_syn = tcg_const_i32(syndrome); - gen_helper_exception_bkpt_insn(cpu_env, tcg_syn); - tcg_temp_free_i32(tcg_syn); + gen_helper_exception_bkpt_insn(cpu_env, tcg_constant_i32(syndrome)); s->base.is_jmp = DISAS_NORETURN; } @@ -827,15 +814,15 @@ static void gen_adc(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) { if (sf) { - TCGv_i64 result, cf_64, vf_64, tmp; - result = tcg_temp_new_i64(); - cf_64 = tcg_temp_new_i64(); - vf_64 = tcg_temp_new_i64(); - tmp = tcg_const_i64(0); + TCGv_i64 result = tcg_temp_new_i64(); + TCGv_i64 cf_64 = tcg_temp_new_i64(); + TCGv_i64 vf_64 = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_extu_i32_i64(cf_64, cpu_CF); - tcg_gen_add2_i64(result, cf_64, t0, tmp, cf_64, tmp); - tcg_gen_add2_i64(result, cf_64, result, cf_64, t1, tmp); + tcg_gen_add2_i64(result, cf_64, t0, zero, cf_64, zero); + tcg_gen_add2_i64(result, cf_64, result, cf_64, t1, zero); tcg_gen_extrl_i64_i32(cpu_CF, cf_64); gen_set_NZ64(result); @@ -851,15 +838,15 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_temp_free_i64(cf_64); tcg_temp_free_i64(result); } else { - TCGv_i32 t0_32, t1_32, tmp; - t0_32 = tcg_temp_new_i32(); - t1_32 = tcg_temp_new_i32(); - tmp = tcg_const_i32(0); + TCGv_i32 t0_32 = tcg_temp_new_i32(); + TCGv_i32 t1_32 = tcg_temp_new_i32(); + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_extrl_i64_i32(t0_32, t0); tcg_gen_extrl_i64_i32(t1_32, t1); - tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, cpu_CF, tmp); - tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1_32, tmp); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, zero, cpu_CF, zero); + tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1_32, zero); tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); @@ -1628,7 +1615,6 @@ static void gen_axflag(void) static void handle_msr_i(DisasContext *s, uint32_t insn, unsigned int op1, unsigned int op2, unsigned int crm) { - TCGv_i32 t1; int op = op1 << 3 | op2; /* End the TB by default, chaining is ok. */ @@ -1668,9 +1654,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, } else { clear_pstate_bits(PSTATE_UAO); } - t1 = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_rebuild_hflags(s); break; case 0x04: /* PAN */ @@ -1682,18 +1666,14 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, } else { clear_pstate_bits(PSTATE_PAN); } - t1 = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_rebuild_hflags(s); break; case 0x05: /* SPSel */ if (s->current_el == 0) { goto do_unallocated; } - t1 = tcg_const_i32(crm & PSTATE_SP); - gen_helper_msr_i_spsel(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_helper_msr_i_spsel(cpu_env, tcg_constant_i32(crm & PSTATE_SP)); break; case 0x19: /* SSBS */ @@ -1721,15 +1701,11 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, break; case 0x1e: /* DAIFSet */ - t1 = tcg_const_i32(crm); - gen_helper_msr_i_daifset(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_helper_msr_i_daifset(cpu_env, tcg_constant_i32(crm)); break; case 0x1f: /* DAIFClear */ - t1 = tcg_const_i32(crm); - gen_helper_msr_i_daifclear(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_helper_msr_i_daifclear(cpu_env, tcg_constant_i32(crm)); /* For DAIFClear, exit the cpu loop to re-evaluate pending IRQs. */ s->base.is_jmp = DISAS_UPDATE_EXIT; break; @@ -1742,9 +1718,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, } else { clear_pstate_bits(PSTATE_TCO); } - t1 = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_rebuild_hflags(s); /* Many factors, including TCO, go into MTE_ACTIVE. */ s->base.is_jmp = DISAS_UPDATE_NOCHAIN; } else if (dc_isar_feature(aa64_mte_insn_reg, s)) { @@ -1844,19 +1818,14 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. */ - TCGv_ptr tmpptr; - TCGv_i32 tcg_syn, tcg_isread; uint32_t syndrome; - gen_a64_set_pc_im(s->pc_curr); - tmpptr = tcg_const_ptr(ri); syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); - tcg_syn = tcg_const_i32(syndrome); - tcg_isread = tcg_const_i32(isread); - gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn, tcg_isread); - tcg_temp_free_ptr(tmpptr); - tcg_temp_free_i32(tcg_syn); - tcg_temp_free_i32(tcg_isread); + gen_a64_set_pc_im(s->pc_curr); + gen_helper_access_check_cp_reg(cpu_env, + tcg_constant_ptr(ri), + tcg_constant_i32(syndrome), + tcg_constant_i32(isread)); } else if (ri->type & ARM_CP_RAISES_EXC) { /* * The readfn or writefn might raise an exception; @@ -1887,17 +1856,15 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, case ARM_CP_DC_ZVA: /* Writes clear the aligned block of memory which rt points into. */ if (s->mte_active[0]) { - TCGv_i32 t_desc; int desc = 0; desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); - t_desc = tcg_const_i32(desc); tcg_rt = new_tmp_a64(s); - gen_helper_mte_check_zva(tcg_rt, cpu_env, t_desc, cpu_reg(s, rt)); - tcg_temp_free_i32(t_desc); + gen_helper_mte_check_zva(tcg_rt, cpu_env, + tcg_constant_i32(desc), cpu_reg(s, rt)); } else { tcg_rt = clean_data_tbi(s, cpu_reg(s, rt)); } @@ -1961,10 +1928,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, if (ri->type & ARM_CP_CONST) { tcg_gen_movi_i64(tcg_rt, ri->resetvalue); } else if (ri->readfn) { - TCGv_ptr tmpptr; - tmpptr = tcg_const_ptr(ri); - gen_helper_get_cp_reg64(tcg_rt, cpu_env, tmpptr); - tcg_temp_free_ptr(tmpptr); + gen_helper_get_cp_reg64(tcg_rt, cpu_env, tcg_constant_ptr(ri)); } else { tcg_gen_ld_i64(tcg_rt, cpu_env, ri->fieldoffset); } @@ -1973,10 +1937,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, /* If not forbidden by access permissions, treat as WI */ return; } else if (ri->writefn) { - TCGv_ptr tmpptr; - tmpptr = tcg_const_ptr(ri); - gen_helper_set_cp_reg64(cpu_env, tmpptr, tcg_rt); - tcg_temp_free_ptr(tmpptr); + gen_helper_set_cp_reg64(cpu_env, tcg_constant_ptr(ri), tcg_rt); } else { tcg_gen_st_i64(tcg_rt, cpu_env, ri->fieldoffset); } @@ -1991,9 +1952,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, * A write to any coprocessor regiser that ends a TB * must rebuild the hflags for the next TB. */ - TCGv_i32 tcg_el = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, tcg_el); - tcg_temp_free_i32(tcg_el); + gen_rebuild_hflags(s); /* * We default to ending the TB on a coprocessor register write, * but allow this to be suppressed by the register definition @@ -2056,7 +2015,6 @@ static void disas_exc(DisasContext *s, uint32_t insn) int opc = extract32(insn, 21, 3); int op2_ll = extract32(insn, 0, 5); int imm16 = extract32(insn, 5, 16); - TCGv_i32 tmp; switch (opc) { case 0: @@ -2091,9 +2049,7 @@ static void disas_exc(DisasContext *s, uint32_t insn) break; } gen_a64_set_pc_im(s->pc_curr); - tmp = tcg_const_i32(syn_aa64_smc(imm16)); - gen_helper_pre_smc(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa64_smc(imm16))); gen_ss_advance(s); gen_exception_insn(s, s->base.pc_next, EXCP_SMC, syn_aa64_smc(imm16), 3); @@ -2470,7 +2426,12 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { if (!HAVE_CMPXCHG128) { gen_helper_exit_atomic(cpu_env); - s->base.is_jmp = DISAS_NORETURN; + /* + * Produce a result so we have a well-formed opcode + * stream when the following (dead) code uses 'tmp'. + * TCG will remove the dead ops for us. + */ + tcg_gen_movi_i64(tmp, 0); } else if (s->be_data == MO_LE) { gen_helper_paired_cmpxchg64_le_parallel(tmp, cpu_env, cpu_exclusive_addr, @@ -2562,7 +2523,7 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, tcg_temp_free_i64(cmp); } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { if (HAVE_CMPXCHG128) { - TCGv_i32 tcg_rs = tcg_const_i32(rs); + TCGv_i32 tcg_rs = tcg_constant_i32(rs); if (s->be_data == MO_LE) { gen_helper_casp_le_parallel(cpu_env, tcg_rs, clean_addr, t1, t2); @@ -2570,7 +2531,6 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, gen_helper_casp_be_parallel(cpu_env, tcg_rs, clean_addr, t1, t2); } - tcg_temp_free_i32(tcg_rs); } else { gen_helper_exit_atomic(cpu_env); s->base.is_jmp = DISAS_NORETURN; @@ -2581,7 +2541,7 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, TCGv_i64 a2 = tcg_temp_new_i64(); TCGv_i64 c1 = tcg_temp_new_i64(); TCGv_i64 c2 = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); /* Load the two words, in memory order. */ tcg_gen_qemu_ld_i64(d1, clean_addr, memidx, @@ -2602,7 +2562,6 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, tcg_temp_free_i64(a2); tcg_temp_free_i64(c1); tcg_temp_free_i64(c2); - tcg_temp_free_i64(zero); /* Write back the data from memory to Rs. */ tcg_gen_mov_i64(s1, d1); @@ -2819,7 +2778,7 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) tcg_rt = cpu_reg(s, rt); - clean_addr = tcg_const_i64(s->pc_curr + imm); + clean_addr = tcg_constant_i64(s->pc_curr + imm); if (is_vector) { do_fp_ld(s, rt, clean_addr, size); } else { @@ -2829,7 +2788,6 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, false, true, rt, iss_sf, false); } - tcg_temp_free_i64(clean_addr); } /* @@ -3735,7 +3693,7 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) mop = endian | size | align; elements = (is_q ? 16 : 8) >> size; - tcg_ebytes = tcg_const_i64(1 << size); + tcg_ebytes = tcg_constant_i64(1 << size); for (r = 0; r < rpt; r++) { int e; for (e = 0; e < elements; e++) { @@ -3751,7 +3709,6 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) } } } - tcg_temp_free_i64(tcg_ebytes); if (!is_store) { /* For non-quad operations, setting a slice of the low @@ -3881,7 +3838,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) total); mop = finalize_memop(s, scale); - tcg_ebytes = tcg_const_i64(1 << scale); + tcg_ebytes = tcg_constant_i64(1 << scale); for (xs = 0; xs < selem; xs++) { if (replicate) { /* Load and replicate to all elements */ @@ -3903,7 +3860,6 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); rt = (rt + 1) % 32; } - tcg_temp_free_i64(tcg_ebytes); if (is_postidx) { if (rm == 31) { @@ -4094,7 +4050,7 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) if (is_zero) { TCGv_i64 clean_addr = clean_data_tbi(s, addr); - TCGv_i64 tcg_zero = tcg_const_i64(0); + TCGv_i64 tcg_zero = tcg_constant_i64(0); int mem_index = get_mem_index(s); int i, n = (1 + is_pair) << LOG2_TAG_GRANULE; @@ -4104,7 +4060,6 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) tcg_gen_addi_i64(clean_addr, clean_addr, 8); tcg_gen_qemu_st_i64(tcg_zero, clean_addr, mem_index, MO_UQ); } - tcg_temp_free_i64(tcg_zero); } if (index != 0) { @@ -4239,13 +4194,12 @@ static void disas_add_sub_imm(DisasContext *s, uint32_t insn) tcg_gen_addi_i64(tcg_result, tcg_rn, imm); } } else { - TCGv_i64 tcg_imm = tcg_const_i64(imm); + TCGv_i64 tcg_imm = tcg_constant_i64(imm); if (sub_op) { gen_sub_CC(is_64bit, tcg_result, tcg_rn, tcg_imm); } else { gen_add_CC(is_64bit, tcg_result, tcg_rn, tcg_imm); } - tcg_temp_free_i64(tcg_imm); } if (is_64bit) { @@ -4293,12 +4247,9 @@ static void disas_add_sub_imm_with_tags(DisasContext *s, uint32_t insn) tcg_rd = cpu_reg_sp(s, rd); if (s->ata) { - TCGv_i32 offset = tcg_const_i32(imm); - TCGv_i32 tag_offset = tcg_const_i32(uimm4); - - gen_helper_addsubg(tcg_rd, cpu_env, tcg_rn, offset, tag_offset); - tcg_temp_free_i32(tag_offset); - tcg_temp_free_i32(offset); + gen_helper_addsubg(tcg_rd, cpu_env, tcg_rn, + tcg_constant_i32(imm), + tcg_constant_i32(uimm4)); } else { tcg_gen_addi_i64(tcg_rd, tcg_rn, imm); gen_address_with_allocation_tag0(tcg_rd, tcg_rd); @@ -4484,7 +4435,6 @@ static void disas_movw_imm(DisasContext *s, uint32_t insn) int opc = extract32(insn, 29, 2); int pos = extract32(insn, 21, 2) << 4; TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_imm; if (!sf && (pos >= 32)) { unallocated_encoding(s); @@ -4504,9 +4454,7 @@ static void disas_movw_imm(DisasContext *s, uint32_t insn) tcg_gen_movi_i64(tcg_rd, imm); break; case 3: /* MOVK */ - tcg_imm = tcg_const_i64(imm); - tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_imm, pos, 16); - tcg_temp_free_i64(tcg_imm); + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_constant_i64(imm), pos, 16); if (!sf) { tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } @@ -4746,11 +4694,7 @@ static void shift_reg_imm(TCGv_i64 dst, TCGv_i64 src, int sf, if (shift_i == 0) { tcg_gen_mov_i64(dst, src); } else { - TCGv_i64 shift_const; - - shift_const = tcg_const_i64(shift_i); - shift_reg(dst, src, sf, shift_type, shift_const); - tcg_temp_free_i64(shift_const); + shift_reg(dst, src, sf, shift_type, tcg_constant_i64(shift_i)); } } @@ -5347,7 +5291,7 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) tcg_rd = cpu_reg(s, rd); a64_test_cc(&c, cond); - zero = tcg_const_i64(0); + zero = tcg_constant_i64(0); if (rn == 31 && rm == 31 && (else_inc ^ else_inv)) { /* CSET & CSETM. */ @@ -5368,7 +5312,6 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) tcg_gen_movcond_i64(c.cond, tcg_rd, c.value, zero, t_true, t_false); } - tcg_temp_free_i64(zero); a64_free_cc(&c); if (!sf) { @@ -5465,7 +5408,7 @@ static void handle_rev16(DisasContext *s, unsigned int sf, TCGv_i64 tcg_rd = cpu_reg(s, rd); TCGv_i64 tcg_tmp = tcg_temp_new_i64(); TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); - TCGv_i64 mask = tcg_const_i64(sf ? 0x00ff00ff00ff00ffull : 0x00ff00ff); + TCGv_i64 mask = tcg_constant_i64(sf ? 0x00ff00ff00ff00ffull : 0x00ff00ff); tcg_gen_shri_i64(tcg_tmp, tcg_rn, 8); tcg_gen_and_i64(tcg_rd, tcg_rn, mask); @@ -5473,7 +5416,6 @@ static void handle_rev16(DisasContext *s, unsigned int sf, tcg_gen_shli_i64(tcg_rd, tcg_rd, 8); tcg_gen_or_i64(tcg_rd, tcg_rd, tcg_tmp); - tcg_temp_free_i64(mask); tcg_temp_free_i64(tcg_tmp); } @@ -5756,15 +5698,13 @@ static void handle_crc32(DisasContext *s, } tcg_acc = cpu_reg(s, rn); - tcg_bytes = tcg_const_i32(1 << sz); + tcg_bytes = tcg_constant_i32(1 << sz); if (crc32c) { gen_helper_crc32c_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); } else { gen_helper_crc32_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); } - - tcg_temp_free_i32(tcg_bytes); } /* Data-processing (2 source) @@ -5830,15 +5770,13 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { goto do_unallocated; } else { - TCGv_i64 t1 = tcg_const_i64(1); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_extract_i64(t2, cpu_reg_sp(s, rn), 56, 4); - tcg_gen_shl_i64(t1, t1, t2); - tcg_gen_or_i64(cpu_reg(s, rd), cpu_reg(s, rm), t1); + tcg_gen_extract_i64(t, cpu_reg_sp(s, rn), 56, 4); + tcg_gen_shl_i64(t, tcg_constant_i64(1), t); + tcg_gen_or_i64(cpu_reg(s, rd), cpu_reg(s, rm), t); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); + tcg_temp_free_i64(t); } break; case 8: /* LSLV */ @@ -5973,7 +5911,7 @@ static void handle_fp_compare(DisasContext *s, int size, tcg_vn = read_fp_dreg(s, rn); if (cmp_with_zero) { - tcg_vm = tcg_const_i64(0); + tcg_vm = tcg_constant_i64(0); } else { tcg_vm = read_fp_dreg(s, rm); } @@ -6083,7 +6021,6 @@ static void disas_fp_compare(DisasContext *s, uint32_t insn) static void disas_fp_ccomp(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, cond, rn, op, nzcv; - TCGv_i64 tcg_flags; TCGLabel *label_continue = NULL; int size; @@ -6127,9 +6064,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) label_continue = gen_new_label(); arm_gen_test_cc(cond, label_match); /* nomatch: */ - tcg_flags = tcg_const_i64(nzcv << 28); - gen_set_nzcv(tcg_flags); - tcg_temp_free_i64(tcg_flags); + gen_set_nzcv(tcg_constant_i64(nzcv << 28)); tcg_gen_br(label_continue); gen_set_label(label_match); } @@ -6150,7 +6085,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) static void disas_fp_csel(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, cond, rn, rd; - TCGv_i64 t_true, t_false, t_zero; + TCGv_i64 t_true, t_false; DisasCompare64 c; MemOp sz; @@ -6195,9 +6130,8 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) read_vec_element(s, t_false, rm, 0, sz); a64_test_cc(&c, cond); - t_zero = tcg_const_i64(0); - tcg_gen_movcond_i64(c.cond, t_true, c.value, t_zero, t_true, t_false); - tcg_temp_free_i64(t_zero); + tcg_gen_movcond_i64(c.cond, t_true, c.value, tcg_constant_i64(0), + t_true, t_false); tcg_temp_free_i64(t_false); a64_free_cc(&c); @@ -6979,7 +6913,6 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) int type = extract32(insn, 22, 2); int mos = extract32(insn, 29, 3); uint64_t imm; - TCGv_i64 tcg_res; MemOp sz; if (mos || imm5) { @@ -7010,10 +6943,7 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) } imm = vfp_expand_imm(sz, imm8); - - tcg_res = tcg_const_i64(imm); - write_fp_dreg(s, rd, tcg_res); - tcg_temp_free_i64(tcg_res); + write_fp_dreg(s, rd, tcg_constant_i64(imm)); } /* Handle floating point <=> fixed point conversions. Note that we can @@ -7031,7 +6961,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_fpstatus = fpstatus_ptr(type == 3 ? FPST_FPCR_F16 : FPST_FPCR); - tcg_shift = tcg_const_i32(64 - scale); + tcg_shift = tcg_constant_i32(64 - scale); if (itof) { TCGv_i64 tcg_int = cpu_reg(s, rn); @@ -7190,7 +7120,6 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, } tcg_temp_free_ptr(tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); } /* Floating point <-> fixed point conversions @@ -8461,7 +8390,7 @@ static void handle_shri_with_rndacc(TCGv_i64 tcg_res, TCGv_i64 tcg_src, /* Deal with the rounding step */ if (round) { if (extended_result) { - TCGv_i64 tcg_zero = tcg_const_i64(0); + TCGv_i64 tcg_zero = tcg_constant_i64(0); if (!is_u) { /* take care of sign extending tcg_res */ tcg_gen_sari_i64(tcg_src_hi, tcg_src, 63); @@ -8473,7 +8402,6 @@ static void handle_shri_with_rndacc(TCGv_i64 tcg_res, TCGv_i64 tcg_src, tcg_src, tcg_zero, tcg_rnd, tcg_zero); } - tcg_temp_free_i64(tcg_zero); } else { tcg_gen_add_i64(tcg_src, tcg_src, tcg_rnd); } @@ -8559,8 +8487,7 @@ static void handle_scalar_simd_shri(DisasContext *s, } if (round) { - uint64_t round_const = 1ULL << (shift - 1); - tcg_round = tcg_const_i64(round_const); + tcg_round = tcg_constant_i64(1ULL << (shift - 1)); } else { tcg_round = NULL; } @@ -8586,9 +8513,6 @@ static void handle_scalar_simd_shri(DisasContext *s, tcg_temp_free_i64(tcg_rn); tcg_temp_free_i64(tcg_rd); - if (round) { - tcg_temp_free_i64(tcg_round); - } } /* SHL/SLI - Scalar shift left */ @@ -8686,8 +8610,7 @@ static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, tcg_final = tcg_const_i64(0); if (round) { - uint64_t round_const = 1ULL << (shift - 1); - tcg_round = tcg_const_i64(round_const); + tcg_round = tcg_constant_i64(1ULL << (shift - 1)); } else { tcg_round = NULL; } @@ -8707,9 +8630,6 @@ static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, write_vec_element(s, tcg_final, rd, 1, MO_64); } - if (round) { - tcg_temp_free_i64(tcg_round); - } tcg_temp_free_i64(tcg_rn); tcg_temp_free_i64(tcg_rd); tcg_temp_free_i32(tcg_rd_narrowed); @@ -8761,7 +8681,7 @@ static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, } if (size == 3) { - TCGv_i64 tcg_shift = tcg_const_i64(shift); + TCGv_i64 tcg_shift = tcg_constant_i64(shift); static NeonGenTwo64OpEnvFn * const fns[2][2] = { { gen_helper_neon_qshl_s64, gen_helper_neon_qshlu_s64 }, { NULL, gen_helper_neon_qshl_u64 }, @@ -8778,10 +8698,9 @@ static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, tcg_temp_free_i64(tcg_op); } - tcg_temp_free_i64(tcg_shift); clear_vec_high(s, is_q, rd); } else { - TCGv_i32 tcg_shift = tcg_const_i32(shift); + TCGv_i32 tcg_shift = tcg_constant_i32(shift); static NeonGenTwoOpEnvFn * const fns[2][2][3] = { { { gen_helper_neon_qshl_s8, @@ -8826,7 +8745,6 @@ static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, tcg_temp_free_i32(tcg_op); } - tcg_temp_free_i32(tcg_shift); if (!scalar) { clear_vec_high(s, is_q, rd); @@ -8846,7 +8764,7 @@ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int pass; if (fracbits || size == MO_64) { - tcg_shift = tcg_const_i32(fracbits); + tcg_shift = tcg_constant_i32(fracbits); } if (size == MO_64) { @@ -8931,9 +8849,6 @@ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, } tcg_temp_free_ptr(tcg_fpst); - if (tcg_shift) { - tcg_temp_free_i32(tcg_shift); - } clear_vec_high(s, elements << size == 16, rd); } @@ -9023,7 +8938,7 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, tcg_fpstatus = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); fracbits = (16 << size) - immhb; - tcg_shift = tcg_const_i32(fracbits); + tcg_shift = tcg_constant_i32(fracbits); if (size == MO_64) { int maxpass = is_scalar ? 1 : 2; @@ -9081,9 +8996,8 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, } } - tcg_temp_free_ptr(tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + tcg_temp_free_ptr(tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); } @@ -9953,23 +9867,15 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0x1c: /* FCVTAS */ case 0x3a: /* FCVTPS */ case 0x3b: /* FCVTZS */ - { - TCGv_i32 tcg_shift = tcg_const_i32(0); - gen_helper_vfp_tosqd(tcg_rd, tcg_rn, tcg_shift, tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); + gen_helper_vfp_tosqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); break; - } case 0x5a: /* FCVTNU */ case 0x5b: /* FCVTMU */ case 0x5c: /* FCVTAU */ case 0x7a: /* FCVTPU */ case 0x7b: /* FCVTZU */ - { - TCGv_i32 tcg_shift = tcg_const_i32(0); - gen_helper_vfp_touqd(tcg_rd, tcg_rn, tcg_shift, tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); + gen_helper_vfp_touqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); break; - } case 0x18: /* FRINTN */ case 0x19: /* FRINTM */ case 0x38: /* FRINTP */ @@ -10009,7 +9915,7 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, if (is_double) { TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_zero = tcg_const_i64(0); + TCGv_i64 tcg_zero = tcg_constant_i64(0); TCGv_i64 tcg_res = tcg_temp_new_i64(); NeonGenTwoDoubleOpFn *genfn; bool swap = false; @@ -10045,13 +9951,12 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, write_vec_element(s, tcg_res, rd, pass, MO_64); } tcg_temp_free_i64(tcg_res); - tcg_temp_free_i64(tcg_zero); tcg_temp_free_i64(tcg_op); clear_vec_high(s, !is_scalar, rd); } else { TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_zero = tcg_const_i32(0); + TCGv_i32 tcg_zero = tcg_constant_i32(0); TCGv_i32 tcg_res = tcg_temp_new_i32(); NeonGenTwoSingleOpFn *genfn; bool swap = false; @@ -10120,7 +10025,6 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, } } tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_zero); tcg_temp_free_i32(tcg_op); if (!is_scalar) { clear_vec_high(s, is_q, rd); @@ -10221,7 +10125,7 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, int passes = scalar ? 1 : 2; if (scalar) { - tcg_res[1] = tcg_const_i32(0); + tcg_res[1] = tcg_constant_i32(0); } for (pass = 0; pass < passes; pass++) { @@ -10399,9 +10303,7 @@ static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, } if (is_scalar) { - TCGv_i64 tcg_zero = tcg_const_i64(0); - write_vec_element(s, tcg_zero, rd, 0, MO_64); - tcg_temp_free_i64(tcg_zero); + write_vec_element(s, tcg_constant_i64(0), rd, 0, MO_64); } write_vec_element_i32(s, tcg_rd, rd, pass, MO_32); } @@ -10584,23 +10486,17 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) case 0x1c: /* FCVTAS */ case 0x3a: /* FCVTPS */ case 0x3b: /* FCVTZS */ - { - TCGv_i32 tcg_shift = tcg_const_i32(0); - gen_helper_vfp_tosls(tcg_rd, tcg_rn, tcg_shift, tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); + gen_helper_vfp_tosls(tcg_rd, tcg_rn, tcg_constant_i32(0), + tcg_fpstatus); break; - } case 0x5a: /* FCVTNU */ case 0x5b: /* FCVTMU */ case 0x5c: /* FCVTAU */ case 0x7a: /* FCVTPU */ case 0x7b: /* FCVTZU */ - { - TCGv_i32 tcg_shift = tcg_const_i32(0); - gen_helper_vfp_touls(tcg_rd, tcg_rn, tcg_shift, tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); + gen_helper_vfp_touls(tcg_rd, tcg_rn, tcg_constant_i32(0), + tcg_fpstatus); break; - } default: g_assert_not_reached(); } @@ -10772,8 +10668,7 @@ static void handle_vec_simd_shrn(DisasContext *s, bool is_q, read_vec_element(s, tcg_final, rd, is_q ? 1 : 0, MO_64); if (round) { - uint64_t round_const = 1ULL << (shift - 1); - tcg_round = tcg_const_i64(round_const); + tcg_round = tcg_constant_i64(1ULL << (shift - 1)); } else { tcg_round = NULL; } @@ -10791,9 +10686,6 @@ static void handle_vec_simd_shrn(DisasContext *s, bool is_q, } else { write_vec_element(s, tcg_final, rd, 1, MO_64); } - if (round) { - tcg_temp_free_i64(tcg_round); - } tcg_temp_free_i64(tcg_rn); tcg_temp_free_i64(tcg_rd); tcg_temp_free_i64(tcg_final); @@ -12497,7 +12389,7 @@ static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, } } if (!is_q) { - tcg_res[1] = tcg_const_i64(0); + tcg_res[1] = tcg_constant_i64(0); } for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], rd, pass, MO_64); @@ -12930,25 +12822,17 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x1c: /* FCVTAS */ case 0x3a: /* FCVTPS */ case 0x3b: /* FCVTZS */ - { - TCGv_i32 tcg_shift = tcg_const_i32(0); gen_helper_vfp_tosls(tcg_res, tcg_op, - tcg_shift, tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); + tcg_constant_i32(0), tcg_fpstatus); break; - } case 0x5a: /* FCVTNU */ case 0x5b: /* FCVTMU */ case 0x5c: /* FCVTAU */ case 0x7a: /* FCVTPU */ case 0x7b: /* FCVTZU */ - { - TCGv_i32 tcg_shift = tcg_const_i32(0); gen_helper_vfp_touls(tcg_res, tcg_op, - tcg_shift, tcg_fpstatus); - tcg_temp_free_i32(tcg_shift); + tcg_constant_i32(0), tcg_fpstatus); break; - } case 0x18: /* FRINTN */ case 0x19: /* FRINTM */ case 0x38: /* FRINTP */ @@ -14046,7 +13930,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } if (is_scalar) { - tcg_res[1] = tcg_const_i64(0); + tcg_res[1] = tcg_constant_i64(0); } for (pass = 0; pass < 2; pass++) { @@ -14450,7 +14334,7 @@ static void disas_crypto_four_reg(DisasContext *s, uint32_t insn) tcg_op2 = tcg_temp_new_i32(); tcg_op3 = tcg_temp_new_i32(); tcg_res = tcg_temp_new_i32(); - tcg_zero = tcg_const_i32(0); + tcg_zero = tcg_constant_i32(0); read_vec_element_i32(s, tcg_op1, rn, 3, MO_32); read_vec_element_i32(s, tcg_op2, rm, 3, MO_32); @@ -14470,7 +14354,6 @@ static void disas_crypto_four_reg(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tcg_op2); tcg_temp_free_i32(tcg_op3); tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_zero); } } @@ -14695,13 +14578,13 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->isar = &arm_cpu->isar; dc->condjmp = 0; - dc->aarch64 = 1; + dc->aarch64 = true; /* If we are coming from secure EL0 in a system with a 32-bit EL3, then * there is no secure EL1, so we route exceptions to EL3. */ dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); - dc->thumb = 0; + dc->thumb = false; dc->sctlr_b = 0; dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; dc->condexec_mask = 0; @@ -14978,32 +14861,29 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_helper_yield(cpu_env); break; case DISAS_WFI: - { - /* This is a special case because we don't want to just halt the CPU - * if trying to debug across a WFI. + /* + * This is a special case because we don't want to just halt + * the CPU if trying to debug across a WFI. */ - TCGv_i32 tmp = tcg_const_i32(4); - gen_a64_set_pc_im(dc->base.pc_next); - gen_helper_wfi(cpu_env, tmp); - tcg_temp_free_i32(tmp); - /* The helper doesn't necessarily throw an exception, but we + gen_helper_wfi(cpu_env, tcg_constant_i32(4)); + /* + * The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. */ tcg_gen_exit_tb(NULL, 0); break; } - } } } static void aarch64_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu) + CPUState *cpu, FILE *logfile) { DisasContext *dc = container_of(dcbase, DisasContext, base); - qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); + target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); } const TranslatorOps aarch64_translator_ops = { diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h index 58f50abca4..38884158aa 100644 --- a/target/arm/translate-a64.h +++ b/target/arm/translate-a64.h @@ -71,7 +71,7 @@ static inline int vec_reg_offset(DisasContext *s, int regno, { int element_size = 1 << size; int offs = element * element_size; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN /* This is complicated slightly because vfp.zregs[n].d[0] is * still the lowest and vfp.zregs[n].d[15] the highest of the * 256 byte vector, even on big endian systems. diff --git a/target/arm/translate-m-nocp.c b/target/arm/translate-m-nocp.c index d9e144e8eb..27363a7b4e 100644 --- a/target/arm/translate-m-nocp.c +++ b/target/arm/translate-m-nocp.c @@ -173,7 +173,7 @@ static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a) } /* Zero the Sregs from btmreg to topreg inclusive. */ - zero = tcg_const_i64(0); + zero = tcg_constant_i64(0); if (btmreg & 1) { write_neon_element64(zero, btmreg >> 1, 1, MO_32); btmreg++; @@ -187,8 +187,7 @@ static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a) } assert(btmreg == topreg + 1); if (dc_isar_feature(aa32_mve, s)) { - TCGv_i32 z32 = tcg_const_i32(0); - store_cpu_field(z32, v7m.vpr); + store_cpu_field(tcg_constant_i32(0), v7m.vpr); } clear_eci_state(s); @@ -512,7 +511,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, } case ARM_VFP_FPCXT_NS: { - TCGv_i32 control, sfpa, fpscr, fpdscr, zero; + TCGv_i32 control, sfpa, fpscr, fpdscr; TCGLabel *lab_active = gen_new_label(); lookup_tb = true; @@ -552,10 +551,9 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, storefn(s, opaque, tmp, true); /* If SFPA is zero then set FPSCR from FPDSCR_NS */ fpdscr = load_cpu_field(v7m.fpdscr[M_REG_NS]); - zero = tcg_const_i32(0); - tcg_gen_movcond_i32(TCG_COND_EQ, fpscr, sfpa, zero, fpdscr, fpscr); + tcg_gen_movcond_i32(TCG_COND_EQ, fpscr, sfpa, tcg_constant_i32(0), + fpdscr, fpscr); gen_helper_vfp_set_fpscr(cpu_env, fpscr); - tcg_temp_free_i32(zero); tcg_temp_free_i32(sfpa); tcg_temp_free_i32(fpdscr); tcg_temp_free_i32(fpscr); diff --git a/target/arm/translate-neon.c b/target/arm/translate-neon.c index 3854dd3516..2e4d1ec87d 100644 --- a/target/arm/translate-neon.c +++ b/target/arm/translate-neon.c @@ -447,7 +447,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) int mmu_idx = get_mem_index(s); int size = a->size; TCGv_i64 tmp64; - TCGv_i32 addr, tmp; + TCGv_i32 addr; if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { return false; @@ -513,7 +513,6 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) tmp64 = tcg_temp_new_i64(); addr = tcg_temp_new_i32(); - tmp = tcg_const_i32(1 << size); load_reg_var(s, addr, a->rn); mop = endian | size | align; @@ -530,7 +529,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) neon_load_element64(tmp64, tt, n, size); gen_aa32_st_internal_i64(s, tmp64, addr, mmu_idx, mop); } - tcg_gen_add_i32(addr, addr, tmp); + tcg_gen_addi_i32(addr, addr, 1 << size); /* Subsequent memory operations inherit alignment */ mop &= ~MO_AMASK; @@ -538,7 +537,6 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) } } tcg_temp_free_i32(addr); - tcg_temp_free_i32(tmp); tcg_temp_free_i64(tmp64); gen_neon_ldst_base_update(s, a->rm, a->rn, nregs * interleave * 8); @@ -657,21 +655,24 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a) /* Catch the UNDEF cases. This is unavoidably a bit messy. */ switch (nregs) { case 1: + if (a->stride != 1) { + return false; + } if (((a->align & (1 << a->size)) != 0) || (a->size == 2 && (a->align == 1 || a->align == 2))) { return false; } break; - case 3: - if ((a->align & 1) != 0) { - return false; - } - /* fall through */ case 2: if (a->size == 2 && (a->align & 2) != 0) { return false; } break; + case 3: + if (a->align != 0) { + return false; + } + break; case 4: if (a->size == 2 && a->align == 3) { return false; @@ -1345,7 +1346,7 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a, * To avoid excessive duplication of ops we implement shift * by immediate using the variable shift operations. */ - constimm = tcg_const_i64(dup_const(a->size, a->shift)); + constimm = tcg_constant_i64(dup_const(a->size, a->shift)); for (pass = 0; pass < a->q + 1; pass++) { TCGv_i64 tmp = tcg_temp_new_i64(); @@ -1355,7 +1356,6 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a, write_neon_element64(tmp, a->vd, pass, MO_64); tcg_temp_free_i64(tmp); } - tcg_temp_free_i64(constimm); return true; } @@ -1391,7 +1391,7 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a, * To avoid excessive duplication of ops we implement shift * by immediate using the variable shift operations. */ - constimm = tcg_const_i32(dup_const(a->size, a->shift)); + constimm = tcg_constant_i32(dup_const(a->size, a->shift)); tmp = tcg_temp_new_i32(); for (pass = 0; pass < (a->q ? 4 : 2); pass++) { @@ -1400,7 +1400,6 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a, write_neon_element32(tmp, a->vd, pass, MO_32); } tcg_temp_free_i32(tmp); - tcg_temp_free_i32(constimm); return true; } @@ -1454,7 +1453,7 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, * This is always a right shift, and the shiftfn is always a * left-shift helper, which thus needs the negated shift count. */ - constimm = tcg_const_i64(-a->shift); + constimm = tcg_constant_i64(-a->shift); rm1 = tcg_temp_new_i64(); rm2 = tcg_temp_new_i64(); rd = tcg_temp_new_i32(); @@ -1474,7 +1473,6 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, tcg_temp_free_i32(rd); tcg_temp_free_i64(rm1); tcg_temp_free_i64(rm2); - tcg_temp_free_i64(constimm); return true; } @@ -1518,7 +1516,7 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, /* size == 2 */ imm = -a->shift; } - constimm = tcg_const_i32(imm); + constimm = tcg_constant_i32(imm); /* Load all inputs first to avoid potential overwrite */ rm1 = tcg_temp_new_i32(); @@ -1543,7 +1541,6 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, shiftfn(rm3, rm3, constimm); shiftfn(rm4, rm4, constimm); - tcg_temp_free_i32(constimm); tcg_gen_concat_i32_i64(rtmp, rm3, rm4); tcg_temp_free_i32(rm4); @@ -2908,7 +2905,7 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a) return true; } - desc = tcg_const_i32((a->vn << 2) | a->len); + desc = tcg_constant_i32((a->vn << 2) | a->len); def = tcg_temp_new_i64(); if (a->op) { read_neon_element64(def, a->vd, 0, MO_64); @@ -2923,7 +2920,6 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a) tcg_temp_free_i64(def); tcg_temp_free_i64(val); - tcg_temp_free_i32(desc); return true; } diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 33ca1bcfac..dd4a5b23ab 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -282,13 +282,12 @@ static void do_predtest(DisasContext *s, int dofs, int gofs, int words) { TCGv_ptr dptr = tcg_temp_new_ptr(); TCGv_ptr gptr = tcg_temp_new_ptr(); - TCGv_i32 t; + TCGv_i32 t = tcg_temp_new_i32(); tcg_gen_addi_ptr(dptr, cpu_env, dofs); tcg_gen_addi_ptr(gptr, cpu_env, gofs); - t = tcg_const_i32(words); - gen_helper_sve_predtest(t, dptr, gptr, t); + gen_helper_sve_predtest(t, dptr, gptr, tcg_constant_i32(words)); tcg_temp_free_ptr(dptr); tcg_temp_free_ptr(gptr); @@ -889,7 +888,7 @@ static bool do_vpz_ool(DisasContext *s, arg_rpr_esz *a, return true; } - desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); temp = tcg_temp_new_i64(); t_zn = tcg_temp_new_ptr(); t_pg = tcg_temp_new_ptr(); @@ -899,7 +898,6 @@ static bool do_vpz_ool(DisasContext *s, arg_rpr_esz *a, fn(temp, t_zn, t_pg, desc); tcg_temp_free_ptr(t_zn); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(desc); write_fp_dreg(s, a->rd, temp); tcg_temp_free_i64(temp); @@ -1236,7 +1234,7 @@ static void do_index(DisasContext *s, int esz, int rd, TCGv_i64 start, TCGv_i64 incr) { unsigned vsz = vec_full_reg_size(s); - TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_i32 desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); TCGv_ptr t_zd = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, rd)); @@ -1260,17 +1258,14 @@ static void do_index(DisasContext *s, int esz, int rd, tcg_temp_free_i32(i32); } tcg_temp_free_ptr(t_zd); - tcg_temp_free_i32(desc); } static bool trans_INDEX_ii(DisasContext *s, arg_INDEX_ii *a) { if (sve_access_check(s)) { - TCGv_i64 start = tcg_const_i64(a->imm1); - TCGv_i64 incr = tcg_const_i64(a->imm2); + TCGv_i64 start = tcg_constant_i64(a->imm1); + TCGv_i64 incr = tcg_constant_i64(a->imm2); do_index(s, a->esz, a->rd, start, incr); - tcg_temp_free_i64(start); - tcg_temp_free_i64(incr); } return true; } @@ -1278,10 +1273,9 @@ static bool trans_INDEX_ii(DisasContext *s, arg_INDEX_ii *a) static bool trans_INDEX_ir(DisasContext *s, arg_INDEX_ir *a) { if (sve_access_check(s)) { - TCGv_i64 start = tcg_const_i64(a->imm); + TCGv_i64 start = tcg_constant_i64(a->imm); TCGv_i64 incr = cpu_reg(s, a->rm); do_index(s, a->esz, a->rd, start, incr); - tcg_temp_free_i64(start); } return true; } @@ -1290,9 +1284,8 @@ static bool trans_INDEX_ri(DisasContext *s, arg_INDEX_ri *a) { if (sve_access_check(s)) { TCGv_i64 start = cpu_reg(s, a->rn); - TCGv_i64 incr = tcg_const_i64(a->imm); + TCGv_i64 incr = tcg_constant_i64(a->imm); do_index(s, a->esz, a->rd, start, incr); - tcg_temp_free_i64(incr); } return true; } @@ -1884,9 +1877,9 @@ static bool do_pfirst_pnext(DisasContext *s, arg_rr_esz *a, tcg_gen_addi_ptr(t_pd, cpu_env, pred_full_reg_offset(s, a->rd)); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->rn)); - t = tcg_const_i32(desc); + t = tcg_temp_new_i32(); - gen_fn(t, t_pd, t_pg, t); + gen_fn(t, t_pd, t_pg, tcg_constant_i32(desc)); tcg_temp_free_ptr(t_pd); tcg_temp_free_ptr(t_pg); @@ -1916,8 +1909,6 @@ static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a) static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) { int64_t ibound; - TCGv_i64 bound; - TCGCond cond; /* Use normal 64-bit arithmetic to detect 32-bit overflow. */ if (u) { @@ -1928,15 +1919,12 @@ static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) if (d) { tcg_gen_sub_i64(reg, reg, val); ibound = (u ? 0 : INT32_MIN); - cond = TCG_COND_LT; + tcg_gen_smax_i64(reg, reg, tcg_constant_i64(ibound)); } else { tcg_gen_add_i64(reg, reg, val); ibound = (u ? UINT32_MAX : INT32_MAX); - cond = TCG_COND_GT; + tcg_gen_smin_i64(reg, reg, tcg_constant_i64(ibound)); } - bound = tcg_const_i64(ibound); - tcg_gen_movcond_i64(cond, reg, reg, bound, bound, reg); - tcg_temp_free_i64(bound); } /* Similarly with 64-bit values. */ @@ -1998,7 +1986,7 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, nptr = tcg_temp_new_ptr(); tcg_gen_addi_ptr(dptr, cpu_env, vec_full_reg_offset(s, rd)); tcg_gen_addi_ptr(nptr, cpu_env, vec_full_reg_offset(s, rn)); - desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); switch (esz) { case MO_8: @@ -2067,7 +2055,6 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, tcg_temp_free_ptr(dptr); tcg_temp_free_ptr(nptr); - tcg_temp_free_i32(desc); } static bool trans_CNT_r(DisasContext *s, arg_CNT_r *a) @@ -2112,9 +2099,7 @@ static bool trans_SINCDEC_r_32(DisasContext *s, arg_incdec_cnt *a) tcg_gen_ext32s_i64(reg, reg); } } else { - TCGv_i64 t = tcg_const_i64(inc); - do_sat_addsub_32(reg, t, a->u, a->d); - tcg_temp_free_i64(t); + do_sat_addsub_32(reg, tcg_constant_i64(inc), a->u, a->d); } return true; } @@ -2131,9 +2116,7 @@ static bool trans_SINCDEC_r_64(DisasContext *s, arg_incdec_cnt *a) TCGv_i64 reg = cpu_reg(s, a->rd); if (inc != 0) { - TCGv_i64 t = tcg_const_i64(inc); - do_sat_addsub_64(reg, t, a->u, a->d); - tcg_temp_free_i64(t); + do_sat_addsub_64(reg, tcg_constant_i64(inc), a->u, a->d); } return true; } @@ -2150,11 +2133,10 @@ static bool trans_INCDEC_v(DisasContext *s, arg_incdec2_cnt *a) if (inc != 0) { if (sve_access_check(s)) { - TCGv_i64 t = tcg_const_i64(a->d ? -inc : inc); tcg_gen_gvec_adds(a->esz, vec_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), - t, fullsz, fullsz); - tcg_temp_free_i64(t); + tcg_constant_i64(a->d ? -inc : inc), + fullsz, fullsz); } } else { do_mov_z(s, a->rd, a->rn); @@ -2174,9 +2156,8 @@ static bool trans_SINCDEC_v(DisasContext *s, arg_incdec2_cnt *a) if (inc != 0) { if (sve_access_check(s)) { - TCGv_i64 t = tcg_const_i64(inc); - do_sat_addsub_vec(s, a->esz, a->rd, a->rn, t, a->u, a->d); - tcg_temp_free_i64(t); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, + tcg_constant_i64(inc), a->u, a->d); } } else { do_mov_z(s, a->rd, a->rn); @@ -2249,7 +2230,7 @@ static void do_cpy_m(DisasContext *s, int esz, int rd, int rn, int pg, gen_helper_sve_cpy_m_s, gen_helper_sve_cpy_m_d, }; unsigned vsz = vec_full_reg_size(s); - TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_i32 desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); TCGv_ptr t_zd = tcg_temp_new_ptr(); TCGv_ptr t_zn = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); @@ -2263,7 +2244,6 @@ static void do_cpy_m(DisasContext *s, int esz, int rd, int rn, int pg, tcg_temp_free_ptr(t_zd); tcg_temp_free_ptr(t_zn); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(desc); } static bool trans_FCPY(DisasContext *s, arg_FCPY *a) @@ -2274,9 +2254,7 @@ static bool trans_FCPY(DisasContext *s, arg_FCPY *a) if (sve_access_check(s)) { /* Decode the VFP immediate. */ uint64_t imm = vfp_expand_imm(a->esz, a->imm); - TCGv_i64 t_imm = tcg_const_i64(imm); - do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, t_imm); - tcg_temp_free_i64(t_imm); + do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, tcg_constant_i64(imm)); } return true; } @@ -2287,9 +2265,7 @@ static bool trans_CPY_m_i(DisasContext *s, arg_rpri_esz *a) return false; } if (sve_access_check(s)) { - TCGv_i64 t_imm = tcg_const_i64(a->imm); - do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, t_imm); - tcg_temp_free_i64(t_imm); + do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, tcg_constant_i64(a->imm)); } return true; } @@ -2306,11 +2282,10 @@ static bool trans_CPY_z_i(DisasContext *s, arg_CPY_z_i *a) } if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); - TCGv_i64 t_imm = tcg_const_i64(a->imm); tcg_gen_gvec_2i_ool(vec_full_reg_offset(s, a->rd), pred_full_reg_offset(s, a->pg), - t_imm, vsz, vsz, 0, fns[a->esz]); - tcg_temp_free_i64(t_imm); + tcg_constant_i64(a->imm), + vsz, vsz, 0, fns[a->esz]); } return true; } @@ -2411,7 +2386,7 @@ static void do_insr_i64(DisasContext *s, arg_rrr_esz *a, TCGv_i64 val) gen_helper_sve_insr_s, gen_helper_sve_insr_d, }; unsigned vsz = vec_full_reg_size(s); - TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_i32 desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); TCGv_ptr t_zd = tcg_temp_new_ptr(); TCGv_ptr t_zn = tcg_temp_new_ptr(); @@ -2422,7 +2397,6 @@ static void do_insr_i64(DisasContext *s, arg_rrr_esz *a, TCGv_i64 val) tcg_temp_free_ptr(t_zd); tcg_temp_free_ptr(t_zn); - tcg_temp_free_i32(desc); } static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a) @@ -2541,7 +2515,6 @@ static bool do_perm_pred3(DisasContext *s, arg_rrr_esz *a, bool high_odd, TCGv_ptr t_d = tcg_temp_new_ptr(); TCGv_ptr t_n = tcg_temp_new_ptr(); TCGv_ptr t_m = tcg_temp_new_ptr(); - TCGv_i32 t_desc; uint32_t desc = 0; desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz); @@ -2551,14 +2524,12 @@ static bool do_perm_pred3(DisasContext *s, arg_rrr_esz *a, bool high_odd, tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); tcg_gen_addi_ptr(t_n, cpu_env, pred_full_reg_offset(s, a->rn)); tcg_gen_addi_ptr(t_m, cpu_env, pred_full_reg_offset(s, a->rm)); - t_desc = tcg_const_i32(desc); - fn(t_d, t_n, t_m, t_desc); + fn(t_d, t_n, t_m, tcg_constant_i32(desc)); tcg_temp_free_ptr(t_d); tcg_temp_free_ptr(t_n); tcg_temp_free_ptr(t_m); - tcg_temp_free_i32(t_desc); return true; } @@ -2572,7 +2543,6 @@ static bool do_perm_pred2(DisasContext *s, arg_rr_esz *a, bool high_odd, unsigned vsz = pred_full_reg_size(s); TCGv_ptr t_d = tcg_temp_new_ptr(); TCGv_ptr t_n = tcg_temp_new_ptr(); - TCGv_i32 t_desc; uint32_t desc = 0; tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); @@ -2581,11 +2551,9 @@ static bool do_perm_pred2(DisasContext *s, arg_rr_esz *a, bool high_odd, desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); desc = FIELD_DP32(desc, PREDDESC, DATA, high_odd); - t_desc = tcg_const_i32(desc); - fn(t_d, t_n, t_desc); + fn(t_d, t_n, tcg_constant_i32(desc)); - tcg_temp_free_i32(t_desc); tcg_temp_free_ptr(t_d); tcg_temp_free_ptr(t_n); return true; @@ -2787,18 +2755,15 @@ static void find_last_active(DisasContext *s, TCGv_i32 ret, int esz, int pg) * round up, as we do elsewhere, because we need the exact size. */ TCGv_ptr t_p = tcg_temp_new_ptr(); - TCGv_i32 t_desc; unsigned desc = 0; desc = FIELD_DP32(desc, PREDDESC, OPRSZ, pred_full_reg_size(s)); desc = FIELD_DP32(desc, PREDDESC, ESZ, esz); tcg_gen_addi_ptr(t_p, cpu_env, pred_full_reg_offset(s, pg)); - t_desc = tcg_const_i32(desc); - gen_helper_sve_last_active_element(ret, t_p, t_desc); + gen_helper_sve_last_active_element(ret, t_p, tcg_constant_i32(desc)); - tcg_temp_free_i32(t_desc); tcg_temp_free_ptr(t_p); } @@ -2813,11 +2778,9 @@ static void incr_last_active(DisasContext *s, TCGv_i32 last, int esz) if (is_power_of_2(vsz)) { tcg_gen_andi_i32(last, last, vsz - 1); } else { - TCGv_i32 max = tcg_const_i32(vsz); - TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 max = tcg_constant_i32(vsz); + TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_movcond_i32(TCG_COND_GEU, last, last, max, zero, last); - tcg_temp_free_i32(max); - tcg_temp_free_i32(zero); } } @@ -2829,11 +2792,9 @@ static void wrap_last_active(DisasContext *s, TCGv_i32 last, int esz) if (is_power_of_2(vsz)) { tcg_gen_andi_i32(last, last, vsz - 1); } else { - TCGv_i32 max = tcg_const_i32(vsz - (1 << esz)); - TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 max = tcg_constant_i32(vsz - (1 << esz)); + TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_movcond_i32(TCG_COND_LT, last, last, zero, max, last); - tcg_temp_free_i32(max); - tcg_temp_free_i32(zero); } } @@ -2872,7 +2833,7 @@ static TCGv_i64 load_last_active(DisasContext *s, TCGv_i32 last, * The final adjustment for the vector register base * is added via constant offset to the load. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN /* Adjust for element ordering. See vec_reg_offset. */ if (esz < 3) { tcg_gen_xori_i32(last, last, 8 - (1 << esz)); @@ -2950,7 +2911,7 @@ static void do_clast_scalar(DisasContext *s, int esz, int pg, int rm, bool before, TCGv_i64 reg_val) { TCGv_i32 last = tcg_temp_new_i32(); - TCGv_i64 ele, cmp, zero; + TCGv_i64 ele, cmp; find_last_active(s, last, esz, pg); @@ -2970,10 +2931,9 @@ static void do_clast_scalar(DisasContext *s, int esz, int pg, int rm, ele = load_last_active(s, last, rm, esz); tcg_temp_free_i32(last); - zero = tcg_const_i64(0); - tcg_gen_movcond_i64(TCG_COND_GE, reg_val, cmp, zero, ele, reg_val); + tcg_gen_movcond_i64(TCG_COND_GE, reg_val, cmp, tcg_constant_i64(0), + ele, reg_val); - tcg_temp_free_i64(zero); tcg_temp_free_i64(cmp); tcg_temp_free_i64(ele); } @@ -3201,7 +3161,7 @@ static bool do_ppzz_flags(DisasContext *s, arg_rprr_esz *a, } vsz = vec_full_reg_size(s); - t = tcg_const_i32(simd_desc(vsz, vsz, 0)); + t = tcg_temp_new_i32(); pd = tcg_temp_new_ptr(); zn = tcg_temp_new_ptr(); zm = tcg_temp_new_ptr(); @@ -3212,7 +3172,7 @@ static bool do_ppzz_flags(DisasContext *s, arg_rprr_esz *a, tcg_gen_addi_ptr(zm, cpu_env, vec_full_reg_offset(s, a->rm)); tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); - gen_fn(t, pd, zn, zm, pg, t); + gen_fn(t, pd, zn, zm, pg, tcg_constant_i32(simd_desc(vsz, vsz, 0))); tcg_temp_free_ptr(pd); tcg_temp_free_ptr(zn); @@ -3286,7 +3246,7 @@ static bool do_ppzi_flags(DisasContext *s, arg_rpri_esz *a, } vsz = vec_full_reg_size(s); - t = tcg_const_i32(simd_desc(vsz, vsz, a->imm)); + t = tcg_temp_new_i32(); pd = tcg_temp_new_ptr(); zn = tcg_temp_new_ptr(); pg = tcg_temp_new_ptr(); @@ -3295,7 +3255,7 @@ static bool do_ppzi_flags(DisasContext *s, arg_rpri_esz *a, tcg_gen_addi_ptr(zn, cpu_env, vec_full_reg_offset(s, a->rn)); tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); - gen_fn(t, pd, zn, pg, t); + gen_fn(t, pd, zn, pg, tcg_constant_i32(simd_desc(vsz, vsz, a->imm))); tcg_temp_free_ptr(pd); tcg_temp_free_ptr(zn); @@ -3348,7 +3308,7 @@ static bool do_brk3(DisasContext *s, arg_rprr_s *a, TCGv_ptr n = tcg_temp_new_ptr(); TCGv_ptr m = tcg_temp_new_ptr(); TCGv_ptr g = tcg_temp_new_ptr(); - TCGv_i32 t = tcg_const_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); + TCGv_i32 desc = tcg_constant_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); @@ -3356,16 +3316,17 @@ static bool do_brk3(DisasContext *s, arg_rprr_s *a, tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); if (a->s) { - fn_s(t, d, n, m, g, t); + TCGv_i32 t = tcg_temp_new_i32(); + fn_s(t, d, n, m, g, desc); do_pred_flags(t); + tcg_temp_free_i32(t); } else { - fn(d, n, m, g, t); + fn(d, n, m, g, desc); } tcg_temp_free_ptr(d); tcg_temp_free_ptr(n); tcg_temp_free_ptr(m); tcg_temp_free_ptr(g); - tcg_temp_free_i32(t); return true; } @@ -3382,22 +3343,23 @@ static bool do_brk2(DisasContext *s, arg_rpr_s *a, TCGv_ptr d = tcg_temp_new_ptr(); TCGv_ptr n = tcg_temp_new_ptr(); TCGv_ptr g = tcg_temp_new_ptr(); - TCGv_i32 t = tcg_const_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); + TCGv_i32 desc = tcg_constant_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); if (a->s) { - fn_s(t, d, n, g, t); + TCGv_i32 t = tcg_temp_new_i32(); + fn_s(t, d, n, g, desc); do_pred_flags(t); + tcg_temp_free_i32(t); } else { - fn(d, n, g, t); + fn(d, n, g, desc); } tcg_temp_free_ptr(d); tcg_temp_free_ptr(n); tcg_temp_free_ptr(g); - tcg_temp_free_i32(t); return true; } @@ -3466,19 +3428,16 @@ static void do_cntp(DisasContext *s, TCGv_i64 val, int esz, int pn, int pg) TCGv_ptr t_pn = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); unsigned desc = 0; - TCGv_i32 t_desc; desc = FIELD_DP32(desc, PREDDESC, OPRSZ, psz); desc = FIELD_DP32(desc, PREDDESC, ESZ, esz); tcg_gen_addi_ptr(t_pn, cpu_env, pred_full_reg_offset(s, pn)); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); - t_desc = tcg_const_i32(desc); - gen_helper_sve_cntp(val, t_pn, t_pg, t_desc); + gen_helper_sve_cntp(val, t_pn, t_pg, tcg_constant_i32(desc)); tcg_temp_free_ptr(t_pn); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(t_desc); } } @@ -3593,7 +3552,7 @@ static bool trans_CTERM(DisasContext *s, arg_CTERM *a) static bool trans_WHILE(DisasContext *s, arg_WHILE *a) { TCGv_i64 op0, op1, t0, t1, tmax; - TCGv_i32 t2, t3; + TCGv_i32 t2; TCGv_ptr ptr; unsigned vsz = vec_full_reg_size(s); unsigned desc = 0; @@ -3649,7 +3608,7 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) } } - tmax = tcg_const_i64(vsz >> a->esz); + tmax = tcg_constant_i64(vsz >> a->esz); if (eq) { /* Equality means one more iteration. */ tcg_gen_addi_i64(t0, t0, 1); @@ -3669,7 +3628,6 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) /* Bound to the maximum. */ tcg_gen_umin_i64(t0, t0, tmax); - tcg_temp_free_i64(tmax); /* Set the count to zero if the condition is false. */ tcg_gen_movi_i64(t1, 0); @@ -3686,28 +3644,26 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); - t3 = tcg_const_i32(desc); ptr = tcg_temp_new_ptr(); tcg_gen_addi_ptr(ptr, cpu_env, pred_full_reg_offset(s, a->rd)); if (a->lt) { - gen_helper_sve_whilel(t2, ptr, t2, t3); + gen_helper_sve_whilel(t2, ptr, t2, tcg_constant_i32(desc)); } else { - gen_helper_sve_whileg(t2, ptr, t2, t3); + gen_helper_sve_whileg(t2, ptr, t2, tcg_constant_i32(desc)); } do_pred_flags(t2); tcg_temp_free_ptr(ptr); tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); return true; } static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) { TCGv_i64 op0, op1, diff, t1, tmax; - TCGv_i32 t2, t3; + TCGv_i32 t2; TCGv_ptr ptr; unsigned vsz = vec_full_reg_size(s); unsigned desc = 0; @@ -3722,7 +3678,7 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) op0 = read_cpu_reg(s, a->rn, 1); op1 = read_cpu_reg(s, a->rm, 1); - tmax = tcg_const_i64(vsz); + tmax = tcg_constant_i64(vsz); diff = tcg_temp_new_i64(); if (a->rw) { @@ -3748,7 +3704,6 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) /* Bound to the maximum. */ tcg_gen_umin_i64(diff, diff, tmax); - tcg_temp_free_i64(tmax); /* Since we're bounded, pass as a 32-bit type. */ t2 = tcg_temp_new_i32(); @@ -3757,17 +3712,15 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); - t3 = tcg_const_i32(desc); ptr = tcg_temp_new_ptr(); tcg_gen_addi_ptr(ptr, cpu_env, pred_full_reg_offset(s, a->rd)); - gen_helper_sve_whilel(t2, ptr, t2, t3); + gen_helper_sve_whilel(t2, ptr, t2, tcg_constant_i32(desc)); do_pred_flags(t2); tcg_temp_free_ptr(ptr); tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); return true; } @@ -3861,11 +3814,9 @@ static bool trans_SUBR_zzi(DisasContext *s, arg_rri_esz *a) } if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); - TCGv_i64 c = tcg_const_i64(a->imm); tcg_gen_gvec_2s(vec_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), - vsz, vsz, c, &op[a->esz]); - tcg_temp_free_i64(c); + vsz, vsz, tcg_constant_i64(a->imm), &op[a->esz]); } return true; } @@ -3886,9 +3837,8 @@ static bool do_zzi_sat(DisasContext *s, arg_rri_esz *a, bool u, bool d) return false; } if (sve_access_check(s)) { - TCGv_i64 val = tcg_const_i64(a->imm); - do_sat_addsub_vec(s, a->esz, a->rd, a->rn, val, u, d); - tcg_temp_free_i64(val); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, + tcg_constant_i64(a->imm), u, d); } return true; } @@ -3917,12 +3867,9 @@ static bool do_zzi_ool(DisasContext *s, arg_rri_esz *a, gen_helper_gvec_2i *fn) { if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); - TCGv_i64 c = tcg_const_i64(a->imm); - tcg_gen_gvec_2i_ool(vec_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), - c, vsz, vsz, 0, fn); - tcg_temp_free_i64(c); + tcg_constant_i64(a->imm), vsz, vsz, 0, fn); } return true; } @@ -4226,7 +4173,7 @@ static void do_reduce(DisasContext *s, arg_rpr_esz *a, { unsigned vsz = vec_full_reg_size(s); unsigned p2vsz = pow2ceil(vsz); - TCGv_i32 t_desc = tcg_const_i32(simd_desc(vsz, vsz, p2vsz)); + TCGv_i32 t_desc = tcg_constant_i32(simd_desc(vsz, vsz, p2vsz)); TCGv_ptr t_zn, t_pg, status; TCGv_i64 temp; @@ -4242,7 +4189,6 @@ static void do_reduce(DisasContext *s, arg_rpr_esz *a, tcg_temp_free_ptr(t_zn); tcg_temp_free_ptr(t_pg); tcg_temp_free_ptr(status); - tcg_temp_free_i32(t_desc); write_fp_dreg(s, a->rd, temp); tcg_temp_free_i64(temp); @@ -4419,11 +4365,10 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) tcg_gen_addi_ptr(t_rm, cpu_env, vec_full_reg_offset(s, a->rm)); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); t_fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - t_desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + t_desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fns[a->esz - 1](t_val, t_val, t_rm, t_pg, t_fpst, t_desc); - tcg_temp_free_i32(t_desc); tcg_temp_free_ptr(t_fpst); tcg_temp_free_ptr(t_pg); tcg_temp_free_ptr(t_rm); @@ -4540,10 +4485,9 @@ static void do_fp_scalar(DisasContext *s, int zd, int zn, int pg, bool is_fp16, tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); status = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); - desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fn(t_zd, t_zn, t_pg, scalar, status, desc); - tcg_temp_free_i32(desc); tcg_temp_free_ptr(status); tcg_temp_free_ptr(t_pg); tcg_temp_free_ptr(t_zn); @@ -4553,9 +4497,8 @@ static void do_fp_scalar(DisasContext *s, int zd, int zn, int pg, bool is_fp16, static void do_fp_imm(DisasContext *s, arg_rpri_esz *a, uint64_t imm, gen_helper_sve_fp2scalar *fn) { - TCGv_i64 temp = tcg_const_i64(imm); - do_fp_scalar(s, a->rd, a->rn, a->pg, a->esz == MO_16, temp, fn); - tcg_temp_free_i64(temp); + do_fp_scalar(s, a->rd, a->rn, a->pg, a->esz == MO_16, + tcg_constant_i64(imm), fn); } #define DO_FP_IMM(NAME, name, const0, const1) \ @@ -5302,7 +5245,6 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, { unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_pg; - TCGv_i32 t_desc; int desc = 0; /* @@ -5324,14 +5266,12 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, } desc = simd_desc(vsz, vsz, zt | desc); - t_desc = tcg_const_i32(desc); t_pg = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); - fn(cpu_env, t_pg, addr, t_desc); + fn(cpu_env, t_pg, addr, tcg_constant_i32(desc)); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(t_desc); } /* Indexed by [mte][be][dtype][nreg] */ @@ -5711,7 +5651,7 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) * for this load operation. */ TCGv_i64 tmp = tcg_temp_new_i64(); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN poff += 6; #endif tcg_gen_ld16u_i64(tmp, cpu_env, poff); @@ -5790,7 +5730,7 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) * for this load operation. */ TCGv_i64 tmp = tcg_temp_new_i64(); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN poff += 4; #endif tcg_gen_ld32u_i64(tmp, cpu_env, poff); @@ -6074,7 +6014,6 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, TCGv_ptr t_zm = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); TCGv_ptr t_zt = tcg_temp_new_ptr(); - TCGv_i32 t_desc; int desc = 0; if (s->mte_active[0]) { @@ -6086,17 +6025,15 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, desc <<= SVE_MTEDESC_SHIFT; } desc = simd_desc(vsz, vsz, desc | scale); - t_desc = tcg_const_i32(desc); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); tcg_gen_addi_ptr(t_zm, cpu_env, vec_full_reg_offset(s, zm)); tcg_gen_addi_ptr(t_zt, cpu_env, vec_full_reg_offset(s, zt)); - fn(cpu_env, t_zt, t_pg, t_zm, scalar, t_desc); + fn(cpu_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i32(desc)); tcg_temp_free_ptr(t_zt); tcg_temp_free_ptr(t_zm); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(t_desc); } /* Indexed by [mte][be][ff][xs][u][msz]. */ @@ -6457,7 +6394,6 @@ static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a) gen_helper_gvec_mem_scatter *fn = NULL; bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; - TCGv_i64 imm; if (a->esz < a->msz || (a->esz == a->msz && !a->u)) { return false; @@ -6479,18 +6415,40 @@ static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a) /* Treat LD1_zpiz (zn[x] + imm) the same way as LD1_zprz (rn + zm[x]) * by loading the immediate into the scalar parameter. */ - imm = tcg_const_i64(a->imm << a->msz); - do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, a->msz, false, fn); - tcg_temp_free_i64(imm); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + tcg_constant_i64(a->imm << a->msz), a->msz, false, fn); return true; } static bool trans_LDNT1_zprz(DisasContext *s, arg_LD1_zprz *a) { + gen_helper_gvec_mem_scatter *fn = NULL; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (a->esz < a->msz + !a->u) { + return false; + } if (!dc_isar_feature(aa64_sve2, s)) { return false; } - return trans_LD1_zprz(s, a); + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = gather_load_fn32[mte][be][0][0][a->u][a->msz]; + break; + case MO_64: + fn = gather_load_fn64[mte][be][0][2][a->u][a->msz]; + break; + } + assert(fn != NULL); + + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + cpu_reg(s, a->rm), a->msz, false, fn); + return true; } /* Indexed by [mte][be][xs][msz]. */ @@ -6617,7 +6575,6 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a) gen_helper_gvec_mem_scatter *fn = NULL; bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; - TCGv_i64 imm; if (a->esz < a->msz) { return false; @@ -6639,18 +6596,41 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a) /* Treat ST1_zpiz (zn[x] + imm) the same way as ST1_zprz (rn + zm[x]) * by loading the immediate into the scalar parameter. */ - imm = tcg_const_i64(a->imm << a->msz); - do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, a->msz, true, fn); - tcg_temp_free_i64(imm); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + tcg_constant_i64(a->imm << a->msz), a->msz, true, fn); return true; } static bool trans_STNT1_zprz(DisasContext *s, arg_ST1_zprz *a) { + gen_helper_gvec_mem_scatter *fn; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (a->esz < a->msz) { + return false; + } if (!dc_isar_feature(aa64_sve2, s)) { return false; } - return trans_ST1_zprz(s, a); + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = scatter_store_fn32[mte][be][0][a->msz]; + break; + case MO_64: + fn = scatter_store_fn64[mte][be][2][a->msz]; + break; + default: + g_assert_not_reached(); + } + + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + cpu_reg(s, a->rm), a->msz, true, fn); + return true; } /* diff --git a/target/arm/translate-vfp.c b/target/arm/translate-vfp.c index 17f796e32a..40a513b822 100644 --- a/target/arm/translate-vfp.c +++ b/target/arm/translate-vfp.c @@ -93,7 +93,7 @@ uint64_t vfp_expand_imm(int size, uint8_t imm8) static inline long vfp_f16_offset(unsigned reg, bool top) { long offs = vfp_reg_offset(false, reg); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN if (!top) { offs += 2; } @@ -180,8 +180,7 @@ static void gen_update_fp_context(DisasContext *s) gen_helper_vfp_set_fpscr(cpu_env, fpscr); tcg_temp_free_i32(fpscr); if (dc_isar_feature(aa32_mve, s)) { - TCGv_i32 z32 = tcg_const_i32(0); - store_cpu_field(z32, v7m.vpr); + store_cpu_field(tcg_constant_i32(0), v7m.vpr); } /* * We just updated the FPSCR and VPR. Some of this state is cached @@ -317,7 +316,7 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) TCGv_i64 frn, frm, dest; TCGv_i64 tmp, zero, zf, nf, vf; - zero = tcg_const_i64(0); + zero = tcg_constant_i64(0); frn = tcg_temp_new_i64(); frm = tcg_temp_new_i64(); @@ -335,27 +334,22 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) vfp_load_reg64(frm, rm); switch (a->cc) { case 0: /* eq: Z */ - tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero, frn, frm); break; case 1: /* vs: V */ - tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero, frn, frm); break; case 2: /* ge: N == V -> N ^ V == 0 */ tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, vf, nf); - tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, frn, frm); tcg_temp_free_i64(tmp); break; case 3: /* gt: !Z && N == V */ - tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, frn, frm); tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, vf, nf); - tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, - dest, frm); + tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, dest, frm); tcg_temp_free_i64(tmp); break; } @@ -367,13 +361,11 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) tcg_temp_free_i64(zf); tcg_temp_free_i64(nf); tcg_temp_free_i64(vf); - - tcg_temp_free_i64(zero); } else { TCGv_i32 frn, frm, dest; TCGv_i32 tmp, zero; - zero = tcg_const_i32(0); + zero = tcg_constant_i32(0); frn = tcg_temp_new_i32(); frm = tcg_temp_new_i32(); @@ -382,27 +374,22 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) vfp_load_reg32(frm, rm); switch (a->cc) { case 0: /* eq: Z */ - tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero, frn, frm); break; case 1: /* vs: V */ - tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero, frn, frm); break; case 2: /* ge: N == V -> N ^ V == 0 */ tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, frn, frm); tcg_temp_free_i32(tmp); break; case 3: /* gt: !Z && N == V */ - tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, frn, frm); tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, - dest, frm); + tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, dest, frm); tcg_temp_free_i32(tmp); break; } @@ -414,8 +401,6 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) tcg_temp_free_i32(frn); tcg_temp_free_i32(frm); tcg_temp_free_i32(dest); - - tcg_temp_free_i32(zero); } return true; @@ -547,7 +532,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) fpst = fpstatus_ptr(FPST_FPCR); } - tcg_shift = tcg_const_i32(0); + tcg_shift = tcg_constant_i32(0); tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding)); gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); @@ -595,8 +580,6 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); tcg_temp_free_i32(tcg_rmode); - tcg_temp_free_i32(tcg_shift); - tcg_temp_free_ptr(fpst); return true; @@ -850,15 +833,11 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) case ARM_VFP_MVFR2: case ARM_VFP_FPSID: if (s->current_el == 1) { - TCGv_i32 tcg_reg, tcg_rt; - gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); - tcg_reg = tcg_const_i32(a->reg); - tcg_rt = tcg_const_i32(a->rt); - gen_helper_check_hcr_el2_trap(cpu_env, tcg_rt, tcg_reg); - tcg_temp_free_i32(tcg_reg); - tcg_temp_free_i32(tcg_rt); + gen_helper_check_hcr_el2_trap(cpu_env, + tcg_constant_i32(a->rt), + tcg_constant_i32(a->reg)); } /* fall through */ case ARM_VFP_FPEXC: @@ -2388,8 +2367,6 @@ MAKE_VFM_TRANS_FNS(dp) static bool trans_VMOV_imm_hp(DisasContext *s, arg_VMOV_imm_sp *a) { - TCGv_i32 fd; - if (!dc_isar_feature(aa32_fp16_arith, s)) { return false; } @@ -2402,9 +2379,7 @@ static bool trans_VMOV_imm_hp(DisasContext *s, arg_VMOV_imm_sp *a) return true; } - fd = tcg_const_i32(vfp_expand_imm(MO_16, a->imm)); - vfp_store_reg32(fd, a->vd); - tcg_temp_free_i32(fd); + vfp_store_reg32(tcg_constant_i32(vfp_expand_imm(MO_16, a->imm)), a->vd); return true; } @@ -2440,7 +2415,7 @@ static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a) } } - fd = tcg_const_i32(vfp_expand_imm(MO_32, a->imm)); + fd = tcg_constant_i32(vfp_expand_imm(MO_32, a->imm)); for (;;) { vfp_store_reg32(fd, vd); @@ -2454,7 +2429,6 @@ static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a) vd = vfp_advance_sreg(vd, delta_d); } - tcg_temp_free_i32(fd); return true; } @@ -2495,7 +2469,7 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) } } - fd = tcg_const_i64(vfp_expand_imm(MO_64, a->imm)); + fd = tcg_constant_i64(vfp_expand_imm(MO_64, a->imm)); for (;;) { vfp_store_reg64(fd, vd); @@ -2509,7 +2483,6 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) vd = vfp_advance_dreg(vd, delta_d); } - tcg_temp_free_i64(fd); return true; } @@ -3294,7 +3267,7 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_load_reg32(vd, a->vd); fpst = fpstatus_ptr(FPST_FPCR_F16); - shift = tcg_const_i32(frac_bits); + shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ switch (a->opc) { @@ -3328,7 +3301,6 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_store_reg32(vd, a->vd); tcg_temp_free_i32(vd); - tcg_temp_free_i32(shift); tcg_temp_free_ptr(fpst); return true; } @@ -3353,7 +3325,7 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_load_reg32(vd, a->vd); fpst = fpstatus_ptr(FPST_FPCR); - shift = tcg_const_i32(frac_bits); + shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ switch (a->opc) { @@ -3387,7 +3359,6 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_store_reg32(vd, a->vd); tcg_temp_free_i32(vd); - tcg_temp_free_i32(shift); tcg_temp_free_ptr(fpst); return true; } @@ -3418,7 +3389,7 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) vfp_load_reg64(vd, a->vd); fpst = fpstatus_ptr(FPST_FPCR); - shift = tcg_const_i32(frac_bits); + shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ switch (a->opc) { @@ -3452,7 +3423,6 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) vfp_store_reg64(vd, a->vd); tcg_temp_free_i64(vd); - tcg_temp_free_i32(shift); tcg_temp_free_ptr(fpst); return true; } diff --git a/target/arm/translate.c b/target/arm/translate.c index 2b87e438d9..54a05beb17 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -180,6 +180,25 @@ typedef enum ISSInfo { ISSIs16Bit = (1 << 8), } ISSInfo; +/* + * Store var into env + offset to a member with size bytes. + * Free var after use. + */ +void store_cpu_offset(TCGv_i32 var, int offset, int size) +{ + switch (size) { + case 1: + tcg_gen_st8_i32(var, cpu_env, offset); + break; + case 4: + tcg_gen_st_i32(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } + tcg_temp_free_i32(var); +} + /* Save the syndrome information for a Data Abort */ static void disas_set_da_iss(DisasContext *s, MemOp memop, ISSInfo issinfo) { @@ -327,18 +346,33 @@ static void store_sp_checked(DisasContext *s, TCGv_i32 var) void gen_set_cpsr(TCGv_i32 var, uint32_t mask) { - TCGv_i32 tmp_mask = tcg_const_i32(mask); - gen_helper_cpsr_write(cpu_env, var, tmp_mask); - tcg_temp_free_i32(tmp_mask); + gen_helper_cpsr_write(cpu_env, var, tcg_constant_i32(mask)); +} + +static void gen_rebuild_hflags(DisasContext *s, bool new_el) +{ + bool m_profile = arm_dc_feature(s, ARM_FEATURE_M); + + if (new_el) { + if (m_profile) { + gen_helper_rebuild_hflags_m32_newel(cpu_env); + } else { + gen_helper_rebuild_hflags_a32_newel(cpu_env); + } + } else { + TCGv_i32 tcg_el = tcg_constant_i32(s->current_el); + if (m_profile) { + gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); + } else { + gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); + } + } } static void gen_exception_internal(int excp) { - TCGv_i32 tcg_excp = tcg_const_i32(excp); - assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_excp); - tcg_temp_free_i32(tcg_excp); + gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); } static void gen_singlestep_exception(DisasContext *s) @@ -513,16 +547,14 @@ static void gen_sbc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) #define GEN_SHIFT(name) \ static void gen_##name(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) \ { \ - TCGv_i32 tmp1, tmp2, tmp3; \ - tmp1 = tcg_temp_new_i32(); \ - tcg_gen_andi_i32(tmp1, t1, 0xff); \ - tmp2 = tcg_const_i32(0); \ - tmp3 = tcg_const_i32(0x1f); \ - tcg_gen_movcond_i32(TCG_COND_GTU, tmp2, tmp1, tmp3, tmp2, t0); \ - tcg_temp_free_i32(tmp3); \ - tcg_gen_andi_i32(tmp1, tmp1, 0x1f); \ - tcg_gen_##name##_i32(dest, tmp2, tmp1); \ - tcg_temp_free_i32(tmp2); \ + TCGv_i32 tmpd = tcg_temp_new_i32(); \ + TCGv_i32 tmp1 = tcg_temp_new_i32(); \ + TCGv_i32 zero = tcg_constant_i32(0); \ + tcg_gen_andi_i32(tmp1, t1, 0x1f); \ + tcg_gen_##name##_i32(tmpd, t0, tmp1); \ + tcg_gen_andi_i32(tmp1, t1, 0xe0); \ + tcg_gen_movcond_i32(TCG_COND_NE, dest, tmp1, zero, zero, tmpd); \ + tcg_temp_free_i32(tmpd); \ tcg_temp_free_i32(tmp1); \ } GEN_SHIFT(shl) @@ -531,12 +563,10 @@ GEN_SHIFT(shr) static void gen_sar(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) { - TCGv_i32 tmp1, tmp2; - tmp1 = tcg_temp_new_i32(); + TCGv_i32 tmp1 = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp1, t1, 0xff); - tmp2 = tcg_const_i32(0x1f); - tcg_gen_movcond_i32(TCG_COND_GTU, tmp1, tmp1, tmp2, tmp2, tmp1); - tcg_temp_free_i32(tmp2); + tcg_gen_umin_i32(tmp1, tmp1, tcg_constant_i32(31)); tcg_gen_sar_i32(dest, t0, tmp1); tcg_temp_free_i32(tmp1); } @@ -1043,12 +1073,8 @@ static inline void gen_smc(DisasContext *s) /* As with HVC, we may take an exception either before or after * the insn executes. */ - TCGv_i32 tmp; - gen_set_pc_im(s, s->pc_curr); - tmp = tcg_const_i32(syn_aa32_smc()); - gen_helper_pre_smc(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa32_smc())); gen_set_pc_im(s, s->base.pc_next); s->base.is_jmp = DISAS_SMC; } @@ -1076,13 +1102,9 @@ void gen_exception_insn(DisasContext *s, uint64_t pc, int excp, static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syn) { - TCGv_i32 tcg_syn; - gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); - tcg_syn = tcg_const_i32(syn); - gen_helper_exception_bkpt_insn(cpu_env, tcg_syn); - tcg_temp_free_i32(tcg_syn); + gen_helper_exception_bkpt_insn(cpu_env, tcg_constant_i32(syn)); s->base.is_jmp = DISAS_NORETURN; } @@ -1096,16 +1118,11 @@ void unallocated_encoding(DisasContext *s) static void gen_exception_el(DisasContext *s, int excp, uint32_t syn, TCGv_i32 tcg_el) { - TCGv_i32 tcg_excp; - TCGv_i32 tcg_syn; - gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); - tcg_excp = tcg_const_i32(excp); - tcg_syn = tcg_const_i32(syn); - gen_helper_exception_with_syndrome(cpu_env, tcg_excp, tcg_syn, tcg_el); - tcg_temp_free_i32(tcg_syn); - tcg_temp_free_i32(tcg_excp); + gen_helper_exception_with_syndrome(cpu_env, + tcg_constant_i32(excp), + tcg_constant_i32(syn), tcg_el); s->base.is_jmp = DISAS_NORETURN; } @@ -1158,7 +1175,7 @@ long neon_element_offset(int reg, int element, MemOp memop) { int element_size = 1 << (memop & MO_SIZE); int ofs = element * element_size; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN /* * Calculate the offset assuming fully little-endian, * then XOR to account for the order of the 8-byte units. @@ -1828,24 +1845,21 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_M0_wRn(wrd); switch ((insn >> 6) & 3) { case 0: - tmp2 = tcg_const_i32(0xff); - tmp3 = tcg_const_i32((insn & 7) << 3); + tmp2 = tcg_constant_i32(0xff); + tmp3 = tcg_constant_i32((insn & 7) << 3); break; case 1: - tmp2 = tcg_const_i32(0xffff); - tmp3 = tcg_const_i32((insn & 3) << 4); + tmp2 = tcg_constant_i32(0xffff); + tmp3 = tcg_constant_i32((insn & 3) << 4); break; case 2: - tmp2 = tcg_const_i32(0xffffffff); - tmp3 = tcg_const_i32((insn & 1) << 5); + tmp2 = tcg_constant_i32(0xffffffff); + tmp3 = tcg_constant_i32((insn & 1) << 5); break; default: - tmp2 = NULL; - tmp3 = NULL; + g_assert_not_reached(); } gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3); - tcg_temp_free_i32(tmp3); - tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); @@ -2301,10 +2315,9 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_const_i32((insn >> 20) & 3); iwmmxt_load_reg(cpu_V1, rd1); - gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); - tcg_temp_free_i32(tmp); + gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, + tcg_constant_i32((insn >> 20) & 3)); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -2358,9 +2371,8 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_const_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); + tmp = tcg_constant_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp); - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2833,7 +2845,7 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, tcg_gen_sextract_i32(tcg_el, tcg_el, ctz32(SCR_EEL2), 1); tcg_gen_addi_i32(tcg_el, tcg_el, 3); } else { - tcg_el = tcg_const_i32(3); + tcg_el = tcg_constant_i32(3); } gen_exception_el(s, EXCP_UDEF, syn_uncategorized(), tcg_el); @@ -2868,7 +2880,7 @@ undef: static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) { - TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno; + TCGv_i32 tcg_reg; int tgtmode = 0, regno = 0; if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { @@ -2879,18 +2891,16 @@ static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); tcg_reg = load_reg(s, rn); - tcg_tgtmode = tcg_const_i32(tgtmode); - tcg_regno = tcg_const_i32(regno); - gen_helper_msr_banked(cpu_env, tcg_reg, tcg_tgtmode, tcg_regno); - tcg_temp_free_i32(tcg_tgtmode); - tcg_temp_free_i32(tcg_regno); + gen_helper_msr_banked(cpu_env, tcg_reg, + tcg_constant_i32(tgtmode), + tcg_constant_i32(regno)); tcg_temp_free_i32(tcg_reg); s->base.is_jmp = DISAS_UPDATE_EXIT; } static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) { - TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno; + TCGv_i32 tcg_reg; int tgtmode = 0, regno = 0; if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { @@ -2901,11 +2911,9 @@ static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); tcg_reg = tcg_temp_new_i32(); - tcg_tgtmode = tcg_const_i32(tgtmode); - tcg_regno = tcg_const_i32(regno); - gen_helper_mrs_banked(tcg_reg, cpu_env, tcg_tgtmode, tcg_regno); - tcg_temp_free_i32(tcg_tgtmode); - tcg_temp_free_i32(tcg_regno); + gen_helper_mrs_banked(tcg_reg, cpu_env, + tcg_constant_i32(tgtmode), + tcg_constant_i32(regno)); store_reg(s, rn, tcg_reg); s->base.is_jmp = DISAS_UPDATE_EXIT; } @@ -2988,9 +2996,8 @@ void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, } \ static void gen_##NAME##0_vec(unsigned vece, TCGv_vec d, TCGv_vec a) \ { \ - TCGv_vec zero = tcg_const_zeros_vec_matching(d); \ + TCGv_vec zero = tcg_constant_vec_matching(d, vece, 0); \ tcg_gen_cmp_vec(COND, vece, d, a, zero); \ - tcg_temp_free_vec(zero); \ } \ void gen_gvec_##NAME##0(unsigned vece, uint32_t d, uint32_t m, \ uint32_t opr_sz, uint32_t max_sz) \ @@ -3980,8 +3987,8 @@ void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) TCGv_i32 rval = tcg_temp_new_i32(); TCGv_i32 lsh = tcg_temp_new_i32(); TCGv_i32 rsh = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_const_i32(0); - TCGv_i32 max = tcg_const_i32(32); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 max = tcg_constant_i32(32); /* * Rely on the TCG guarantee that out of range shifts produce @@ -3999,8 +4006,6 @@ void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) tcg_temp_free_i32(rval); tcg_temp_free_i32(lsh); tcg_temp_free_i32(rsh); - tcg_temp_free_i32(zero); - tcg_temp_free_i32(max); } void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) @@ -4009,8 +4014,8 @@ void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) TCGv_i64 rval = tcg_temp_new_i64(); TCGv_i64 lsh = tcg_temp_new_i64(); TCGv_i64 rsh = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_const_i64(0); - TCGv_i64 max = tcg_const_i64(64); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 max = tcg_constant_i64(64); /* * Rely on the TCG guarantee that out of range shifts produce @@ -4028,8 +4033,6 @@ void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) tcg_temp_free_i64(rval); tcg_temp_free_i64(lsh); tcg_temp_free_i64(rsh); - tcg_temp_free_i64(zero); - tcg_temp_free_i64(max); } static void gen_ushl_vec(unsigned vece, TCGv_vec dst, @@ -4124,8 +4127,8 @@ void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) TCGv_i32 rval = tcg_temp_new_i32(); TCGv_i32 lsh = tcg_temp_new_i32(); TCGv_i32 rsh = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_const_i32(0); - TCGv_i32 max = tcg_const_i32(31); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 max = tcg_constant_i32(31); /* * Rely on the TCG guarantee that out of range shifts produce @@ -4144,8 +4147,6 @@ void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) tcg_temp_free_i32(rval); tcg_temp_free_i32(lsh); tcg_temp_free_i32(rsh); - tcg_temp_free_i32(zero); - tcg_temp_free_i32(max); } void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) @@ -4154,8 +4155,8 @@ void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) TCGv_i64 rval = tcg_temp_new_i64(); TCGv_i64 lsh = tcg_temp_new_i64(); TCGv_i64 rsh = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_const_i64(0); - TCGv_i64 max = tcg_const_i64(63); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 max = tcg_constant_i64(63); /* * Rely on the TCG guarantee that out of range shifts produce @@ -4174,8 +4175,6 @@ void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) tcg_temp_free_i64(rval); tcg_temp_free_i64(lsh); tcg_temp_free_i64(rsh); - tcg_temp_free_i64(zero); - tcg_temp_free_i64(max); } static void gen_sshl_vec(unsigned vece, TCGv_vec dst, @@ -4690,8 +4689,6 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, * Note that on XScale all cp0..c13 registers do an access check * call in order to handle c15_cpar. */ - TCGv_ptr tmpptr; - TCGv_i32 tcg_syn, tcg_isread; uint32_t syndrome; /* Note that since we are an implementation which takes an @@ -4734,14 +4731,10 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); - tmpptr = tcg_const_ptr(ri); - tcg_syn = tcg_const_i32(syndrome); - tcg_isread = tcg_const_i32(isread); - gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn, - tcg_isread); - tcg_temp_free_ptr(tmpptr); - tcg_temp_free_i32(tcg_syn); - tcg_temp_free_i32(tcg_isread); + gen_helper_access_check_cp_reg(cpu_env, + tcg_constant_ptr(ri), + tcg_constant_i32(syndrome), + tcg_constant_i32(isread)); } else if (ri->type & ARM_CP_RAISES_EXC) { /* * The readfn or writefn might raise an exception; @@ -4777,13 +4770,11 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, TCGv_i64 tmp64; TCGv_i32 tmp; if (ri->type & ARM_CP_CONST) { - tmp64 = tcg_const_i64(ri->resetvalue); + tmp64 = tcg_constant_i64(ri->resetvalue); } else if (ri->readfn) { - TCGv_ptr tmpptr; tmp64 = tcg_temp_new_i64(); - tmpptr = tcg_const_ptr(ri); - gen_helper_get_cp_reg64(tmp64, cpu_env, tmpptr); - tcg_temp_free_ptr(tmpptr); + gen_helper_get_cp_reg64(tmp64, cpu_env, + tcg_constant_ptr(ri)); } else { tmp64 = tcg_temp_new_i64(); tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset); @@ -4798,13 +4789,10 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, } else { TCGv_i32 tmp; if (ri->type & ARM_CP_CONST) { - tmp = tcg_const_i32(ri->resetvalue); + tmp = tcg_constant_i32(ri->resetvalue); } else if (ri->readfn) { - TCGv_ptr tmpptr; tmp = tcg_temp_new_i32(); - tmpptr = tcg_const_ptr(ri); - gen_helper_get_cp_reg(tmp, cpu_env, tmpptr); - tcg_temp_free_ptr(tmpptr); + gen_helper_get_cp_reg(tmp, cpu_env, tcg_constant_ptr(ri)); } else { tmp = load_cpu_offset(ri->fieldoffset); } @@ -4834,25 +4822,19 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, tcg_temp_free_i32(tmplo); tcg_temp_free_i32(tmphi); if (ri->writefn) { - TCGv_ptr tmpptr = tcg_const_ptr(ri); - gen_helper_set_cp_reg64(cpu_env, tmpptr, tmp64); - tcg_temp_free_ptr(tmpptr); + gen_helper_set_cp_reg64(cpu_env, tcg_constant_ptr(ri), + tmp64); } else { tcg_gen_st_i64(tmp64, cpu_env, ri->fieldoffset); } tcg_temp_free_i64(tmp64); } else { + TCGv_i32 tmp = load_reg(s, rt); if (ri->writefn) { - TCGv_i32 tmp; - TCGv_ptr tmpptr; - tmp = load_reg(s, rt); - tmpptr = tcg_const_ptr(ri); - gen_helper_set_cp_reg(cpu_env, tmpptr, tmp); - tcg_temp_free_ptr(tmpptr); + gen_helper_set_cp_reg(cpu_env, tcg_constant_ptr(ri), tmp); tcg_temp_free_i32(tmp); } else { - TCGv_i32 tmp = load_reg(s, rt); - store_cpu_offset(tmp, ri->fieldoffset); + store_cpu_offset(tmp, ri->fieldoffset, 4); } } } @@ -4866,17 +4848,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, * A write to any coprocessor register that ends a TB * must rebuild the hflags for the next TB. */ - TCGv_i32 tcg_el = tcg_const_i32(s->current_el); - if (arm_dc_feature(s, ARM_FEATURE_M)) { - gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); - } else { - if (ri->type & ARM_CP_NEWEL) { - gen_helper_rebuild_hflags_a32_newel(cpu_env); - } else { - gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); - } - } - tcg_temp_free_i32(tcg_el); + gen_rebuild_hflags(s, ri->type & ARM_CP_NEWEL); /* * We default to ending the TB on a coprocessor register write, * but allow this to be suppressed by the register definition @@ -5165,12 +5137,10 @@ static void gen_srs(DisasContext *s, } addr = tcg_temp_new_i32(); - tmp = tcg_const_i32(mode); /* get_r13_banked() will raise an exception if called from System mode */ gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); - gen_helper_get_r13_banked(addr, cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_get_r13_banked(addr, cpu_env, tcg_constant_i32(mode)); switch (amode) { case 0: /* DA */ offset = -4; @@ -5213,9 +5183,7 @@ static void gen_srs(DisasContext *s, abort(); } tcg_gen_addi_i32(addr, addr, offset); - tmp = tcg_const_i32(mode); - gen_helper_set_r13_banked(cpu_env, tmp, addr); - tcg_temp_free_i32(tmp); + gen_helper_set_r13_banked(cpu_env, tcg_constant_i32(mode), addr); } tcg_temp_free_i32(addr); s->base.is_jmp = DISAS_UPDATE_EXIT; @@ -5551,14 +5519,13 @@ static bool op_s_rri_rot(DisasContext *s, arg_s_rri_rot *a, void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32), int logic_cc, StoreRegKind kind) { - TCGv_i32 tmp1, tmp2; + TCGv_i32 tmp1; uint32_t imm; imm = ror32(a->imm, a->rot); if (logic_cc && a->rot) { tcg_gen_movi_i32(cpu_CF, imm >> 31); } - tmp2 = tcg_const_i32(imm); tmp1 = load_reg(s, a->rn); //// --- Begin LibAFL code --- @@ -5579,8 +5546,7 @@ static bool op_s_rri_rot(DisasContext *s, arg_s_rri_rot *a, //// --- End LibAFL code --- - gen(tmp1, tmp1, tmp2); - tcg_temp_free_i32(tmp2); + gen(tmp1, tmp1, tcg_constant_i32(imm)); if (logic_cc) { gen_logic_CC(tmp1); @@ -5599,9 +5565,10 @@ static bool op_s_rxi_rot(DisasContext *s, arg_s_rri_rot *a, if (logic_cc && a->rot) { tcg_gen_movi_i32(cpu_CF, imm >> 31); } - tmp = tcg_const_i32(imm); - gen(tmp, tmp); + tmp = tcg_temp_new_i32(); + gen(tmp, tcg_constant_i32(imm)); + if (logic_cc) { gen_logic_CC(tmp); } @@ -5727,14 +5694,11 @@ static bool trans_ADR(DisasContext *s, arg_ri *a) static bool trans_MOVW(DisasContext *s, arg_MOVW *a) { - TCGv_i32 tmp; - if (!ENABLE_ARCH_6T2) { return false; } - tmp = tcg_const_i32(a->imm); - store_reg(s, a->rd, tmp); + store_reg(s, a->rd, tcg_constant_i32(a->imm)); return true; } @@ -6105,14 +6069,13 @@ static bool trans_UMAAL(DisasContext *s, arg_UMAAL *a) t0 = load_reg(s, a->rm); t1 = load_reg(s, a->rn); tcg_gen_mulu2_i32(t0, t1, t0, t1); - zero = tcg_const_i32(0); + zero = tcg_constant_i32(0); t2 = load_reg(s, a->ra); tcg_gen_add2_i32(t0, t1, t0, t1, t2, zero); tcg_temp_free_i32(t2); t2 = load_reg(s, a->rd); tcg_gen_add2_i32(t0, t1, t0, t1, t2, zero); tcg_temp_free_i32(t2); - tcg_temp_free_i32(zero); store_reg(s, a->ra, t0); store_reg(s, a->rd, t1); return true; @@ -6359,14 +6322,13 @@ static bool op_crc32(DisasContext *s, arg_rrr *a, bool c, MemOp sz) default: g_assert_not_reached(); } - t3 = tcg_const_i32(1 << sz); + t3 = tcg_constant_i32(1 << sz); if (c) { gen_helper_crc32c(t1, t1, t2, t3); } else { gen_helper_crc32(t1, t1, t2, t3); } tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); store_reg(s, a->rd, t1); return true; } @@ -6449,8 +6411,8 @@ static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a) if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; } - tmp = tcg_const_i32(a->sysm); - gen_helper_v7m_mrs(tmp, cpu_env, tmp); + tmp = tcg_temp_new_i32(); + gen_helper_v7m_mrs(tmp, cpu_env, tcg_constant_i32(a->sysm)); store_reg(s, a->rd, tmp); return true; } @@ -6462,13 +6424,12 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; } - addr = tcg_const_i32((a->mask << 10) | a->sysm); + addr = tcg_constant_i32((a->mask << 10) | a->sysm); reg = load_reg(s, a->rn); gen_helper_v7m_msr(cpu_env, addr, reg); - tcg_temp_free_i32(addr); tcg_temp_free_i32(reg); /* If we wrote to CONTROL, the EL might have changed */ - gen_helper_rebuild_hflags_m32_newel(cpu_env); + gen_rebuild_hflags(s, true); gen_lookup_tb(s); return true; } @@ -6677,8 +6638,8 @@ static bool trans_TT(DisasContext *s, arg_TT *a) } addr = load_reg(s, a->rn); - tmp = tcg_const_i32((a->A << 1) | a->T); - gen_helper_v7m_tt(tmp, cpu_env, addr, tmp); + tmp = tcg_temp_new_i32(); + gen_helper_v7m_tt(tmp, cpu_env, addr, tcg_constant_i32((a->A << 1) | a->T)); tcg_temp_free_i32(addr); store_reg(s, a->rd, tmp); return true; @@ -7645,7 +7606,7 @@ static bool trans_PKH(DisasContext *s, arg_PKH *a) static bool op_sat(DisasContext *s, arg_sat *a, void (*gen)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) { - TCGv_i32 tmp, satimm; + TCGv_i32 tmp; int shift = a->imm; if (!ENABLE_ARCH_6) { @@ -7659,9 +7620,7 @@ static bool op_sat(DisasContext *s, arg_sat *a, tcg_gen_shli_i32(tmp, tmp, shift); } - satimm = tcg_const_i32(a->satimm); - gen(tmp, cpu_env, tmp, satimm); - tcg_temp_free_i32(satimm); + gen(tmp, cpu_env, tmp, tcg_constant_i32(a->satimm)); store_reg(s, a->rd, tmp); return true; @@ -7996,9 +7955,7 @@ static bool op_smmla(DisasContext *s, arg_rrrr *a, bool round, bool sub) * a non-zero multiplicand lowpart, and the correct result * lowpart for rounding. */ - TCGv_i32 zero = tcg_const_i32(0); - tcg_gen_sub2_i32(t2, t1, zero, t3, t2, t1); - tcg_temp_free_i32(zero); + tcg_gen_sub2_i32(t2, t1, tcg_constant_i32(0), t3, t2, t1); } else { tcg_gen_add_i32(t1, t1, t3); } @@ -8135,7 +8092,7 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) { int i, j, n, list, mem_idx; bool user = a->u; - TCGv_i32 addr, tmp, tmp2; + TCGv_i32 addr, tmp; if (user) { /* STM (user) */ @@ -8165,9 +8122,7 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) if (user && i != 15) { tmp = tcg_temp_new_i32(); - tmp2 = tcg_const_i32(i); - gen_helper_get_user_reg(tmp, cpu_env, tmp2); - tcg_temp_free_i32(tmp2); + gen_helper_get_user_reg(tmp, cpu_env, tcg_constant_i32(i)); } else { tmp = load_reg(s, i); } @@ -8208,7 +8163,7 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) bool loaded_base; bool user = a->u; bool exc_return = false; - TCGv_i32 addr, tmp, tmp2, loaded_var; + TCGv_i32 addr, tmp, loaded_var; if (user) { /* LDM (user), LDM (exception return) */ @@ -8251,9 +8206,7 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) tmp = tcg_temp_new_i32(); gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); if (user) { - tmp2 = tcg_const_i32(i); - gen_helper_set_user_reg(cpu_env, tmp2, tmp); - tcg_temp_free_i32(tmp2); + gen_helper_set_user_reg(cpu_env, tcg_constant_i32(i), tmp); tcg_temp_free_i32(tmp); } else if (i == a->rn) { loaded_var = tmp; @@ -8346,7 +8299,7 @@ static bool trans_CLRM(DisasContext *s, arg_CLRM *a) s->eci_handled = true; - zero = tcg_const_i32(0); + zero = tcg_constant_i32(0); for (i = 0; i < 15; i++) { if (extract32(a->list, i, 1)) { /* Clear R[i] */ @@ -8358,11 +8311,8 @@ static bool trans_CLRM(DisasContext *s, arg_CLRM *a) * Clear APSR (by calling the MSR helper with the same argument * as for "MSR APSR_nzcvqg, Rn": mask = 0b1100, SYSM=0) */ - TCGv_i32 maskreg = tcg_const_i32(0xc << 8); - gen_helper_v7m_msr(cpu_env, maskreg, zero); - tcg_temp_free_i32(maskreg); + gen_helper_v7m_msr(cpu_env, tcg_constant_i32(0xc00), zero); } - tcg_temp_free_i32(zero); clear_eci_state(s); return true; } @@ -8505,8 +8455,7 @@ static bool trans_DLS(DisasContext *s, arg_DLS *a) store_reg(s, 14, tmp); if (a->size != 4) { /* DLSTP: set FPSCR.LTPSIZE */ - tmp = tcg_const_i32(a->size); - store_cpu_field(tmp, v7m.ltpsize); + store_cpu_field(tcg_constant_i32(a->size), v7m.ltpsize); s->base.is_jmp = DISAS_UPDATE_NOCHAIN; } return true; @@ -8571,8 +8520,7 @@ static bool trans_WLS(DisasContext *s, arg_WLS *a) */ bool ok = vfp_access_check(s); assert(ok); - tmp = tcg_const_i32(a->size); - store_cpu_field(tmp, v7m.ltpsize); + store_cpu_field(tcg_constant_i32(a->size), v7m.ltpsize); /* * LTPSIZE updated, but MVE_NO_PRED will always be the same thing (0) * when we take this upcoming exit from this TB, so gen_jmp_tb() is OK. @@ -8698,8 +8646,7 @@ static bool trans_LE(DisasContext *s, arg_LE *a) gen_set_label(loopend); if (a->tp) { /* Exits from tail-pred loops must reset LTPSIZE to 4 */ - tmp = tcg_const_i32(4); - store_cpu_field(tmp, v7m.ltpsize); + store_cpu_field(tcg_constant_i32(4), v7m.ltpsize); } /* End TB, continuing to following insn */ gen_jmp_tb(s, s->base.pc_next, 1); @@ -8920,7 +8867,7 @@ static bool trans_CPS(DisasContext *s, arg_CPS *a) static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) { - TCGv_i32 tmp, addr, el; + TCGv_i32 tmp, addr; if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; @@ -8930,23 +8877,18 @@ static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) return true; } - tmp = tcg_const_i32(a->im); + tmp = tcg_constant_i32(a->im); /* FAULTMASK */ if (a->F) { - addr = tcg_const_i32(19); + addr = tcg_constant_i32(19); gen_helper_v7m_msr(cpu_env, addr, tmp); - tcg_temp_free_i32(addr); } /* PRIMASK */ if (a->I) { - addr = tcg_const_i32(16); + addr = tcg_constant_i32(16); gen_helper_v7m_msr(cpu_env, addr, tmp); - tcg_temp_free_i32(addr); } - el = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_m32(cpu_env, el); - tcg_temp_free_i32(el); - tcg_temp_free_i32(tmp); + gen_rebuild_hflags(s, false); gen_lookup_tb(s); return true; } @@ -9082,13 +9024,14 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a) } /* In this insn input reg fields of 0b1111 mean "zero", not "PC" */ + zero = tcg_constant_i32(0); if (a->rn == 15) { - rn = tcg_const_i32(0); + rn = zero; } else { rn = load_reg(s, a->rn); } if (a->rm == 15) { - rm = tcg_const_i32(0); + rm = zero; } else { rm = load_reg(s, a->rm); } @@ -9110,10 +9053,8 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a) } arm_test_cc(&c, a->fcond); - zero = tcg_const_i32(0); tcg_gen_movcond_i32(c.cond, rn, c.value, zero, rn, rm); arm_free_cc(&c); - tcg_temp_free_i32(zero); store_reg(s, a->rd, rn); tcg_temp_free_i32(rm); @@ -9376,7 +9317,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->isar = &cpu->isar; dc->condjmp = 0; - dc->aarch64 = 0; + dc->aarch64 = false; /* If we are coming from secure EL0 in a system with a 32-bit EL3, then * there is no secure EL1, so we route exceptions to EL3. */ @@ -9889,18 +9830,14 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* nothing more to generate */ break; case DISAS_WFI: - { - TCGv_i32 tmp = tcg_const_i32((dc->thumb && - !(dc->insn & (1U << 31))) ? 2 : 4); - - gen_helper_wfi(cpu_env, tmp); - tcg_temp_free_i32(tmp); - /* The helper doesn't necessarily throw an exception, but we + gen_helper_wfi(cpu_env, + tcg_constant_i32(dc->base.pc_next - dc->pc_curr)); + /* + * The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. */ tcg_gen_exit_tb(NULL, 0); break; - } case DISAS_WFE: gen_helper_wfe(cpu_env); break; @@ -9933,12 +9870,13 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void arm_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void arm_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { DisasContext *dc = container_of(dcbase, DisasContext, base); - qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); + target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); } static const TranslatorOps arm_translator_ops = { diff --git a/target/arm/translate.h b/target/arm/translate.h index 3a0db801d3..6f0ebdc88e 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -30,7 +30,6 @@ typedef struct DisasContext { bool eci_handled; /* TCG op to rewind to if this turns out to be an invalid ECI state */ TCGOp *insn_eci_rewind; - int thumb; int sctlr_b; MemOp be_data; #if !defined(CONFIG_USER_ONLY) @@ -59,12 +58,13 @@ typedef struct DisasContext { * so that top level loop can generate correct syndrome information. */ uint32_t svc_imm; - int aarch64; int current_el; /* Debug target exception level for single-step exceptions */ int debug_target_el; GHashTable *cp_regs; uint64_t features; /* CPU features bits */ + bool aarch64; + bool thumb; /* Because unallocated encodings generate different exception syndrome * information from traps due to FP being disabled, we can't do a single * "is fp access disabled" check at a high level in the decode tree. @@ -332,16 +332,9 @@ static inline void gen_ss_advance(DisasContext *s) static inline void gen_exception(int excp, uint32_t syndrome, uint32_t target_el) { - TCGv_i32 tcg_excp = tcg_const_i32(excp); - TCGv_i32 tcg_syn = tcg_const_i32(syndrome); - TCGv_i32 tcg_el = tcg_const_i32(target_el); - - gen_helper_exception_with_syndrome(cpu_env, tcg_excp, - tcg_syn, tcg_el); - - tcg_temp_free_i32(tcg_el); - tcg_temp_free_i32(tcg_syn); - tcg_temp_free_i32(tcg_excp); + gen_helper_exception_with_syndrome(cpu_env, tcg_constant_i32(excp), + tcg_constant_i32(syndrome), + tcg_constant_i32(target_el)); } /* Generate an architectural singlestep exception */ diff --git a/target/arm/vec_internal.h b/target/arm/vec_internal.h index 2a33558290..fb43a2380e 100644 --- a/target/arm/vec_internal.h +++ b/target/arm/vec_internal.h @@ -29,7 +29,7 @@ * The H1_ macros are used when performing byte arithmetic and then * casting the final pointer to a type of size N. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define H1(x) ((x) ^ 7) #define H1_2(x) ((x) ^ 6) #define H1_4(x) ((x) ^ 4) diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h index 14e5b3ce72..32a1c762e6 100644 --- a/target/avr/cpu-qom.h +++ b/target/avr/cpu-qom.h @@ -26,8 +26,7 @@ #define TYPE_AVR_CPU "avr-cpu" -OBJECT_DECLARE_TYPE(AVRCPU, AVRCPUClass, - AVR_CPU) +OBJECT_DECLARE_CPU_TYPE(AVRCPU, AVRCPUClass, AVR_CPU) /** * AVRCPUClass: diff --git a/target/avr/cpu.h b/target/avr/cpu.h index dceacf3cd7..55497f851d 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -108,9 +108,7 @@ typedef enum AVRFeature { AVR_FEATURE_RAMPZ, } AVRFeature; -typedef struct CPUAVRState CPUAVRState; - -struct CPUAVRState { +typedef struct CPUArchState { uint32_t pc_w; /* 0x003fffff up to 22 bits */ uint32_t sregC; /* 0x00000001 1 bit */ @@ -137,7 +135,7 @@ struct CPUAVRState { bool fullacc; /* CPU/MEM if true MEM only otherwise */ uint64_t features; -}; +} CPUAVRState; /** * AVRCPU: @@ -145,14 +143,14 @@ struct CPUAVRState { * * A AVR CPU. */ -typedef struct AVRCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ CPUNegativeOffsetState neg; CPUAVRState env; -} AVRCPU; +}; extern const struct VMStateDescription vms_avr_cpu; @@ -247,9 +245,6 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -typedef CPUAVRState CPUArchState; -typedef AVRCPU ArchCPU; - #include "exec/cpu-all.h" #endif /* !defined (QEMU_AVR_CPU_H) */ diff --git a/target/avr/translate.c b/target/avr/translate.c index af8a3e0f9c..dc9c3d6bcc 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -3015,10 +3015,11 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void avr_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void avr_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps avr_tr_ops = { diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h index 2596edc7e3..71e8af0e70 100644 --- a/target/cris/cpu-qom.h +++ b/target/cris/cpu-qom.h @@ -25,8 +25,7 @@ #define TYPE_CRIS_CPU "cris-cpu" -OBJECT_DECLARE_TYPE(CRISCPU, CRISCPUClass, - CRIS_CPU) +OBJECT_DECLARE_CPU_TYPE(CRISCPU, CRISCPUClass, CRIS_CPU) /** * CRISCPUClass: diff --git a/target/cris/cpu.h b/target/cris/cpu.h index b445b194ea..e6776f25b1 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -105,7 +105,7 @@ typedef struct { uint32_t lo; } TLBSet; -typedef struct CPUCRISState { +typedef struct CPUArchState { uint32_t regs[16]; /* P0 - P15 are referred to as special registers in the docs. */ uint32_t pregs[16]; @@ -173,7 +173,7 @@ typedef struct CPUCRISState { * * A CRIS CPU. */ -struct CRISCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -265,9 +265,6 @@ static inline int cpu_mmu_index (CPUCRISState *env, bool ifetch) #define SFR_RW_MM_TLB_LO env->pregs[PR_SRS]][5 #define SFR_RW_MM_TLB_HI env->pregs[PR_SRS]][6 -typedef CPUCRISState CPUArchState; -typedef CRISCPU ArchCPU; - #include "exec/cpu-all.h" static inline void cpu_get_tb_cpu_state(CPUCRISState *env, target_ulong *pc, diff --git a/target/cris/translate.c b/target/cris/translate.c index 3656cd6db1..ac101344a3 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -3268,11 +3268,12 @@ static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void cris_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void cris_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { if (!DISAS_CRIS) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } } diff --git a/target/hexagon/arch.c b/target/hexagon/arch.c index 68a55b3bd4..da79b41c4d 100644 --- a/target/hexagon/arch.c +++ b/target/hexagon/arch.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -298,8 +298,8 @@ int arch_sf_recip_common(float32 *Rs, float32 *Rt, float32 *Rd, int *adjust, } else { PeV = 0x00; /* Basic checks passed */ - n_exp = float32_getexp(RsV); - d_exp = float32_getexp(RtV); + n_exp = float32_getexp_raw(RsV); + d_exp = float32_getexp_raw(RtV); if ((n_exp - d_exp + SF_BIAS) <= SF_MANTBITS) { /* Near quotient underflow / inexact Q */ PeV = 0x80; diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 58a0d3870b..2a65a57bab 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,14 +18,13 @@ #ifndef HEXAGON_CPU_H #define HEXAGON_CPU_H -/* Forward declaration needed by some of the header files */ -typedef struct CPUHexagonState CPUHexagonState; - #include "fpu/softfloat-types.h" #include "exec/cpu-defs.h" #include "hex_regs.h" #include "mmvec/mmvec.h" +#include "qom/object.h" +#include "hw/core/cpu.h" #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -75,7 +74,7 @@ typedef struct { /* Maximum number of vector temps in a packet */ #define VECTOR_TEMPS_MAX 4 -struct CPUHexagonState { +typedef struct CPUArchState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; target_ulong branch_taken; @@ -129,14 +128,9 @@ struct CPUHexagonState { target_ulong vstore_pending[VSTORES_MAX]; bool vtcm_pending; VTCMStoreLog vtcm_log; -}; +} CPUHexagonState; -#define HEXAGON_CPU_CLASS(klass) \ - OBJECT_CLASS_CHECK(HexagonCPUClass, (klass), TYPE_HEXAGON_CPU) -#define HEXAGON_CPU(obj) \ - OBJECT_CHECK(HexagonCPU, (obj), TYPE_HEXAGON_CPU) -#define HEXAGON_CPU_GET_CLASS(obj) \ - OBJECT_GET_CLASS(HexagonCPUClass, (obj), TYPE_HEXAGON_CPU) +OBJECT_DECLARE_CPU_TYPE(HexagonCPU, HexagonCPUClass, HEXAGON_CPU) typedef struct HexagonCPUClass { /*< private >*/ @@ -146,7 +140,7 @@ typedef struct HexagonCPUClass { DeviceReset parent_reset; } HexagonCPUClass; -typedef struct HexagonCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -155,7 +149,7 @@ typedef struct HexagonCPU { bool lldb_compat; target_ulong lldb_stack_adjust; -} HexagonCPU; +}; #include "cpu_bits.h" @@ -180,7 +174,6 @@ static inline int cpu_mmu_index(CPUHexagonState *env, bool ifetch) #endif } -typedef struct CPUHexagonState CPUArchState; typedef HexagonCPU ArchCPU; void hexagon_translate_init(void); diff --git a/target/hexagon/fma_emu.h b/target/hexagon/fma_emu.h index e3b99a8cf4..91591d6050 100644 --- a/target/hexagon/fma_emu.h +++ b/target/hexagon/fma_emu.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,10 @@ static inline bool is_finite(float64 x) } int32_t float64_getexp(float64 f64); +static inline uint32_t float32_getexp_raw(float32 f32) +{ + return extract32(f32, 23, 8); +} int32_t float32_getexp(float32 f32); float32 infinite_float32(uint8_t sign); float32 internal_fmafx(float32 a, float32 b, float32 c, diff --git a/target/hexagon/gdbstub.c b/target/hexagon/gdbstub.c index 9c8c04c961..d152d01bfe 100644 --- a/target/hexagon/gdbstub.c +++ b/target/hexagon/gdbstub.c @@ -16,7 +16,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "exec/gdbstub.h" #include "cpu.h" #include "internal.h" diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 4419d30e23..cd6af4bceb 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -210,11 +210,15 @@ static inline void gen_read_ctrl_reg_pair(DisasContext *ctx, const int reg_num, } } -static inline void gen_write_p3_0(TCGv control_reg) +static void gen_write_p3_0(DisasContext *ctx, TCGv control_reg) { + TCGv hex_p8 = tcg_temp_new(); for (int i = 0; i < NUM_PREGS; i++) { - tcg_gen_extract_tl(hex_pred[i], control_reg, i * 8, 8); + tcg_gen_extract_tl(hex_p8, control_reg, i * 8, 8); + gen_log_pred_write(ctx, i, hex_p8); + ctx_log_pred_write(ctx, i); } + tcg_temp_free(hex_p8); } /* @@ -228,7 +232,7 @@ static inline void gen_write_ctrl_reg(DisasContext *ctx, int reg_num, TCGv val) { if (reg_num == HEX_REG_P3_0) { - gen_write_p3_0(val); + gen_write_p3_0(ctx, val); } else { gen_log_reg_write(reg_num, val); ctx_log_reg_write(ctx, reg_num); @@ -250,7 +254,7 @@ static inline void gen_write_ctrl_reg_pair(DisasContext *ctx, int reg_num, if (reg_num == HEX_REG_P3_0) { TCGv val32 = tcg_temp_new(); tcg_gen_extrl_i64_i32(val32, val); - gen_write_p3_0(val32); + gen_write_p3_0(ctx, val32); tcg_gen_extrh_i64_i32(val32, val); gen_log_reg_write(reg_num + 1, val32); tcg_temp_free(val32); diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 19d103cad5..a78e84faa4 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -268,7 +268,7 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) #define fVSATUVALN(N, VAL) \ ({ \ - (((int)(VAL)) < 0) ? 0 : ((1LL << (N)) - 1); \ + (((int64_t)(VAL)) < 0) ? 0 : ((1LL << (N)) - 1); \ }) #define fSATUVALN(N, VAL) \ ({ \ diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index 10f4630364..8345753580 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -164,11 +164,9 @@ target_ulong va = EA; \ target_ulong va_high = EA + LEN; \ uintptr_t ra = GETPC(); \ - int log_bank = 0; \ int log_byte = 0; \ for (i0 = 0; i0 < ELEMENT_SIZE; i0++) { \ log_byte = ((va + i0) <= va_high) && QVAL; \ - log_bank |= (log_byte << i0); \ uint8_t B; \ B = cpu_ldub_data_ra(env, EA + i0, ra); \ env->tmp_VRegs[0].ub[ELEMENT_SIZE * IDX + i0] = B; \ @@ -243,11 +241,9 @@ int i0; \ target_ulong va = EA; \ target_ulong va_high = EA + LEN; \ - int log_bank = 0; \ int log_byte = 0; \ for (i0 = 0; i0 < ELEM_SIZE; i0++) { \ log_byte = ((va + i0) <= va_high) && QVAL; \ - log_bank |= (log_byte << i0); \ LOG_VTCM_BYTE(va + i0, log_byte, IN.ub[ELEM_SIZE * IDX + i0], \ ELEM_SIZE * IDX + i0); \ } \ diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 057baf9a48..a5ed819c04 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,9 +34,10 @@ #define SF_MANTBITS 23 /* Exceptions processing helpers */ -static void QEMU_NORETURN do_raise_exception_err(CPUHexagonState *env, - uint32_t exception, - uintptr_t pc) +static G_NORETURN +void do_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc) { CPUState *cs = env_cpu(env); qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); @@ -44,7 +45,7 @@ static void QEMU_NORETURN do_raise_exception_err(CPUHexagonState *env, cpu_loop_exit_restore(cs, pc); } -void QEMU_NORETURN HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) +G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) { do_raise_exception_err(env, excp, 0); } @@ -304,8 +305,8 @@ void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1) int32_t HELPER(fcircadd)(int32_t RxV, int32_t offset, int32_t M, int32_t CS) { - int32_t K_const = sextract32(M, 24, 4); - int32_t length = sextract32(M, 0, 17); + uint32_t K_const = extract32(M, 24, 4); + uint32_t length = extract32(M, 0, 17); uint32_t new_ptr = RxV + offset; uint32_t start_addr; uint32_t end_addr; @@ -829,7 +830,7 @@ uint32_t HELPER(conv_df2uw_chop)(CPUHexagonState *env, float64 RssV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float32_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -938,8 +939,7 @@ int32_t HELPER(sfcmpuo)(CPUHexagonState *env, float32 RsV, float32 RtV) { int32_t PdV; arch_fpop_start(env); - PdV = f8BITSOF(float32_is_any_nan(RsV) || - float32_is_any_nan(RtV)); + PdV = f8BITSOF(float32_unordered_quiet(RsV, RtV, &env->fp_status)); arch_fpop_end(env); return PdV; } @@ -948,7 +948,7 @@ float32 HELPER(sfmax)(CPUHexagonState *env, float32 RsV, float32 RtV) { float32 RdV; arch_fpop_start(env); - RdV = float32_maxnum(RsV, RtV, &env->fp_status); + RdV = float32_maximum_number(RsV, RtV, &env->fp_status); arch_fpop_end(env); return RdV; } @@ -957,7 +957,7 @@ float32 HELPER(sfmin)(CPUHexagonState *env, float32 RsV, float32 RtV) { float32 RdV; arch_fpop_start(env); - RdV = float32_minnum(RsV, RtV, &env->fp_status); + RdV = float32_minimum_number(RsV, RtV, &env->fp_status); arch_fpop_end(env); return RdV; } @@ -1041,10 +1041,7 @@ float64 HELPER(dfmax)(CPUHexagonState *env, float64 RssV, float64 RttV) { float64 RddV; arch_fpop_start(env); - RddV = float64_maxnum(RssV, RttV, &env->fp_status); - if (float64_is_any_nan(RssV) || float64_is_any_nan(RttV)) { - float_raise(float_flag_invalid, &env->fp_status); - } + RddV = float64_maximum_number(RssV, RttV, &env->fp_status); arch_fpop_end(env); return RddV; } @@ -1053,10 +1050,7 @@ float64 HELPER(dfmin)(CPUHexagonState *env, float64 RssV, float64 RttV) { float64 RddV; arch_fpop_start(env); - RddV = float64_minnum(RssV, RttV, &env->fp_status); - if (float64_is_any_nan(RssV) || float64_is_any_nan(RttV)) { - float_raise(float_flag_invalid, &env->fp_status); - } + RddV = float64_minimum_number(RssV, RttV, &env->fp_status); arch_fpop_end(env); return RddV; } @@ -1097,8 +1091,7 @@ int32_t HELPER(dfcmpuo)(CPUHexagonState *env, float64 RssV, float64 RttV) { int32_t PdV; arch_fpop_start(env); - PdV = f8BITSOF(float64_is_any_nan(RssV) || - float64_is_any_nan(RttV)); + PdV = f8BITSOF(float64_unordered_quiet(RssV, RttV, &env->fp_status)); arch_fpop_end(env); return PdV; } diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index b6f541ecb2..d4fc92f7e9 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -833,10 +833,11 @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void hexagon_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void hexagon_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } @@ -874,12 +875,6 @@ void hexagon_translate_init(void) opcode_init(); - if (HEX_DEBUG) { - if (!qemu_logfile) { - qemu_set_log(qemu_loglevel); - } - } - for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { hex_gpr[i] = tcg_global_mem_new(cpu_env, offsetof(CPUHexagonState, gpr[i]), diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index d424f88370..b96e0318c7 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -25,8 +25,7 @@ #define TYPE_HPPA_CPU "hppa-cpu" -OBJECT_DECLARE_TYPE(HPPACPU, HPPACPUClass, - HPPA_CPU) +OBJECT_DECLARE_CPU_TYPE(HPPACPU, HPPACPUClass, HPPA_CPU) /** * HPPACPUClass: diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 5f46ba801e..a6f52caf14 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -73,10 +73,10 @@ static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info) } #ifndef CONFIG_USER_ONLY -static void QEMU_NORETURN -hppa_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) +static G_NORETURN +void hppa_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 93c119532a..6f3b6beecf 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" /* PA-RISC 1.x processors have a strong memory model. */ /* ??? While we do not yet implement PA-RISC 2.0, those processors have @@ -138,8 +139,6 @@ #define CR_IPSW 22 #define CR_EIRR 23 -typedef struct CPUHPPAState CPUHPPAState; - #if TARGET_REGISTER_BITS == 32 typedef uint32_t target_ureg; typedef int32_t target_sreg; @@ -168,7 +167,7 @@ typedef struct { unsigned access_id : 16; } hppa_tlb_entry; -struct CPUHPPAState { +typedef struct CPUArchState { target_ureg gr[32]; uint64_t fr[32]; uint64_t sr[8]; /* stored shifted into place for gva */ @@ -207,7 +206,7 @@ struct CPUHPPAState { /* ??? We should use a more intelligent data structure. */ hppa_tlb_entry tlb[HPPA_TLB_ENTRIES]; uint32_t tlb_last; -}; +} CPUHPPAState; /** * HPPACPU: @@ -215,7 +214,7 @@ struct CPUHPPAState { * * An HPPA CPU. */ -struct HPPACPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -225,10 +224,6 @@ struct HPPACPU { QEMUTimer *alarm_timer; }; - -typedef CPUHPPAState CPUArchState; -typedef HPPACPU ArchCPU; - #include "exec/cpu-all.h" static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) @@ -344,6 +339,6 @@ extern const VMStateDescription vmstate_hppa_cpu; void hppa_cpu_alarm_timer(void *); int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr); #endif -void QEMU_NORETURN hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra); +G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra); #endif /* HPPA_CPU_H */ diff --git a/target/hppa/helper.h b/target/hppa/helper.h index fe8a9ce493..c7e35ce8c7 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,7 +1,9 @@ #if TARGET_REGISTER_BITS == 64 # define dh_alias_tr i64 +# define dh_typecode_tr dh_typecode_i64 #else # define dh_alias_tr i32 +# define dh_typecode_tr dh_typecode_i32 #endif #define dh_ctype_tr target_ureg diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 2810361be0..cd304f051e 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -28,7 +28,7 @@ #include "fpu/softfloat.h" #include "trace.h" -void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp) +G_NORETURN void HELPER(excp)(CPUHPPAState *env, int excp) { CPUState *cs = env_cpu(env); @@ -36,7 +36,7 @@ void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp) cpu_loop_exit(cs); } -void QEMU_NORETURN hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra) +G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra) { CPUState *cs = env_cpu(env); diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 5c0b1eb274..b8dbfee5e9 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -566,7 +566,7 @@ static void save_gpr(DisasContext *ctx, unsigned reg, TCGv_reg t) } } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN # define HI_OFS 0 # define LO_OFS 4 #else @@ -4305,29 +4305,30 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void hppa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void hppa_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { target_ulong pc = dcbase->pc_first; #ifdef CONFIG_USER_ONLY switch (pc) { case 0x00: - qemu_log("IN:\n0x00000000: (null)\n"); + fprintf(logfile, "IN:\n0x00000000: (null)\n"); return; case 0xb0: - qemu_log("IN:\n0x000000b0: light-weight-syscall\n"); + fprintf(logfile, "IN:\n0x000000b0: light-weight-syscall\n"); return; case 0xe0: - qemu_log("IN:\n0x000000e0: set-thread-pointer-syscall\n"); + fprintf(logfile, "IN:\n0x000000e0: set-thread-pointer-syscall\n"); return; case 0x100: - qemu_log("IN:\n0x00000100: syscall\n"); + fprintf(logfile, "IN:\n0x00000100: syscall\n"); return; } #endif - qemu_log("IN: %s\n", lookup_symbol(pc)); - log_target_disas(cs, pc, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(pc)); + target_disas(logfile, cs, pc, dcbase->tb->size); } static const TranslatorOps hppa_tr_ops = { diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index f9923cee04..c557a522e1 100644 --- a/target/i386/cpu-qom.h +++ b/target/i386/cpu-qom.h @@ -30,8 +30,7 @@ #define TYPE_X86_CPU "i386-cpu" #endif -OBJECT_DECLARE_TYPE(X86CPU, X86CPUClass, - X86_CPU) +OBJECT_DECLARE_CPU_TYPE(X86CPU, X86CPUClass, X86_CPU) typedef struct X86CPUModel X86CPUModel; diff --git a/target/i386/cpu-sysemu.c b/target/i386/cpu-sysemu.c index 37b7c562f5..e254d8ba10 100644 --- a/target/i386/cpu-sysemu.c +++ b/target/i386/cpu-sysemu.c @@ -313,7 +313,7 @@ GuestPanicInformation *x86_cpu_get_crash_info(CPUState *cs) GuestPanicInformation *panic_info = NULL; if (hyperv_feat_enabled(cpu, HYPERV_FEAT_CRASH)) { - panic_info = g_malloc0(sizeof(GuestPanicInformation)); + panic_info = g_new0(GuestPanicInformation, 1); panic_info->type = GUEST_PANIC_INFORMATION_TYPE_HYPER_V; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6c7ef1099b..99343be926 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -575,6 +575,18 @@ static CPUCacheInfo legacy_l3_cache = { #define INTEL_PT_CYCLE_BITMAP 0x1fff /* Support 0,2^(0~11) */ #define INTEL_PT_PSB_BITMAP (0x003f << 16) /* Support 2K,4K,8K,16K,32K,64K */ +/* CPUID Leaf 0x1D constants: */ +#define INTEL_AMX_TILE_MAX_SUBLEAF 0x1 +#define INTEL_AMX_TOTAL_TILE_BYTES 0x2000 +#define INTEL_AMX_BYTES_PER_TILE 0x400 +#define INTEL_AMX_BYTES_PER_ROW 0x40 +#define INTEL_AMX_TILE_MAX_NAMES 0x8 +#define INTEL_AMX_TILE_MAX_ROWS 0x10 + +/* CPUID Leaf 0x1E constants: */ +#define INTEL_AMX_TMUL_MAX_K 0x10 +#define INTEL_AMX_TMUL_MAX_N 0x40 + void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, uint32_t vendor2, uint32_t vendor3) { @@ -844,8 +856,8 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "avx512-vp2intersect", NULL, "md-clear", NULL, NULL, NULL, "serialize", NULL, "tsx-ldtrk", NULL, NULL /* pconfig */, NULL, - NULL, NULL, NULL, "avx512-fp16", - NULL, NULL, "spec-ctrl", "stibp", + NULL, NULL, "amx-bf16", "avx512-fp16", + "amx-tile", "amx-int8", "spec-ctrl", "stibp", NULL, "arch-capabilities", "core-capability", "ssbd", }, .cpuid = { @@ -910,7 +922,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .type = CPUID_FEATURE_WORD, .feat_names = { "xsaveopt", "xsavec", "xgetbv1", "xsaves", - NULL, NULL, NULL, NULL, + "xfd", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1402,6 +1414,14 @@ ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = { [XSTATE_PKRU_BIT] = { .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU, .size = sizeof(XSavePKRU) }, + [XSTATE_XTILE_CFG_BIT] = { + .feature = FEAT_7_0_EDX, .bits = CPUID_7_0_EDX_AMX_TILE, + .size = sizeof(XSaveXTILECFG), + }, + [XSTATE_XTILE_DATA_BIT] = { + .feature = FEAT_7_0_EDX, .bits = CPUID_7_0_EDX_AMX_TILE, + .size = sizeof(XSaveXTILEDATA) + }, }; static uint32_t xsave_area_size(uint64_t mask) @@ -3506,6 +3526,14 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } }, }, + { + .version = 6, + .note = "5-level EPT", + .props = (PropValue[]) { + { "vmx-page-walk-5", "on" }, + { /* end of list */ } + }, + }, { /* end of list */ } } }, @@ -4953,8 +4981,8 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } -static uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, - bool migratable_only) +uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, + bool migratable_only) { FeatureWordInfo *wi = &feature_word_info[w]; uint64_t r = 0; @@ -5488,6 +5516,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, const ExtSaveArea *esa = &x86_ext_save_areas[count]; *eax = esa->size; *ebx = esa->offset; + *ecx = esa->ecx & + (ESA_FEATURE_ALIGN64_MASK | ESA_FEATURE_XFD_MASK); } } break; @@ -5576,6 +5606,43 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x1D: { + /* AMX TILE */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + if (!(env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_AMX_TILE)) { + break; + } + + if (count == 0) { + /* Highest numbered palette subleaf */ + *eax = INTEL_AMX_TILE_MAX_SUBLEAF; + } else if (count == 1) { + *eax = INTEL_AMX_TOTAL_TILE_BYTES | + (INTEL_AMX_BYTES_PER_TILE << 16); + *ebx = INTEL_AMX_BYTES_PER_ROW | (INTEL_AMX_TILE_MAX_NAMES << 16); + *ecx = INTEL_AMX_TILE_MAX_ROWS; + } + break; + } + case 0x1E: { + /* AMX TMUL */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + if (!(env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_AMX_TILE)) { + break; + } + + if (count == 0) { + /* Highest numbered palette subleaf */ + *ebx = INTEL_AMX_TMUL_MAX_K | (INTEL_AMX_TMUL_MAX_N << 8); + } + break; + } case 0x40000000: /* * CPUID code in kvm_arch_init_vcpu() ignores stuff @@ -5864,6 +5931,19 @@ static void x86_cpu_reset(DeviceState *dev) env->xstate_bv = 0; env->pat = 0x0007040600070406ULL; + + if (kvm_enabled()) { + /* + * KVM handles TSC = 0 specially and thinks we are hot-plugging + * a new CPU, use 1 instead to force a reset. + */ + if (env->tsc != 0) { + env->tsc = 1; + } + } else { + env->tsc = 0; + } + env->msr_ia32_misc_enable = MSR_IA32_MISC_ENABLE_DEFAULT; if (env->features[FEAT_1_ECX] & CPUID_EXT_MONITOR) { env->msr_ia32_misc_enable |= MSR_IA32_MISC_ENABLE_MWAIT; @@ -5930,9 +6010,7 @@ static void x86_cpu_reset(DeviceState *dev) x86_cpu_set_sgxlepubkeyhash(env); - if (env->features[FEAT_SVM] & CPUID_SVM_TSCSCALE) { - env->amd_tsc_scale_msr = MSR_AMD64_TSC_RATIO_DEFAULT; - } + env->amd_tsc_scale_msr = MSR_AMD64_TSC_RATIO_DEFAULT; #endif } @@ -5998,6 +6076,7 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) CPUX86State *env = &cpu->env; int i; uint64_t mask; + static bool request_perm; if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { env->features[FEAT_XSAVE_COMP_LO] = 0; @@ -6013,6 +6092,12 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) } } + /* Only request permission for first vcpu */ + if (kvm_enabled() && !request_perm) { + kvm_request_xsave_components(cpu, mask); + request_perm = true; + } + env->features[FEAT_XSAVE_COMP_LO] = mask; env->features[FEAT_XSAVE_COMP_HI] = mask >> 32; } @@ -6842,6 +6927,8 @@ static Property x86_cpu_properties[] = { HYPERV_FEAT_AVIC, 0), DEFINE_PROP_ON_OFF_AUTO("hv-no-nonarch-coresharing", X86CPU, hyperv_no_nonarch_cs, ON_OFF_AUTO_OFF), + DEFINE_PROP_BIT64("hv-syndbg", X86CPU, hyperv_features, + HYPERV_FEAT_SYNDBG, 0), DEFINE_PROP_BOOL("hv-passthrough", X86CPU, hyperv_passthrough, false), DEFINE_PROP_BOOL("hv-enforce-cpuid", X86CPU, hyperv_enforce_cpuid, false), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e69ab5dd78..9661f9fbd1 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -25,6 +25,7 @@ #include "kvm/hyperv-proto.h" #include "exec/cpu-defs.h" #include "qapi/qapi-types-common.h" +#include "qemu/cpu-float.h" /* The x86 has a strong memory model with some store-after-load re-ordering */ #define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) @@ -507,6 +508,9 @@ typedef enum X86Seg { #define MSR_VM_HSAVE_PA 0xc0010117 +#define MSR_IA32_XFD 0x000001c4 +#define MSR_IA32_XFD_ERR 0x000001c5 + #define MSR_IA32_BNDCFGS 0x00000d90 #define MSR_IA32_XSS 0x00000da0 #define MSR_IA32_UMWAIT_CONTROL 0xe1 @@ -539,6 +543,8 @@ typedef enum X86Seg { #define XSTATE_ZMM_Hi256_BIT 6 #define XSTATE_Hi16_ZMM_BIT 7 #define XSTATE_PKRU_BIT 9 +#define XSTATE_XTILE_CFG_BIT 17 +#define XSTATE_XTILE_DATA_BIT 18 #define XSTATE_FP_MASK (1ULL << XSTATE_FP_BIT) #define XSTATE_SSE_MASK (1ULL << XSTATE_SSE_BIT) @@ -549,6 +555,17 @@ typedef enum X86Seg { #define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT) #define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT) #define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT) +#define XSTATE_XTILE_CFG_MASK (1ULL << XSTATE_XTILE_CFG_BIT) +#define XSTATE_XTILE_DATA_MASK (1ULL << XSTATE_XTILE_DATA_BIT) + +#define XSTATE_DYNAMIC_MASK (XSTATE_XTILE_DATA_MASK) + +#define ESA_FEATURE_ALIGN64_BIT 1 +#define ESA_FEATURE_XFD_BIT 2 + +#define ESA_FEATURE_ALIGN64_MASK (1U << ESA_FEATURE_ALIGN64_BIT) +#define ESA_FEATURE_XFD_MASK (1U << ESA_FEATURE_XFD_BIT) + /* CPUID feature words */ typedef enum FeatureWord { @@ -590,6 +607,8 @@ typedef enum FeatureWord { } FeatureWord; typedef uint64_t FeatureWordArray[FEATURE_WORDS]; +uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, + bool migratable_only); /* cpuid_features bits */ #define CPUID_FP87 (1U << 0) @@ -842,6 +861,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EDX_TSX_LDTRK (1U << 16) /* AVX512_FP16 instruction */ #define CPUID_7_0_EDX_AVX512_FP16 (1U << 23) +/* AMX tile (two-dimensional register) */ +#define CPUID_7_0_EDX_AMX_TILE (1U << 24) /* Speculation Control */ #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Single Thread Indirect Branch Predictors */ @@ -857,6 +878,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_1_EAX_AVX_VNNI (1U << 4) /* AVX512 BFloat16 Instruction */ #define CPUID_7_1_EAX_AVX512_BF16 (1U << 5) +/* XFD Extend Feature Disabled */ +#define CPUID_D_1_EAX_XFD (1U << 4) /* Packets which contain IP payload have LIP values */ #define CPUID_14_0_ECX_LIP (1U << 31) @@ -1062,6 +1085,7 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define HYPERV_FEAT_IPI 13 #define HYPERV_FEAT_STIMER_DIRECT 14 #define HYPERV_FEAT_AVIC 15 +#define HYPERV_FEAT_SYNDBG 16 #ifndef HYPERV_SPINLOCK_NEVER_NOTIFY #define HYPERV_SPINLOCK_NEVER_NOTIFY 0xFFFFFFFF @@ -1195,20 +1219,6 @@ typedef struct SegmentCache { float64 _d_##n[(bits)/64]; \ } -typedef union { - uint8_t _b[16]; - uint16_t _w[8]; - uint32_t _l[4]; - uint64_t _q[2]; -} XMMReg; - -typedef union { - uint8_t _b[32]; - uint16_t _w[16]; - uint32_t _l[8]; - uint64_t _q[4]; -} YMMReg; - typedef MMREG_UNION(ZMMReg, 512) ZMMReg; typedef MMREG_UNION(MMXReg, 64) MMXReg; @@ -1226,7 +1236,7 @@ typedef struct BNDCSReg { #define BNDCFG_BNDPRESERVE 2ULL #define BNDCFG_BDIR_MASK TARGET_PAGE_MASK -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define ZMM_B(n) _b_ZMMReg[63 - (n)] #define ZMM_W(n) _w_ZMMReg[31 - (n)] #define ZMM_L(n) _l_ZMMReg[15 - (n)] @@ -1345,6 +1355,16 @@ typedef struct XSavePKRU { uint32_t padding; } XSavePKRU; +/* Ext. save area 17: AMX XTILECFG state */ +typedef struct XSaveXTILECFG { + uint8_t xtilecfg[64]; +} XSaveXTILECFG; + +/* Ext. save area 18: AMX XTILEDATA state */ +typedef struct XSaveXTILEDATA { + uint8_t xtiledata[8][1024]; +} XSaveXTILEDATA; + QEMU_BUILD_BUG_ON(sizeof(XSaveAVX) != 0x100); QEMU_BUILD_BUG_ON(sizeof(XSaveBNDREG) != 0x40); QEMU_BUILD_BUG_ON(sizeof(XSaveBNDCSR) != 0x40); @@ -1352,13 +1372,16 @@ QEMU_BUILD_BUG_ON(sizeof(XSaveOpmask) != 0x40); QEMU_BUILD_BUG_ON(sizeof(XSaveZMM_Hi256) != 0x200); QEMU_BUILD_BUG_ON(sizeof(XSaveHi16_ZMM) != 0x400); QEMU_BUILD_BUG_ON(sizeof(XSavePKRU) != 0x8); +QEMU_BUILD_BUG_ON(sizeof(XSaveXTILECFG) != 0x40); +QEMU_BUILD_BUG_ON(sizeof(XSaveXTILEDATA) != 0x2000); typedef struct ExtSaveArea { uint32_t feature, bits; uint32_t offset, size; + uint32_t ecx; } ExtSaveArea; -#define XSAVE_STATE_AREA_COUNT (XSTATE_PKRU_BIT + 1) +#define XSAVE_STATE_AREA_COUNT (XSTATE_XTILE_DATA_BIT + 1) extern ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT]; @@ -1431,7 +1454,7 @@ typedef struct HVFX86LazyFlags { target_ulong auxbits; } HVFX86LazyFlags; -typedef struct CPUX86State { +typedef struct CPUArchState { /* standard registers */ target_ulong regs[CPU_NB_REGS]; target_ulong eip; @@ -1494,11 +1517,11 @@ typedef struct CPUX86State { ZMMReg xmm_t0; MMXReg mmx_t0; - XMMReg ymmh_regs[CPU_NB_REGS]; - uint64_t opmask_regs[NB_OPMASK_REGS]; - YMMReg zmmh_regs[CPU_NB_REGS]; - ZMMReg hi16_zmm_regs[CPU_NB_REGS]; +#ifdef TARGET_X86_64 + uint8_t xtilecfg[64]; + uint8_t xtiledata[8192]; +#endif /* sysenter registers */ uint32_t sysenter_cs; @@ -1515,7 +1538,6 @@ typedef struct CPUX86State { target_ulong kernelgsbase; #endif - uint64_t tsc; uint64_t tsc_adjust; uint64_t tsc_deadline; uint64_t tsc_aux; @@ -1562,6 +1584,12 @@ typedef struct CPUX86State { uint64_t msr_hv_hypercall; uint64_t msr_hv_guest_os_id; uint64_t msr_hv_tsc; + uint64_t msr_hv_syndbg_control; + uint64_t msr_hv_syndbg_status; + uint64_t msr_hv_syndbg_send_page; + uint64_t msr_hv_syndbg_recv_page; + uint64_t msr_hv_syndbg_pending_page; + uint64_t msr_hv_syndbg_options; /* Per-VCPU HV MSRs */ uint64_t msr_hv_vapic; @@ -1584,6 +1612,10 @@ typedef struct CPUX86State { uint64_t msr_rtit_cr3_match; uint64_t msr_rtit_addrs[MAX_RTIT_ADDRS]; + /* Per-VCPU XFD MSRs */ + uint64_t msr_xfd; + uint64_t msr_xfd_err; + /* exception/interrupt handling */ int error_code; int exception_is_int; @@ -1665,6 +1697,7 @@ typedef struct CPUX86State { int64_t tsc_khz; int64_t user_tsc_khz; /* for sanity check only */ uint64_t apic_bus_freq; + uint64_t tsc; #if defined(CONFIG_KVM) || defined(CONFIG_HVF) void *xsave_buf; uint32_t xsave_buf_len; @@ -1707,7 +1740,7 @@ struct kvm_msrs; * * An x86 CPU. */ -struct X86CPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -2074,9 +2107,6 @@ static inline int cpu_mmu_index_kernel(CPUX86State *env) #define CC_SRC2 (env->cc_src2) #define CC_OP (env->cc_op) -typedef CPUX86State CPUArchState; -typedef X86CPU ArchCPU; - #include "exec/cpu-all.h" #include "svm.h" diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index 098a2ad15a..c3a2cf6f28 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -129,8 +129,8 @@ int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) n -= IDX_XMM_REGS; if (n < CPU_NB_REGS32 || TARGET_LONG_BITS == 64) { return gdb_get_reg128(mem_buf, - env->xmm_regs[n].ZMM_Q(0), - env->xmm_regs[n].ZMM_Q(1)); + env->xmm_regs[n].ZMM_Q(1), + env->xmm_regs[n].ZMM_Q(0)); } } else { switch (n) { diff --git a/target/i386/hax/hax-accel-ops.c b/target/i386/hax/hax-accel-ops.c index 136630e9b2..18114fe34d 100644 --- a/target/i386/hax/hax-accel-ops.c +++ b/target/i386/hax/hax-accel-ops.c @@ -61,8 +61,8 @@ static void hax_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + cpu->thread = g_new0(QemuThread, 1); + cpu->halt_cond = g_new0(QemuCond, 1); qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HAX", diff --git a/target/i386/hax/hax-all.c b/target/i386/hax/hax-all.c index bf65ed6fa9..b185ee8de4 100644 --- a/target/i386/hax/hax-all.c +++ b/target/i386/hax/hax-all.c @@ -27,7 +27,6 @@ #include "cpu.h" #include "exec/address-spaces.h" -#include "qemu-common.h" #include "qemu/accel.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" @@ -49,18 +48,13 @@ const uint32_t hax_cur_version = 0x4; /* API v4: unmapping and MMIO moves */ /* Minimum HAX kernel version */ const uint32_t hax_min_version = 0x4; /* API v4: supports unmapping */ -static bool hax_allowed; +bool hax_allowed; struct hax_state hax_global; static void hax_vcpu_sync_state(CPUArchState *env, int modified); static int hax_arch_get_registers(CPUArchState *env); -int hax_enabled(void) -{ - return hax_allowed; -} - int valid_hax_tunnel_size(uint16_t size) { return size >= sizeof(struct hax_tunnel); @@ -227,7 +221,7 @@ int hax_init_vcpu(CPUState *cpu) cpu->hax_vcpu = hax_global.vm->vcpus[cpu->cpu_index]; cpu->vcpu_dirty = true; - qemu_register_reset(hax_reset_vcpu_state, (CPUArchState *) (cpu->env_ptr)); + qemu_register_reset(hax_reset_vcpu_state, cpu->env_ptr); return ret; } @@ -674,7 +668,7 @@ void hax_cpu_synchronize_pre_loadvm(CPUState *cpu) int hax_smp_cpu_exec(CPUState *cpu) { - CPUArchState *env = (CPUArchState *) (cpu->env_ptr); + CPUArchState *env = cpu->env_ptr; int fatal; int ret; diff --git a/target/i386/hax/hax-mem.c b/target/i386/hax/hax-mem.c index a226d174d8..05dbe8cce3 100644 --- a/target/i386/hax/hax-mem.c +++ b/target/i386/hax/hax-mem.c @@ -188,15 +188,15 @@ static void hax_process_section(MemoryRegionSection *section, uint8_t flags) /* Adjust start_pa and size so that they are page-aligned. (Cf * kvm_set_phys_mem() in kvm-all.c). */ - delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask); - delta &= ~qemu_real_host_page_mask; + delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask()); + delta &= ~qemu_real_host_page_mask(); if (delta > size) { return; } start_pa += delta; size -= delta; - size &= qemu_real_host_page_mask; - if (!size || (start_pa & ~qemu_real_host_page_mask)) { + size &= qemu_real_host_page_mask(); + if (!size || (start_pa & ~qemu_real_host_page_mask())) { return; } @@ -214,7 +214,7 @@ static void hax_process_section(MemoryRegionSection *section, uint8_t flags) * call into the kernel. Instead, we split the mapping into smaller ones, * and call hax_update_mapping() on each. */ - max_mapping_size = UINT32_MAX & qemu_real_host_page_mask; + max_mapping_size = UINT32_MAX & qemu_real_host_page_mask(); while (size > max_mapping_size) { hax_update_mapping(start_pa, max_mapping_size, host_va, flags); start_pa += max_mapping_size; diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 4ba6e82fab..f8833277ab 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -47,8 +47,8 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" +#include "qemu/memalign.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 6df87116f6..573ddc33c0 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -124,10 +124,11 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) uint64_t efer = rvmcs(vcpu, VMCS_GUEST_IA32_EFER); uint64_t old_cr0 = rvmcs(vcpu, VMCS_GUEST_CR0); uint64_t changed_cr0 = old_cr0 ^ cr0; - uint64_t mask = CR0_PG | CR0_CD | CR0_NW | CR0_NE | CR0_ET; + uint64_t mask = CR0_PG_MASK | CR0_CD_MASK | CR0_NW_MASK | + CR0_NE_MASK | CR0_ET_MASK; uint64_t entry_ctls; - if ((cr0 & CR0_PG) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE) && + if ((cr0 & CR0_PG_MASK) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE_MASK) && !(efer & MSR_EFER_LME)) { address_space_read(&address_space_memory, rvmcs(vcpu, VMCS_GUEST_CR3) & ~0x1f, @@ -142,8 +143,8 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) wvmcs(vcpu, VMCS_CR0_SHADOW, cr0); if (efer & MSR_EFER_LME) { - if (changed_cr0 & CR0_PG) { - if (cr0 & CR0_PG) { + if (changed_cr0 & CR0_PG_MASK) { + if (cr0 & CR0_PG_MASK) { enter_long_mode(vcpu, cr0, efer); } else { exit_long_mode(vcpu, cr0, efer); @@ -155,23 +156,21 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) } /* Filter new CR0 after we are finished examining it above. */ - cr0 = (cr0 & ~(mask & ~CR0_PG)); - wvmcs(vcpu, VMCS_GUEST_CR0, cr0 | CR0_NE | CR0_ET); + cr0 = (cr0 & ~(mask & ~CR0_PG_MASK)); + wvmcs(vcpu, VMCS_GUEST_CR0, cr0 | CR0_NE_MASK | CR0_ET_MASK); hv_vcpu_invalidate_tlb(vcpu); - hv_vcpu_flush(vcpu); } static inline void macvm_set_cr4(hv_vcpuid_t vcpu, uint64_t cr4) { - uint64_t guest_cr4 = cr4 | CR4_VMXE; + uint64_t guest_cr4 = cr4 | CR4_VMXE_MASK; wvmcs(vcpu, VMCS_GUEST_CR4, guest_cr4); wvmcs(vcpu, VMCS_CR4_SHADOW, cr4); - wvmcs(vcpu, VMCS_CR4_MASK, CR4_VMXE); + wvmcs(vcpu, VMCS_CR4_MASK, CR4_VMXE_MASK); hv_vcpu_invalidate_tlb(vcpu); - hv_vcpu_flush(vcpu); } static inline void macvm_set_rip(CPUState *cpu, uint64_t rip) diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index 2898bb70a8..d086584f26 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "qemu-common.h" #include "x86_decode.h" #include "x86_emu.h" #include "vmcs.h" @@ -119,7 +118,7 @@ bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc, bool x86_is_protected(struct CPUState *cpu) { uint64_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); - return cr0 & CR0_PE; + return cr0 & CR0_PE_MASK; } bool x86_is_real(struct CPUState *cpu) @@ -150,13 +149,13 @@ bool x86_is_long64_mode(struct CPUState *cpu) bool x86_is_paging_mode(struct CPUState *cpu) { uint64_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); - return cr0 & CR0_PG; + return cr0 & CR0_PG_MASK; } bool x86_is_pae_enabled(struct CPUState *cpu) { uint64_t cr4 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR4); - return cr4 & CR4_PAE; + return cr4 & CR4_PAE_MASK; } target_ulong linear_addr(struct CPUState *cpu, target_ulong addr, X86Seg seg) diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h index 782664c2ea..947b98da41 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/hvf/x86.h @@ -42,40 +42,6 @@ typedef struct x86_register { }; } __attribute__ ((__packed__)) x86_register; -typedef enum x86_reg_cr0 { - CR0_PE = (1L << 0), - CR0_MP = (1L << 1), - CR0_EM = (1L << 2), - CR0_TS = (1L << 3), - CR0_ET = (1L << 4), - CR0_NE = (1L << 5), - CR0_WP = (1L << 16), - CR0_AM = (1L << 18), - CR0_NW = (1L << 29), - CR0_CD = (1L << 30), - CR0_PG = (1L << 31), -} x86_reg_cr0; - -typedef enum x86_reg_cr4 { - CR4_VME = (1L << 0), - CR4_PVI = (1L << 1), - CR4_TSD = (1L << 2), - CR4_DE = (1L << 3), - CR4_PSE = (1L << 4), - CR4_PAE = (1L << 5), - CR4_MSE = (1L << 6), - CR4_PGE = (1L << 7), - CR4_PCE = (1L << 8), - CR4_OSFXSR = (1L << 9), - CR4_OSXMMEXCPT = (1L << 10), - CR4_VMXE = (1L << 13), - CR4_SMXE = (1L << 14), - CR4_FSGSBASE = (1L << 16), - CR4_PCIDE = (1L << 17), - CR4_OSXSAVE = (1L << 18), - CR4_SMEP = (1L << 20), -} x86_reg_cr4; - /* 16 bit Task State Segment */ typedef struct x86_tss_segment16 { uint16_t link; diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index 32b0d131df..f24dd50e48 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -21,7 +21,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "x86.h" #include "vmx.h" diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index 062713b1a4..3728d7705e 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "panic.h" #include "x86_decode.h" #include "vmx.h" diff --git a/target/i386/hvf/x86_descr.c b/target/i386/hvf/x86_descr.c index af15c06ac5..a484942cfc 100644 --- a/target/i386/hvf/x86_descr.c +++ b/target/i386/hvf/x86_descr.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "vmx.h" #include "x86_descr.h" diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 7c8203b21f..f5704f63e8 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -37,7 +37,6 @@ #include "qemu/osdep.h" #include "panic.h" -#include "qemu-common.h" #include "x86_decode.h" #include "x86.h" #include "x86_emu.h" @@ -171,12 +170,12 @@ void write_val_to_reg(target_ulong reg_ptr, target_ulong val, int size) } } -static bool is_host_reg(struct CPUX86State *env, target_ulong ptr) +static bool is_host_reg(CPUX86State *env, target_ulong ptr) { return (ptr - (target_ulong)&env->regs[0]) < sizeof(env->regs); } -void write_val_ext(struct CPUX86State *env, target_ulong ptr, target_ulong val, int size) +void write_val_ext(CPUX86State *env, target_ulong ptr, target_ulong val, int size) { if (is_host_reg(env, ptr)) { write_val_to_reg(ptr, val, size); @@ -185,14 +184,14 @@ void write_val_ext(struct CPUX86State *env, target_ulong ptr, target_ulong val, vmx_write_mem(env_cpu(env), ptr, &val, size); } -uint8_t *read_mmio(struct CPUX86State *env, target_ulong ptr, int bytes) +uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes) { vmx_read_mem(env_cpu(env), env->hvf_mmio_buf, ptr, bytes); return env->hvf_mmio_buf; } -target_ulong read_val_ext(struct CPUX86State *env, target_ulong ptr, int size) +target_ulong read_val_ext(CPUX86State *env, target_ulong ptr, int size) { target_ulong val; uint8_t *mmio_ptr; @@ -222,7 +221,7 @@ target_ulong read_val_ext(struct CPUX86State *env, target_ulong ptr, int size) return val; } -static void fetch_operands(struct CPUX86State *env, struct x86_decode *decode, +static void fetch_operands(CPUX86State *env, struct x86_decode *decode, int n, bool val_op0, bool val_op1, bool val_op2) { int i; @@ -261,7 +260,7 @@ static void fetch_operands(struct CPUX86State *env, struct x86_decode *decode, } } -static void exec_mov(struct CPUX86State *env, struct x86_decode *decode) +static void exec_mov(CPUX86State *env, struct x86_decode *decode) { fetch_operands(env, decode, 2, false, true, false); write_val_ext(env, decode->op[0].ptr, decode->op[1].val, @@ -270,49 +269,49 @@ static void exec_mov(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_add(struct CPUX86State *env, struct x86_decode *decode) +static void exec_add(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true); env->eip += decode->len; } -static void exec_or(struct CPUX86State *env, struct x86_decode *decode) +static void exec_or(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, |, SET_FLAGS_OSZAPC_LOGIC, true); env->eip += decode->len; } -static void exec_adc(struct CPUX86State *env, struct x86_decode *decode) +static void exec_adc(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, +get_CF(env)+, SET_FLAGS_OSZAPC_ADD, true); env->eip += decode->len; } -static void exec_sbb(struct CPUX86State *env, struct x86_decode *decode) +static void exec_sbb(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, -get_CF(env)-, SET_FLAGS_OSZAPC_SUB, true); env->eip += decode->len; } -static void exec_and(struct CPUX86State *env, struct x86_decode *decode) +static void exec_and(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, true); env->eip += decode->len; } -static void exec_sub(struct CPUX86State *env, struct x86_decode *decode) +static void exec_sub(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, true); env->eip += decode->len; } -static void exec_xor(struct CPUX86State *env, struct x86_decode *decode) +static void exec_xor(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, ^, SET_FLAGS_OSZAPC_LOGIC, true); env->eip += decode->len; } -static void exec_neg(struct CPUX86State *env, struct x86_decode *decode) +static void exec_neg(CPUX86State *env, struct x86_decode *decode) { /*EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false);*/ int32_t val; @@ -335,13 +334,13 @@ static void exec_neg(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_cmp(struct CPUX86State *env, struct x86_decode *decode) +static void exec_cmp(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); env->eip += decode->len; } -static void exec_inc(struct CPUX86State *env, struct x86_decode *decode) +static void exec_inc(CPUX86State *env, struct x86_decode *decode) { decode->op[1].type = X86_VAR_IMMEDIATE; decode->op[1].val = 0; @@ -351,7 +350,7 @@ static void exec_inc(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_dec(struct CPUX86State *env, struct x86_decode *decode) +static void exec_dec(CPUX86State *env, struct x86_decode *decode) { decode->op[1].type = X86_VAR_IMMEDIATE; decode->op[1].val = 0; @@ -360,13 +359,13 @@ static void exec_dec(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_tst(struct CPUX86State *env, struct x86_decode *decode) +static void exec_tst(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, false); env->eip += decode->len; } -static void exec_not(struct CPUX86State *env, struct x86_decode *decode) +static void exec_not(CPUX86State *env, struct x86_decode *decode) { fetch_operands(env, decode, 1, true, false, false); @@ -375,7 +374,7 @@ static void exec_not(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -void exec_movzx(struct CPUX86State *env, struct x86_decode *decode) +void exec_movzx(CPUX86State *env, struct x86_decode *decode) { int src_op_size; int op_size = decode->operand_size; @@ -395,7 +394,7 @@ void exec_movzx(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_out(struct CPUX86State *env, struct x86_decode *decode) +static void exec_out(CPUX86State *env, struct x86_decode *decode) { switch (decode->opcode[0]) { case 0xe6: @@ -419,7 +418,7 @@ static void exec_out(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_in(struct CPUX86State *env, struct x86_decode *decode) +static void exec_in(CPUX86State *env, struct x86_decode *decode) { target_ulong val = 0; switch (decode->opcode[0]) { @@ -455,7 +454,7 @@ static void exec_in(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static inline void string_increment_reg(struct CPUX86State *env, int reg, +static inline void string_increment_reg(CPUX86State *env, int reg, struct x86_decode *decode) { target_ulong val = read_reg(env, reg, decode->addressing_size); @@ -467,8 +466,8 @@ static inline void string_increment_reg(struct CPUX86State *env, int reg, write_reg(env, reg, val, decode->addressing_size); } -static inline void string_rep(struct CPUX86State *env, struct x86_decode *decode, - void (*func)(struct CPUX86State *env, +static inline void string_rep(CPUX86State *env, struct x86_decode *decode, + void (*func)(CPUX86State *env, struct x86_decode *ins), int rep) { target_ulong rcx = read_reg(env, R_ECX, decode->addressing_size); @@ -484,7 +483,7 @@ static inline void string_rep(struct CPUX86State *env, struct x86_decode *decode } } -static void exec_ins_single(struct CPUX86State *env, struct x86_decode *decode) +static void exec_ins_single(CPUX86State *env, struct x86_decode *decode) { target_ulong addr = linear_addr_size(env_cpu(env), RDI(env), decode->addressing_size, R_ES); @@ -497,7 +496,7 @@ static void exec_ins_single(struct CPUX86State *env, struct x86_decode *decode) string_increment_reg(env, R_EDI, decode); } -static void exec_ins(struct CPUX86State *env, struct x86_decode *decode) +static void exec_ins(CPUX86State *env, struct x86_decode *decode) { if (decode->rep) { string_rep(env, decode, exec_ins_single, 0); @@ -508,7 +507,7 @@ static void exec_ins(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_outs_single(struct CPUX86State *env, struct x86_decode *decode) +static void exec_outs_single(CPUX86State *env, struct x86_decode *decode) { target_ulong addr = decode_linear_addr(env, decode, RSI(env), R_DS); @@ -520,7 +519,7 @@ static void exec_outs_single(struct CPUX86State *env, struct x86_decode *decode) string_increment_reg(env, R_ESI, decode); } -static void exec_outs(struct CPUX86State *env, struct x86_decode *decode) +static void exec_outs(CPUX86State *env, struct x86_decode *decode) { if (decode->rep) { string_rep(env, decode, exec_outs_single, 0); @@ -531,7 +530,7 @@ static void exec_outs(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_movs_single(struct CPUX86State *env, struct x86_decode *decode) +static void exec_movs_single(CPUX86State *env, struct x86_decode *decode) { target_ulong src_addr; target_ulong dst_addr; @@ -548,7 +547,7 @@ static void exec_movs_single(struct CPUX86State *env, struct x86_decode *decode) string_increment_reg(env, R_EDI, decode); } -static void exec_movs(struct CPUX86State *env, struct x86_decode *decode) +static void exec_movs(CPUX86State *env, struct x86_decode *decode) { if (decode->rep) { string_rep(env, decode, exec_movs_single, 0); @@ -559,7 +558,7 @@ static void exec_movs(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_cmps_single(struct CPUX86State *env, struct x86_decode *decode) +static void exec_cmps_single(CPUX86State *env, struct x86_decode *decode) { target_ulong src_addr; target_ulong dst_addr; @@ -579,7 +578,7 @@ static void exec_cmps_single(struct CPUX86State *env, struct x86_decode *decode) string_increment_reg(env, R_EDI, decode); } -static void exec_cmps(struct CPUX86State *env, struct x86_decode *decode) +static void exec_cmps(CPUX86State *env, struct x86_decode *decode) { if (decode->rep) { string_rep(env, decode, exec_cmps_single, decode->rep); @@ -590,7 +589,7 @@ static void exec_cmps(struct CPUX86State *env, struct x86_decode *decode) } -static void exec_stos_single(struct CPUX86State *env, struct x86_decode *decode) +static void exec_stos_single(CPUX86State *env, struct x86_decode *decode) { target_ulong addr; target_ulong val; @@ -604,7 +603,7 @@ static void exec_stos_single(struct CPUX86State *env, struct x86_decode *decode) } -static void exec_stos(struct CPUX86State *env, struct x86_decode *decode) +static void exec_stos(CPUX86State *env, struct x86_decode *decode) { if (decode->rep) { string_rep(env, decode, exec_stos_single, 0); @@ -615,7 +614,7 @@ static void exec_stos(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_scas_single(struct CPUX86State *env, struct x86_decode *decode) +static void exec_scas_single(CPUX86State *env, struct x86_decode *decode) { target_ulong addr; @@ -628,7 +627,7 @@ static void exec_scas_single(struct CPUX86State *env, struct x86_decode *decode) string_increment_reg(env, R_EDI, decode); } -static void exec_scas(struct CPUX86State *env, struct x86_decode *decode) +static void exec_scas(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = R_EAX; @@ -641,7 +640,7 @@ static void exec_scas(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_lods_single(struct CPUX86State *env, struct x86_decode *decode) +static void exec_lods_single(CPUX86State *env, struct x86_decode *decode) { target_ulong addr; target_ulong val = 0; @@ -653,7 +652,7 @@ static void exec_lods_single(struct CPUX86State *env, struct x86_decode *decode) string_increment_reg(env, R_ESI, decode); } -static void exec_lods(struct CPUX86State *env, struct x86_decode *decode) +static void exec_lods(CPUX86State *env, struct x86_decode *decode) { if (decode->rep) { string_rep(env, decode, exec_lods_single, 0); @@ -760,7 +759,7 @@ void simulate_rdmsr(struct CPUState *cpu) RDX(env) = (uint32_t)(val >> 32); } -static void exec_rdmsr(struct CPUX86State *env, struct x86_decode *decode) +static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) { simulate_rdmsr(env_cpu(env)); env->eip += decode->len; @@ -855,7 +854,7 @@ void simulate_wrmsr(struct CPUState *cpu) printf("write msr %llx\n", RCX(cpu));*/ } -static void exec_wrmsr(struct CPUX86State *env, struct x86_decode *decode) +static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) { simulate_wrmsr(env_cpu(env)); env->eip += decode->len; @@ -865,7 +864,7 @@ static void exec_wrmsr(struct CPUX86State *env, struct x86_decode *decode) * flag: * 0 - bt, 1 - btc, 2 - bts, 3 - btr */ -static void do_bt(struct CPUX86State *env, struct x86_decode *decode, int flag) +static void do_bt(CPUX86State *env, struct x86_decode *decode, int flag) { int32_t displacement; uint8_t index; @@ -911,31 +910,31 @@ static void do_bt(struct CPUX86State *env, struct x86_decode *decode, int flag) set_CF(env, cf); } -static void exec_bt(struct CPUX86State *env, struct x86_decode *decode) +static void exec_bt(CPUX86State *env, struct x86_decode *decode) { do_bt(env, decode, 0); env->eip += decode->len; } -static void exec_btc(struct CPUX86State *env, struct x86_decode *decode) +static void exec_btc(CPUX86State *env, struct x86_decode *decode) { do_bt(env, decode, 1); env->eip += decode->len; } -static void exec_btr(struct CPUX86State *env, struct x86_decode *decode) +static void exec_btr(CPUX86State *env, struct x86_decode *decode) { do_bt(env, decode, 3); env->eip += decode->len; } -static void exec_bts(struct CPUX86State *env, struct x86_decode *decode) +static void exec_bts(CPUX86State *env, struct x86_decode *decode) { do_bt(env, decode, 2); env->eip += decode->len; } -void exec_shl(struct CPUX86State *env, struct x86_decode *decode) +void exec_shl(CPUX86State *env, struct x86_decode *decode) { uint8_t count; int of = 0, cf = 0; @@ -1022,7 +1021,7 @@ void exec_movsx(CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -void exec_ror(struct CPUX86State *env, struct x86_decode *decode) +void exec_ror(CPUX86State *env, struct x86_decode *decode) { uint8_t count; @@ -1100,7 +1099,7 @@ void exec_ror(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -void exec_rol(struct CPUX86State *env, struct x86_decode *decode) +void exec_rol(CPUX86State *env, struct x86_decode *decode) { uint8_t count; @@ -1182,7 +1181,7 @@ void exec_rol(struct CPUX86State *env, struct x86_decode *decode) } -void exec_rcl(struct CPUX86State *env, struct x86_decode *decode) +void exec_rcl(CPUX86State *env, struct x86_decode *decode) { uint8_t count; int of = 0, cf = 0; @@ -1267,7 +1266,7 @@ void exec_rcl(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -void exec_rcr(struct CPUX86State *env, struct x86_decode *decode) +void exec_rcr(CPUX86State *env, struct x86_decode *decode) { uint8_t count; int of = 0, cf = 0; @@ -1342,7 +1341,7 @@ void exec_rcr(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_xchg(struct CPUX86State *env, struct x86_decode *decode) +static void exec_xchg(CPUX86State *env, struct x86_decode *decode) { fetch_operands(env, decode, 2, true, true, false); @@ -1354,7 +1353,7 @@ static void exec_xchg(struct CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void exec_xadd(struct CPUX86State *env, struct x86_decode *decode) +static void exec_xadd(CPUX86State *env, struct x86_decode *decode) { EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true); write_val_ext(env, decode->op[1].ptr, decode->op[0].val, @@ -1365,7 +1364,7 @@ static void exec_xadd(struct CPUX86State *env, struct x86_decode *decode) static struct cmd_handler { enum x86_decode_cmd cmd; - void (*handler)(struct CPUX86State *env, struct x86_decode *ins); + void (*handler)(CPUX86State *env, struct x86_decode *ins); } handlers[] = { {X86_DECODE_CMD_INVL, NULL,}, {X86_DECODE_CMD_MOV, exec_mov}, @@ -1465,7 +1464,7 @@ void store_regs(struct CPUState *cpu) macvm_set_rip(cpu, env->eip); } -bool exec_instruction(struct CPUX86State *env, struct x86_decode *ins) +bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { /*if (hvf_vcpu_id(cpu)) printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cpu), env->eip, diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 233f7b8daa..640da90b30 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -24,7 +24,7 @@ #include "cpu.h" void init_emu(void); -bool exec_instruction(struct CPUX86State *env, struct x86_decode *ins); +bool exec_instruction(CPUX86State *env, struct x86_decode *ins); void load_regs(struct CPUState *cpu); void store_regs(struct CPUState *cpu); @@ -36,15 +36,15 @@ target_ulong read_reg(CPUX86State *env, int reg, int size); void write_reg(CPUX86State *env, int reg, target_ulong val, int size); target_ulong read_val_from_reg(target_ulong reg_ptr, int size); void write_val_to_reg(target_ulong reg_ptr, target_ulong val, int size); -void write_val_ext(struct CPUX86State *env, target_ulong ptr, target_ulong val, int size); -uint8_t *read_mmio(struct CPUX86State *env, target_ulong ptr, int bytes); -target_ulong read_val_ext(struct CPUX86State *env, target_ulong ptr, int size); +void write_val_ext(CPUX86State *env, target_ulong ptr, target_ulong val, int size); +uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes); +target_ulong read_val_ext(CPUX86State *env, target_ulong ptr, int size); -void exec_movzx(struct CPUX86State *env, struct x86_decode *decode); -void exec_shl(struct CPUX86State *env, struct x86_decode *decode); -void exec_movsx(struct CPUX86State *env, struct x86_decode *decode); -void exec_ror(struct CPUX86State *env, struct x86_decode *decode); -void exec_rol(struct CPUX86State *env, struct x86_decode *decode); -void exec_rcl(struct CPUX86State *env, struct x86_decode *decode); -void exec_rcr(struct CPUX86State *env, struct x86_decode *decode); +void exec_movzx(CPUX86State *env, struct x86_decode *decode); +void exec_shl(CPUX86State *env, struct x86_decode *decode); +void exec_movsx(CPUX86State *env, struct x86_decode *decode); +void exec_ror(CPUX86State *env, struct x86_decode *decode); +void exec_rol(CPUX86State *env, struct x86_decode *decode); +void exec_rcl(CPUX86State *env, struct x86_decode *decode); +void exec_rcr(CPUX86State *env, struct x86_decode *decode); #endif diff --git a/target/i386/hvf/x86_flags.c b/target/i386/hvf/x86_flags.c index fecbca7517..03d6de5efc 100644 --- a/target/i386/hvf/x86_flags.c +++ b/target/i386/hvf/x86_flags.c @@ -23,7 +23,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "panic.h" #include "cpu.h" #include "x86_flags.h" diff --git a/target/i386/hvf/x86_mmu.c b/target/i386/hvf/x86_mmu.c index e9ed0f5aa1..96d117567e 100644 --- a/target/i386/hvf/x86_mmu.c +++ b/target/i386/hvf/x86_mmu.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include "panic.h" -#include "qemu-common.h" #include "cpu.h" #include "x86.h" #include "x86_mmu.h" @@ -129,7 +128,7 @@ static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, uint32_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); /* check protection */ - if (cr0 & CR0_WP) { + if (cr0 & CR0_WP_MASK) { if (pt->write_access && !pte_write_access(pte)) { return false; } diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index 422156128b..beaeec0687 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -8,7 +8,6 @@ // GNU General Public License for more details. #include "qemu/osdep.h" #include "panic.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "sysemu/hvf.h" @@ -174,12 +173,12 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea //ret = task_switch_16(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); VM_PANIC("task_switch_16"); - macvm_set_cr0(cpu->hvf->fd, rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0) | CR0_TS); + macvm_set_cr0(cpu->hvf->fd, rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0) | + CR0_TS_MASK); x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); store_regs(cpu); hv_vcpu_invalidate_tlb(cpu->hvf->fd); - hv_vcpu_flush(cpu->hvf->fd); } diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 05ec1bddc4..69d4fb8cf5 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "x86hvf.h" #include "vmx.h" #include "vmcs.h" @@ -83,7 +82,7 @@ void hvf_put_xsave(CPUState *cpu_state) } } -void hvf_put_segments(CPUState *cpu_state) +static void hvf_put_segments(CPUState *cpu_state) { CPUX86State *env = &X86_CPU(cpu_state)->env; struct vmx_segment seg; @@ -125,8 +124,6 @@ void hvf_put_segments(CPUState *cpu_state) hvf_set_segment(cpu_state, &seg, &env->ldt, false); vmx_write_segment_descriptor(cpu_state, &seg, R_LDTR); - - hv_vcpu_flush(cpu_state->hvf->fd); } void hvf_put_msrs(CPUState *cpu_state) @@ -166,7 +163,7 @@ void hvf_get_xsave(CPUState *cpu_state) x86_cpu_xrstor_all_areas(X86_CPU(cpu_state), xsave, xsave_len); } -void hvf_get_segments(CPUState *cpu_state) +static void hvf_get_segments(CPUState *cpu_state) { CPUX86State *env = &X86_CPU(cpu_state)->env; diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h index 99ed8d608d..db6003d6bd 100644 --- a/target/i386/hvf/x86hvf.h +++ b/target/i386/hvf/x86hvf.h @@ -26,11 +26,9 @@ void hvf_set_segment(struct CPUState *cpu, struct vmx_segment *vmx_seg, SegmentCache *qseg, bool is_tr); void hvf_get_segment(SegmentCache *qseg, struct vmx_segment *vmx_seg); void hvf_put_xsave(CPUState *cpu_state); -void hvf_put_segments(CPUState *cpu_state); void hvf_put_msrs(CPUState *cpu_state); void hvf_get_xsave(CPUState *cpu_state); void hvf_get_msrs(CPUState *cpu_state); void vmx_clear_int_window_exiting(CPUState *cpu); -void hvf_get_segments(CPUState *cpu_state); void vmx_update_tpr(CPUState *cpu); #endif diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 89f81afda7..e40e59411c 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -19,6 +19,9 @@ #define HV_CPUID_ENLIGHTMENT_INFO 0x40000004 #define HV_CPUID_IMPLEMENT_LIMITS 0x40000005 #define HV_CPUID_NESTED_FEATURES 0x4000000A +#define HV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS 0x40000080 +#define HV_CPUID_SYNDBG_INTERFACE 0x40000081 +#define HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES 0x40000082 #define HV_CPUID_MIN 0x40000005 #define HV_CPUID_MAX 0x4000ffff #define HV_HYPERVISOR_PRESENT_BIT 0x80000000 @@ -55,8 +58,14 @@ #define HV_GUEST_IDLE_STATE_AVAILABLE (1u << 5) #define HV_FREQUENCY_MSRS_AVAILABLE (1u << 8) #define HV_GUEST_CRASH_MSR_AVAILABLE (1u << 10) +#define HV_FEATURE_DEBUG_MSRS_AVAILABLE (1u << 11) #define HV_STIMER_DIRECT_MODE_AVAILABLE (1u << 19) +/* + * HV_CPUID_FEATURES.EBX bits + */ +#define HV_PARTITION_DEBUGGING_ALLOWED (1u << 12) + /* * HV_CPUID_ENLIGHTMENT_INFO.EAX bits */ @@ -72,6 +81,11 @@ #define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) #define HV_NO_NONARCH_CORESHARING (1u << 18) +/* + * HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES.EAX bits + */ +#define HV_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING (1u << 1) + /* * Basic virtualized MSRs */ @@ -130,6 +144,18 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 #define HV_X64_MSR_STIMER3_COUNT 0x400000B7 +/* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x400000F1 +#define HV_X64_MSR_SYNDBG_STATUS 0x400000F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x400000F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x400000F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER 0x400000F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) + /* * Guest crash notification MSRs */ @@ -168,5 +194,16 @@ #define HV_STIMER_COUNT 4 +/* + * Synthetic debugger control definitions + */ +#define HV_SYNDBG_CONTROL_SEND (1u << 0) +#define HV_SYNDBG_CONTROL_RECV (1u << 1) +#define HV_SYNDBG_CONTROL_SEND_SIZE(ctl) ((ctl >> 16) & 0xffff) +#define HV_SYNDBG_STATUS_INVALID (0) +#define HV_SYNDBG_STATUS_SEND_SUCCESS (1u << 0) +#define HV_SYNDBG_STATUS_RECV_SUCCESS (1u << 2) +#define HV_SYNDBG_STATUS_RESET (1u << 3) +#define HV_SYNDBG_STATUS_SET_SIZE(st, sz) (st | (sz << 16)) #endif diff --git a/target/i386/kvm/hyperv-stub.c b/target/i386/kvm/hyperv-stub.c index 0028527e79..778ed782e6 100644 --- a/target/i386/kvm/hyperv-stub.c +++ b/target/i386/kvm/hyperv-stub.c @@ -27,6 +27,12 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) return 0; case KVM_EXIT_HYPERV_HCALL: exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE; + return 0; + case KVM_EXIT_HYPERV_SYNDBG: + if (!hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNDBG)) { + return -1; + } + return 0; default: return -1; diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index 26efc1e0e6..9026ef3a81 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -81,20 +81,66 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) case KVM_EXIT_HYPERV_HCALL: { uint16_t code = exit->u.hcall.input & 0xffff; bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST; - uint64_t param = exit->u.hcall.params[0]; + uint64_t in_param = exit->u.hcall.params[0]; + uint64_t out_param = exit->u.hcall.params[1]; switch (code) { case HV_POST_MESSAGE: - exit->u.hcall.result = hyperv_hcall_post_message(param, fast); + exit->u.hcall.result = hyperv_hcall_post_message(in_param, fast); break; case HV_SIGNAL_EVENT: - exit->u.hcall.result = hyperv_hcall_signal_event(param, fast); + exit->u.hcall.result = hyperv_hcall_signal_event(in_param, fast); + break; + case HV_POST_DEBUG_DATA: + exit->u.hcall.result = + hyperv_hcall_post_dbg_data(in_param, out_param, fast); + break; + case HV_RETRIEVE_DEBUG_DATA: + exit->u.hcall.result = + hyperv_hcall_retreive_dbg_data(in_param, out_param, fast); + break; + case HV_RESET_DEBUG_SESSION: + exit->u.hcall.result = + hyperv_hcall_reset_dbg_session(out_param); break; default: exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE; } return 0; } + + case KVM_EXIT_HYPERV_SYNDBG: + if (!hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNDBG)) { + return -1; + } + + switch (exit->u.syndbg.msr) { + case HV_X64_MSR_SYNDBG_CONTROL: { + uint64_t control = exit->u.syndbg.control; + env->msr_hv_syndbg_control = control; + env->msr_hv_syndbg_send_page = exit->u.syndbg.send_page; + env->msr_hv_syndbg_recv_page = exit->u.syndbg.recv_page; + exit->u.syndbg.status = HV_STATUS_SUCCESS; + if (control & HV_SYNDBG_CONTROL_SEND) { + exit->u.syndbg.status = + hyperv_syndbg_send(env->msr_hv_syndbg_send_page, + HV_SYNDBG_CONTROL_SEND_SIZE(control)); + } else if (control & HV_SYNDBG_CONTROL_RECV) { + exit->u.syndbg.status = + hyperv_syndbg_recv(env->msr_hv_syndbg_recv_page, + TARGET_PAGE_SIZE); + } + break; + } + case HV_X64_MSR_SYNDBG_PENDING_BUFFER: + env->msr_hv_syndbg_pending_page = exit->u.syndbg.pending_page; + hyperv_syndbg_set_pending_page(env->msr_hv_syndbg_pending_page); + break; + default: + return -1; + } + + return 0; default: return -1; } diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index d95028018e..5eb955ce9a 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -84,7 +84,7 @@ static void kvm_cpu_max_instance_init(X86CPU *cpu) static void kvm_cpu_xsave_init(void) { static bool first = true; - KVMState *s = kvm_state; + uint32_t eax, ebx, ecx, edx; int i; if (!first) { @@ -99,12 +99,18 @@ static void kvm_cpu_xsave_init(void) for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) { ExtSaveArea *esa = &x86_ext_save_areas[i]; - if (esa->size) { - int sz = kvm_arch_get_supported_cpuid(s, 0xd, i, R_EAX); - if (sz != 0) { - assert(esa->size == sz); - esa->offset = kvm_arch_get_supported_cpuid(s, 0xd, i, R_EBX); - } + if (!esa->size) { + continue; + } + if ((x86_cpu_get_supported_feature_word(esa->feature, false) & esa->bits) + != esa->bits) { + continue; + } + host_cpuid(0xd, i, &eax, &ebx, &ecx, &edx); + if (eax != 0) { + assert(esa->size == eax); + esa->offset = ebx; + esa->ecx = ecx; } } } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 2c8feb4a6f..c885763a5b 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -17,6 +17,7 @@ #include "qapi/error.h" #include #include +#include #include #include "standard-headers/asm-x86/kvm_para.h" @@ -37,6 +38,7 @@ #include "qemu/main-loop.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/memalign.h" #include "hw/i386/x86.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" @@ -52,6 +54,8 @@ #include "exec/memattrs.h" #include "trace.h" +#include CONFIG_DEVICES + //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -102,6 +106,7 @@ static bool has_msr_hv_synic; static bool has_msr_hv_stimer; static bool has_msr_hv_frequencies; static bool has_msr_hv_reenlightenment; +static bool has_msr_hv_syndbg_options; static bool has_msr_xss; static bool has_msr_umwait; static bool has_msr_spec_ctrl; @@ -122,6 +127,7 @@ static uint32_t num_architectural_pmu_gp_counters; static uint32_t num_architectural_pmu_fixed_counters; static int has_xsave; +static int has_xsave2; static int has_xcrs; static int has_pit_state2; static int has_sregs2; @@ -348,6 +354,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, struct kvm_cpuid2 *cpuid; uint32_t ret = 0; uint32_t cpuid_1_edx; + uint64_t bitmask; cpuid = get_supported_cpuid(s); @@ -405,6 +412,34 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (!has_msr_arch_capabs) { ret &= ~CPUID_7_0_EDX_ARCH_CAPABILITIES; } + } else if (function == 0xd && index == 0 && + (reg == R_EAX || reg == R_EDX)) { + /* + * The value returned by KVM_GET_SUPPORTED_CPUID does not include + * features that still have to be enabled with the arch_prctl + * system call. QEMU needs the full value, which is retrieved + * with KVM_GET_DEVICE_ATTR. + */ + struct kvm_device_attr attr = { + .group = 0, + .attr = KVM_X86_XCOMP_GUEST_SUPP, + .addr = (unsigned long) &bitmask + }; + + bool sys_attr = kvm_check_extension(s, KVM_CAP_SYS_ATTRIBUTES); + if (!sys_attr) { + return ret; + } + + int rc = kvm_ioctl(s, KVM_GET_DEVICE_ATTR, &attr); + if (rc < 0) { + if (rc != -ENXIO) { + warn_report("KVM_GET_DEVICE_ATTR(0, KVM_X86_XCOMP_GUEST_SUPP) " + "error: %d", rc); + } + return ret; + } + ret = (reg == R_EAX) ? bitmask : bitmask >> 32; } else if (function == 0x80000001 && reg == R_ECX) { /* * It's safe to enable TOPOEXT even if it's not returned by @@ -537,7 +572,7 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) if (code == BUS_MCEERR_AR) { status |= MCI_STATUS_AR | 0x134; - mcg_status |= MCG_STATUS_EIPV; + mcg_status |= MCG_STATUS_RIPV | MCG_STATUS_EIPV; } else { status |= 0xc0; mcg_status |= MCG_STATUS_RIPV; @@ -932,6 +967,16 @@ static struct { .bits = HV_DEPRECATING_AEOI_RECOMMENDED} } }, +#ifdef CONFIG_SYNDBG + [HYPERV_FEAT_SYNDBG] = { + .desc = "Enable synthetic kernel debugger channel (hv-syndbg)", + .flags = { + {.func = HV_CPUID_FEATURES, .reg = R_EDX, + .bits = HV_FEATURE_DEBUG_MSRS_AVAILABLE} + }, + .dependencies = BIT(HYPERV_FEAT_SYNIC) | BIT(HYPERV_FEAT_RELAXED) + }, +#endif }; static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max, @@ -972,8 +1017,8 @@ static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max, static struct kvm_cpuid2 *get_supported_hv_cpuid(CPUState *cs) { struct kvm_cpuid2 *cpuid; - /* 0x40000000..0x40000005, 0x4000000A, 0x40000080..0x40000080 leaves */ - int max = 10; + /* 0x40000000..0x40000005, 0x4000000A, 0x40000080..0x40000082 leaves */ + int max = 11; int i; bool do_sys_ioctl; @@ -1086,6 +1131,12 @@ static struct kvm_cpuid2 *get_supported_hv_cpuid_legacy(CPUState *cs) entry_feat->eax |= HV_SYNTIMERS_AVAILABLE; } + if (has_msr_hv_syndbg_options) { + entry_feat->edx |= HV_GUEST_DEBUGGING_AVAILABLE; + entry_feat->edx |= HV_FEATURE_DEBUG_MSRS_AVAILABLE; + entry_feat->ebx |= HV_PARTITION_DEBUGGING_ALLOWED; + } + if (kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TLBFLUSH) > 0) { entry_recomm->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED; @@ -1337,12 +1388,22 @@ static int hyperv_fill_cpuids(CPUState *cs, { X86CPU *cpu = X86_CPU(cs); struct kvm_cpuid_entry2 *c; - uint32_t cpuid_i = 0; + uint32_t signature[3]; + uint32_t cpuid_i = 0, max_cpuid_leaf = 0; + + max_cpuid_leaf = HV_CPUID_IMPLEMENT_LIMITS; + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_EVMCS)) { + max_cpuid_leaf = MAX(max_cpuid_leaf, HV_CPUID_NESTED_FEATURES); + } + + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNDBG)) { + max_cpuid_leaf = + MAX(max_cpuid_leaf, HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES); + } c = &cpuid_ent[cpuid_i++]; c->function = HV_CPUID_VENDOR_AND_MAX_FUNCTIONS; - c->eax = hyperv_feat_enabled(cpu, HYPERV_FEAT_EVMCS) ? - HV_CPUID_NESTED_FEATURES : HV_CPUID_IMPLEMENT_LIMITS; + c->eax = max_cpuid_leaf; c->ebx = cpu->hyperv_vendor_id[0]; c->ecx = cpu->hyperv_vendor_id[1]; c->edx = cpu->hyperv_vendor_id[2]; @@ -1421,6 +1482,33 @@ static int hyperv_fill_cpuids(CPUState *cs, c->eax = cpu->hyperv_nested[0]; } + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNDBG)) { + c = &cpuid_ent[cpuid_i++]; + c->function = HV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS; + c->eax = hyperv_feat_enabled(cpu, HYPERV_FEAT_EVMCS) ? + HV_CPUID_NESTED_FEATURES : HV_CPUID_IMPLEMENT_LIMITS; + memcpy(signature, "Microsoft VS", 12); + c->eax = 0; + c->ebx = signature[0]; + c->ecx = signature[1]; + c->edx = signature[2]; + + c = &cpuid_ent[cpuid_i++]; + c->function = HV_CPUID_SYNDBG_INTERFACE; + memcpy(signature, "VS#1\0\0\0\0\0\0\0\0", 12); + c->eax = signature[0]; + c->ebx = 0; + c->ecx = 0; + c->edx = 0; + + c = &cpuid_ent[cpuid_i++]; + c->function = HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES; + c->eax = HV_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING; + c->ebx = 0; + c->ecx = 0; + c->edx = 0; + } + return cpuid_i; } @@ -1565,6 +1653,26 @@ static Error *invtsc_mig_blocker; #define KVM_MAX_CPUID_ENTRIES 100 +static void kvm_init_xsave(CPUX86State *env) +{ + if (has_xsave2) { + env->xsave_buf_len = QEMU_ALIGN_UP(has_xsave2, 4096); + } else if (has_xsave) { + env->xsave_buf_len = sizeof(struct kvm_xsave); + } else { + return; + } + + env->xsave_buf = qemu_memalign(4096, env->xsave_buf_len); + memset(env->xsave_buf, 0, env->xsave_buf_len); + /* + * The allocated storage must be large enough for all of the + * possible XSAVE state components. + */ + assert(kvm_arch_get_supported_cpuid(kvm_state, 0xd, 0, R_ECX) <= + env->xsave_buf_len); +} + int kvm_arch_init_vcpu(CPUState *cs) { struct { @@ -1594,6 +1702,8 @@ int kvm_arch_init_vcpu(CPUState *cs) cpuid_i = 0; + has_xsave2 = kvm_check_extension(cs->kvm_state, KVM_CAP_XSAVE2); + r = kvm_arch_set_tsc_khz(cs); if (r < 0) { return r; @@ -1759,7 +1869,9 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; } break; - case 0x14: { + case 0x14: + case 0x1d: + case 0x1e: { uint32_t times; c->function = i; @@ -1981,19 +2093,7 @@ int kvm_arch_init_vcpu(CPUState *cs) if (r) { goto fail; } - - if (has_xsave) { - env->xsave_buf_len = sizeof(struct kvm_xsave); - env->xsave_buf = qemu_memalign(4096, env->xsave_buf_len); - memset(env->xsave_buf, 0, env->xsave_buf_len); - - /* - * The allocated storage must be large enough for all of the - * possible XSAVE state components. - */ - assert(kvm_arch_get_supported_cpuid(kvm_state, 0xd, 0, R_ECX) - <= env->xsave_buf_len); - } + kvm_init_xsave(env); max_nested_state_len = kvm_max_nested_state_length(); if (max_nested_state_len > 0) { @@ -2037,6 +2137,8 @@ int kvm_arch_destroy_vcpu(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + g_free(env->xsave_buf); + if (cpu->kvm_msr_buf) { g_free(cpu->kvm_msr_buf); cpu->kvm_msr_buf = NULL; @@ -2215,6 +2317,9 @@ static int kvm_get_supported_msrs(KVMState *s) case HV_X64_MSR_REENLIGHTENMENT_CONTROL: has_msr_hv_reenlightenment = true; break; + case HV_X64_MSR_SYNDBG_OPTIONS: + has_msr_hv_syndbg_options = true; + break; case MSR_IA32_SPEC_CTRL: has_msr_spec_ctrl = true; break; @@ -3132,6 +3237,13 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_STATUS, env->msr_hv_tsc_emulation_status); } +#ifdef CONFIG_SYNDBG + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNDBG) && + has_msr_hv_syndbg_options) { + kvm_msr_entry_add(cpu, HV_X64_MSR_SYNDBG_OPTIONS, + hyperv_syndbg_query_options()); + } +#endif } if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VAPIC)) { kvm_msr_entry_add(cpu, HV_X64_MSR_APIC_ASSIST_PAGE, @@ -3242,6 +3354,13 @@ static int kvm_put_msrs(X86CPU *cpu, int level) env->msr_ia32_sgxlepubkeyhash[3]); } + if (env->features[FEAT_XSAVE] & CPUID_D_1_EAX_XFD) { + kvm_msr_entry_add(cpu, MSR_IA32_XFD, + env->msr_xfd); + kvm_msr_entry_add(cpu, MSR_IA32_XFD_ERR, + env->msr_xfd_err); + } + /* Note: MSR_IA32_FEATURE_CONTROL is written separately, see * kvm_put_msr_feature_control. */ } @@ -3297,13 +3416,14 @@ static int kvm_get_xsave(X86CPU *cpu) { CPUX86State *env = &cpu->env; void *xsave = env->xsave_buf; - int ret; + int type, ret; if (!has_xsave) { return kvm_get_fpu(cpu); } - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_XSAVE, xsave); + type = has_xsave2 ? KVM_GET_XSAVE2 : KVM_GET_XSAVE; + ret = kvm_vcpu_ioctl(CPU(cpu), type, xsave); if (ret < 0) { return ret; } @@ -3565,6 +3685,9 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_CONTROL, 0); kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_STATUS, 0); } + if (has_msr_hv_syndbg_options) { + kvm_msr_entry_add(cpu, HV_X64_MSR_SYNDBG_OPTIONS, 0); + } if (has_msr_hv_crash) { int j; @@ -3633,6 +3756,11 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH3, 0); } + if (env->features[FEAT_XSAVE] & CPUID_D_1_EAX_XFD) { + kvm_msr_entry_add(cpu, MSR_IA32_XFD, 0); + kvm_msr_entry_add(cpu, MSR_IA32_XFD_ERR, 0); + } + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, cpu->kvm_msr_buf); if (ret < 0) { return ret; @@ -3851,6 +3979,9 @@ static int kvm_get_msrs(X86CPU *cpu) case HV_X64_MSR_TSC_EMULATION_STATUS: env->msr_hv_tsc_emulation_status = msrs[i].data; break; + case HV_X64_MSR_SYNDBG_OPTIONS: + env->msr_hv_syndbg_options = msrs[i].data; + break; case MSR_MTRRdefType: env->mtrr_deftype = msrs[i].data; break; @@ -3929,6 +4060,12 @@ static int kvm_get_msrs(X86CPU *cpu) env->msr_ia32_sgxlepubkeyhash[index - MSR_IA32_SGXLEPUBKEYHASH0] = msrs[i].data; break; + case MSR_IA32_XFD: + env->msr_xfd = msrs[i].data; + break; + case MSR_IA32_XFD_ERR: + env->msr_xfd_err = msrs[i].data; + break; } } @@ -4939,16 +5076,18 @@ void kvm_arch_init_irq_routing(KVMState *s) kvm_gsi_routing_allowed = true; if (kvm_irqchip_is_split()) { + KVMRouteChange c = kvm_irqchip_begin_route_changes(s); int i; /* If the ioapic is in QEMU and the lapics are in KVM, reserve MSI routes for signaling interrupts to the local apics. */ for (i = 0; i < IOAPIC_NUM_PINS; i++) { - if (kvm_irqchip_add_msi_route(s, 0, NULL) < 0) { + if (kvm_irqchip_add_msi_route(&c, 0, NULL) < 0) { error_report("Could not enable split IRQ mode."); exit(1); } } + kvm_irqchip_commit_route_changes(&c); } } @@ -5148,3 +5287,39 @@ bool kvm_arch_cpu_check_are_resettable(void) { return !sev_es_enabled(); } + +#define ARCH_REQ_XCOMP_GUEST_PERM 0x1025 + +void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask) +{ + KVMState *s = kvm_state; + uint64_t supported; + + mask &= XSTATE_DYNAMIC_MASK; + if (!mask) { + return; + } + /* + * Just ignore bits that are not in CPUID[EAX=0xD,ECX=0]. + * ARCH_REQ_XCOMP_GUEST_PERM would fail, and QEMU has warned + * about them already because they are not supported features. + */ + supported = kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX); + supported |= (uint64_t)kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX) << 32; + mask &= supported; + + while (mask) { + int bit = ctz64(mask); + int rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_GUEST_PERM, bit); + if (rc) { + /* + * Older kernel version (<5.17) do not support + * ARCH_REQ_XCOMP_GUEST_PERM, but also do not return + * any dynamic feature from kvm_arch_get_supported_cpuid. + */ + warn_report("prctl(ARCH_REQ_XCOMP_GUEST_PERM) failure " + "for feature bit %d", bit); + } + mask &= ~BIT_ULL(bit); + } +} diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index a978509d50..4124912c20 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -52,5 +52,6 @@ bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address); bool kvm_enable_sgx_provisioning(KVMState *s); +void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); #endif diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c index 6080c007a2..1be5341e8a 100644 --- a/target/i386/kvm/sev-stub.c +++ b/target/i386/kvm/sev-stub.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "sev.h" int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) diff --git a/target/i386/machine.c b/target/i386/machine.c index 6202f47793..7c54bada81 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1483,6 +1483,48 @@ static const VMStateDescription vmstate_pdptrs = { } }; +static bool xfd_msrs_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return !!(env->features[FEAT_XSAVE] & CPUID_D_1_EAX_XFD); +} + +static const VMStateDescription vmstate_msr_xfd = { + .name = "cpu/msr_xfd", + .version_id = 1, + .minimum_version_id = 1, + .needed = xfd_msrs_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_xfd, X86CPU), + VMSTATE_UINT64(env.msr_xfd_err, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + +#ifdef TARGET_X86_64 +static bool amx_xtile_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return !!(env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_AMX_TILE); +} + +static const VMStateDescription vmstate_amx_xtile = { + .name = "cpu/intel_amx_xtile", + .version_id = 1, + .minimum_version_id = 1, + .needed = amx_xtile_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(env.xtilecfg, X86CPU, 64), + VMSTATE_UINT8_ARRAY(env.xtiledata, X86CPU, 8192), + VMSTATE_END_OF_LIST() + } +}; +#endif + const VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -1622,6 +1664,10 @@ const VMStateDescription vmstate_x86_cpu = { &vmstate_msr_tsx_ctrl, &vmstate_msr_intel_sgx, &vmstate_pdptrs, + &vmstate_msr_xfd, +#ifdef TARGET_X86_64 + &vmstate_amx_xtile, +#endif NULL } }; diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index f788f75289..6c46101ac1 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -64,8 +64,8 @@ static void nvmm_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + cpu->thread = g_new0(QemuThread, 1); + cpu->halt_cond = g_new0(QemuCond, 1); qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/NVMM", cpu->cpu_index); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 9af261eea3..b75738ee9c 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -11,7 +11,6 @@ #include "cpu.h" #include "exec/address-spaces.h" #include "exec/ioport.h" -#include "qemu-common.h" #include "qemu/accel.h" #include "sysemu/nvmm.h" #include "sysemu/cpus.h" @@ -85,7 +84,7 @@ nvmm_set_segment(struct nvmm_x64_state_seg *nseg, const SegmentCache *qseg) static void nvmm_set_registers(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; + CPUX86State *env = cpu->env_ptr; struct nvmm_machine *mach = get_nvmm_mach(); struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -222,7 +221,7 @@ nvmm_get_segment(SegmentCache *qseg, const struct nvmm_x64_state_seg *nseg) static void nvmm_get_registers(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; + CPUX86State *env = cpu->env_ptr; struct nvmm_machine *mach = get_nvmm_mach(); struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -347,7 +346,7 @@ nvmm_get_registers(CPUState *cpu) static bool nvmm_can_take_int(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; + CPUX86State *env = cpu->env_ptr; struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); struct nvmm_vcpu *vcpu = &qcpu->vcpu; struct nvmm_machine *mach = get_nvmm_mach(); @@ -394,7 +393,7 @@ nvmm_can_take_nmi(CPUState *cpu) static void nvmm_vcpu_pre_run(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; + CPUX86State *env = cpu->env_ptr; struct nvmm_machine *mach = get_nvmm_mach(); struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -480,7 +479,7 @@ static void nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit) { struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); - struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; + CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); uint64_t tpr; @@ -652,7 +651,7 @@ static int nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu, struct nvmm_vcpu_exit *exit) { - struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; + CPUX86State *env = cpu->env_ptr; int ret = 0; qemu_mutex_lock_iothread(); @@ -685,7 +684,7 @@ nvmm_inject_ud(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) static int nvmm_vcpu_loop(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; + CPUX86State *env = cpu->env_ptr; struct nvmm_machine *mach = get_nvmm_mach(); struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -1075,15 +1074,15 @@ nvmm_process_section(MemoryRegionSection *section, int add) } /* Adjust start_pa and size so that they are page-aligned. */ - delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask); - delta &= ~qemu_real_host_page_mask; + delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask()); + delta &= ~qemu_real_host_page_mask(); if (delta > size) { return; } start_pa += delta; size -= delta; - size &= qemu_real_host_page_mask; - if (!size || (start_pa & ~qemu_real_host_page_mask)) { + size &= qemu_real_host_page_mask(); + if (!size || (start_pa & ~qemu_real_host_page_mask())) { return; } diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h index 6f1fc174b3..535440f882 100644 --- a/target/i386/ops_sse.h +++ b/target/i386/ops_sse.h @@ -22,6 +22,7 @@ #if SHIFT == 0 #define Reg MMXReg +#define SIZE 8 #define XMM_ONLY(...) #define B(n) MMX_B(n) #define W(n) MMX_W(n) @@ -30,6 +31,7 @@ #define SUFFIX _mmx #else #define Reg ZMMReg +#define SIZE 16 #define XMM_ONLY(...) __VA_ARGS__ #define B(n) ZMM_B(n) #define W(n) ZMM_W(n) @@ -38,6 +40,22 @@ #define SUFFIX _xmm #endif +/* + * Copy the relevant parts of a Reg value around. In the case where + * sizeof(Reg) > SIZE, these helpers operate only on the lower bytes of + * a 64 byte ZMMReg, so we must copy only those and keep the top bytes + * untouched in the guest-visible destination destination register. + * Note that the "lower bytes" are placed last in memory on big-endian + * hosts, which store the vector backwards in memory. In that case the + * copy *starts* at B(SIZE - 1) and ends at B(0), the opposite of + * the little-endian case. + */ +#if HOST_BIG_ENDIAN +#define MOVE(d, r) memcpy(&((d).B(SIZE - 1)), &(r).B(SIZE - 1), SIZE) +#else +#define MOVE(d, r) memcpy(&(d).B(0), &(r).B(0), SIZE) +#endif + void glue(helper_psrlw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { int shift; @@ -516,7 +534,7 @@ void glue(helper_pshufw, SUFFIX)(Reg *d, Reg *s, int order) r.W(1) = s->W((order >> 2) & 3); r.W(2) = s->W((order >> 4) & 3); r.W(3) = s->W((order >> 6) & 3); - *d = r; + MOVE(*d, r); } #else void helper_shufps(Reg *d, Reg *s, int order) @@ -527,7 +545,7 @@ void helper_shufps(Reg *d, Reg *s, int order) r.L(1) = d->L((order >> 2) & 3); r.L(2) = s->L((order >> 4) & 3); r.L(3) = s->L((order >> 6) & 3); - *d = r; + MOVE(*d, r); } void helper_shufpd(Reg *d, Reg *s, int order) @@ -536,7 +554,7 @@ void helper_shufpd(Reg *d, Reg *s, int order) r.Q(0) = d->Q(order & 1); r.Q(1) = s->Q((order >> 1) & 1); - *d = r; + MOVE(*d, r); } void glue(helper_pshufd, SUFFIX)(Reg *d, Reg *s, int order) @@ -547,7 +565,7 @@ void glue(helper_pshufd, SUFFIX)(Reg *d, Reg *s, int order) r.L(1) = s->L((order >> 2) & 3); r.L(2) = s->L((order >> 4) & 3); r.L(3) = s->L((order >> 6) & 3); - *d = r; + MOVE(*d, r); } void glue(helper_pshuflw, SUFFIX)(Reg *d, Reg *s, int order) @@ -559,7 +577,7 @@ void glue(helper_pshuflw, SUFFIX)(Reg *d, Reg *s, int order) r.W(2) = s->W((order >> 4) & 3); r.W(3) = s->W((order >> 6) & 3); r.Q(1) = s->Q(1); - *d = r; + MOVE(*d, r); } void glue(helper_pshufhw, SUFFIX)(Reg *d, Reg *s, int order) @@ -571,7 +589,7 @@ void glue(helper_pshufhw, SUFFIX)(Reg *d, Reg *s, int order) r.W(5) = s->W(4 + ((order >> 2) & 3)); r.W(6) = s->W(4 + ((order >> 4) & 3)); r.W(7) = s->W(4 + ((order >> 6) & 3)); - *d = r; + MOVE(*d, r); } #endif @@ -937,7 +955,7 @@ void helper_haddps(CPUX86State *env, ZMMReg *d, ZMMReg *s) r.ZMM_S(1) = float32_add(d->ZMM_S(2), d->ZMM_S(3), &env->sse_status); r.ZMM_S(2) = float32_add(s->ZMM_S(0), s->ZMM_S(1), &env->sse_status); r.ZMM_S(3) = float32_add(s->ZMM_S(2), s->ZMM_S(3), &env->sse_status); - *d = r; + MOVE(*d, r); } void helper_haddpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) @@ -946,7 +964,7 @@ void helper_haddpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) r.ZMM_D(0) = float64_add(d->ZMM_D(0), d->ZMM_D(1), &env->sse_status); r.ZMM_D(1) = float64_add(s->ZMM_D(0), s->ZMM_D(1), &env->sse_status); - *d = r; + MOVE(*d, r); } void helper_hsubps(CPUX86State *env, ZMMReg *d, ZMMReg *s) @@ -957,7 +975,7 @@ void helper_hsubps(CPUX86State *env, ZMMReg *d, ZMMReg *s) r.ZMM_S(1) = float32_sub(d->ZMM_S(2), d->ZMM_S(3), &env->sse_status); r.ZMM_S(2) = float32_sub(s->ZMM_S(0), s->ZMM_S(1), &env->sse_status); r.ZMM_S(3) = float32_sub(s->ZMM_S(2), s->ZMM_S(3), &env->sse_status); - *d = r; + MOVE(*d, r); } void helper_hsubpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) @@ -966,7 +984,7 @@ void helper_hsubpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) r.ZMM_D(0) = float64_sub(d->ZMM_D(0), d->ZMM_D(1), &env->sse_status); r.ZMM_D(1) = float64_sub(s->ZMM_D(0), s->ZMM_D(1), &env->sse_status); - *d = r; + MOVE(*d, r); } void helper_addsubps(CPUX86State *env, ZMMReg *d, ZMMReg *s) @@ -1153,7 +1171,7 @@ void glue(helper_packsswb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.B(14) = satsb((int16_t)s->W(6)); r.B(15) = satsb((int16_t)s->W(7)); #endif - *d = r; + MOVE(*d, r); } void glue(helper_packuswb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -1180,7 +1198,7 @@ void glue(helper_packuswb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.B(14) = satub((int16_t)s->W(6)); r.B(15) = satub((int16_t)s->W(7)); #endif - *d = r; + MOVE(*d, r); } void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -1199,7 +1217,7 @@ void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.W(6) = satsw(s->L(2)); r.W(7) = satsw(s->L(3)); #endif - *d = r; + MOVE(*d, r); } #define UNPCK_OP(base_name, base) \ @@ -1227,7 +1245,7 @@ void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.B(14) = d->B((base << (SHIFT + 2)) + 7); \ r.B(15) = s->B((base << (SHIFT + 2)) + 7); \ ) \ - *d = r; \ + MOVE(*d, r); \ } \ \ void glue(helper_punpck ## base_name ## wd, SUFFIX)(CPUX86State *env,\ @@ -1245,7 +1263,7 @@ void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.W(6) = d->W((base << (SHIFT + 1)) + 3); \ r.W(7) = s->W((base << (SHIFT + 1)) + 3); \ ) \ - *d = r; \ + MOVE(*d, r); \ } \ \ void glue(helper_punpck ## base_name ## dq, SUFFIX)(CPUX86State *env,\ @@ -1259,7 +1277,7 @@ void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.L(2) = d->L((base << SHIFT) + 1); \ r.L(3) = s->L((base << SHIFT) + 1); \ ) \ - *d = r; \ + MOVE(*d, r); \ } \ \ XMM_ONLY( \ @@ -1272,7 +1290,7 @@ void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ r.Q(0) = d->Q(base); \ r.Q(1) = s->Q(base); \ - *d = r; \ + MOVE(*d, r); \ } \ ) @@ -1313,7 +1331,7 @@ void helper_pfacc(CPUX86State *env, MMXReg *d, MMXReg *s) r.MMX_S(0) = float32_add(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); - *d = r; + MOVE(*d, r); } void helper_pfadd(CPUX86State *env, MMXReg *d, MMXReg *s) @@ -1378,7 +1396,7 @@ void helper_pfnacc(CPUX86State *env, MMXReg *d, MMXReg *s) r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); r.MMX_S(1) = float32_sub(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); - *d = r; + MOVE(*d, r); } void helper_pfpnacc(CPUX86State *env, MMXReg *d, MMXReg *s) @@ -1387,7 +1405,7 @@ void helper_pfpnacc(CPUX86State *env, MMXReg *d, MMXReg *s) r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); - *d = r; + MOVE(*d, r); } void helper_pfrcp(CPUX86State *env, MMXReg *d, MMXReg *s) @@ -1424,7 +1442,7 @@ void helper_pswapd(CPUX86State *env, MMXReg *d, MMXReg *s) r.MMX_L(0) = s->MMX_L(1); r.MMX_L(1) = s->MMX_L(0); - *d = r; + MOVE(*d, r); } #endif @@ -1438,7 +1456,7 @@ void glue(helper_pshufb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.B(i) = (s->B(i) & 0x80) ? 0 : (d->B(s->B(i) & ((8 << SHIFT) - 1))); } - *d = r; + MOVE(*d, r); } void glue(helper_phaddw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -1455,7 +1473,7 @@ void glue(helper_phaddw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) XMM_ONLY(r.W(6) = (int16_t)s->W(4) + (int16_t)s->W(5)); XMM_ONLY(r.W(7) = (int16_t)s->W(6) + (int16_t)s->W(7)); - *d = r; + MOVE(*d, r); } void glue(helper_phaddd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -1467,7 +1485,7 @@ void glue(helper_phaddd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.L((1 << SHIFT) + 0) = (int32_t)s->L(0) + (int32_t)s->L(1); XMM_ONLY(r.L(3) = (int32_t)s->L(2) + (int32_t)s->L(3)); - *d = r; + MOVE(*d, r); } void glue(helper_phaddsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -1483,7 +1501,7 @@ void glue(helper_phaddsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) XMM_ONLY(r.W(6) = satsw((int16_t)s->W(4) + (int16_t)s->W(5))); XMM_ONLY(r.W(7) = satsw((int16_t)s->W(6) + (int16_t)s->W(7))); - *d = r; + MOVE(*d, r); } void glue(helper_pmaddubsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -1585,7 +1603,7 @@ void glue(helper_palignr, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, #undef SHR } - *d = r; + MOVE(*d, r); } #define XMM0 (env->xmm_regs[0]) @@ -1718,7 +1736,7 @@ void glue(helper_packusdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) r.W(5) = satuw((int32_t) s->L(1)); r.W(6) = satuw((int32_t) s->L(2)); r.W(7) = satuw((int32_t) s->L(3)); - *d = r; + MOVE(*d, r); } #define FMINSB(d, s) MIN((int8_t)d, (int8_t)s) @@ -1984,7 +2002,7 @@ void glue(helper_mpsadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, r.W(i) += abs1(d->B(d0 + 3) - s->B(s0 + 3)); } - *d = r; + MOVE(*d, r); } /* SSE4.2 op helpers */ @@ -1993,25 +2011,23 @@ SSE_HELPER_Q(helper_pcmpgtq, FCMPGTQ) static inline int pcmp_elen(CPUX86State *env, int reg, uint32_t ctrl) { - int val; + target_long val, limit; /* Presence of REX.W is indicated by a bit higher than 7 set */ if (ctrl >> 8) { - val = abs1((int64_t)env->regs[reg]); + val = (target_long)env->regs[reg]; } else { - val = abs1((int32_t)env->regs[reg]); + val = (int32_t)env->regs[reg]; } - if (ctrl & 1) { - if (val > 8) { - return 8; - } + limit = 8; } else { - if (val > 16) { - return 16; - } + limit = 16; } - return val; + if ((val > limit) || (val < -limit)) { + return limit; + } + return abs1(val); } static inline int pcmp_ilen(Reg *r, uint8_t ctrl) @@ -2324,3 +2340,4 @@ void glue(helper_aeskeygenassist, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, #undef L #undef Q #undef SUFFIX +#undef SIZE diff --git a/target/i386/ops_sse_header.h b/target/i386/ops_sse_header.h index e68af5c403..cef28f2aae 100644 --- a/target/i386/ops_sse_header.h +++ b/target/i386/ops_sse_header.h @@ -30,6 +30,9 @@ #define dh_ctype_Reg Reg * #define dh_ctype_ZMMReg ZMMReg * #define dh_ctype_MMXReg MMXReg * +#define dh_typecode_Reg dh_typecode_ptr +#define dh_typecode_ZMMReg dh_typecode_ptr +#define dh_typecode_MMXReg dh_typecode_ptr DEF_HELPER_3(glue(psrlw, SUFFIX), void, env, Reg, Reg) DEF_HELPER_3(glue(psraw, SUFFIX), void, env, Reg, Reg) diff --git a/target/i386/sev.c b/target/i386/sev.c index 025ff7a6f8..32f7dbac4e 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -531,12 +531,46 @@ e_free: return 1; } +static int sev_get_cpu0_id(int fd, guchar **id, size_t *id_len, Error **errp) +{ + guchar *id_data; + struct sev_user_data_get_id2 get_id2 = {}; + int err, r; + + /* query the ID length */ + r = sev_platform_ioctl(fd, SEV_GET_ID2, &get_id2, &err); + if (r < 0 && err != SEV_RET_INVALID_LEN) { + error_setg(errp, "SEV: Failed to get ID ret=%d fw_err=%d (%s)", + r, err, fw_error_to_str(err)); + return 1; + } + + id_data = g_new(guchar, get_id2.length); + get_id2.address = (unsigned long)id_data; + + r = sev_platform_ioctl(fd, SEV_GET_ID2, &get_id2, &err); + if (r < 0) { + error_setg(errp, "SEV: Failed to get ID ret=%d fw_err=%d (%s)", + r, err, fw_error_to_str(err)); + goto err; + } + + *id = id_data; + *id_len = get_id2.length; + return 0; + +err: + g_free(id_data); + return 1; +} + static SevCapability *sev_get_capabilities(Error **errp) { SevCapability *cap = NULL; guchar *pdh_data = NULL; guchar *cert_chain_data = NULL; - size_t pdh_len = 0, cert_chain_len = 0; + guchar *cpu0_id_data = NULL; + size_t pdh_len = 0, cert_chain_len = 0, cpu0_id_len = 0; uint32_t ebx; int fd; @@ -561,9 +595,14 @@ static SevCapability *sev_get_capabilities(Error **errp) goto out; } + if (sev_get_cpu0_id(fd, &cpu0_id_data, &cpu0_id_len, errp)) { + goto out; + } + cap = g_new0(SevCapability, 1); cap->pdh = g_base64_encode(pdh_data, pdh_len); cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len); + cap->cpu0_id = g_base64_encode(cpu0_id_data, cpu0_id_len); host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); cap->cbitpos = ebx & 0x3f; @@ -575,6 +614,7 @@ static SevCapability *sev_get_capabilities(Error **errp) cap->reduced_phys_bits = 1; out: + g_free(cpu0_id_data); g_free(pdh_data); g_free(cert_chain_data); close(fd); diff --git a/target/i386/tcg/bpt_helper.c b/target/i386/tcg/bpt_helper.c index b6c1fff16e..bc34ac27fe 100644 --- a/target/i386/tcg/bpt_helper.c +++ b/target/i386/tcg/bpt_helper.c @@ -22,7 +22,7 @@ #include "exec/helper-proto.h" #include "helper-tcg.h" -void QEMU_NORETURN helper_single_step(CPUX86State *env) +G_NORETURN void helper_single_step(CPUX86State *env) { #ifndef CONFIG_USER_ONLY check_hw_breakpoints(env, true); diff --git a/target/i386/tcg/excp_helper.c b/target/i386/tcg/excp_helper.c index bdae887d0a..c1ffa1c0ef 100644 --- a/target/i386/tcg/excp_helper.c +++ b/target/i386/tcg/excp_helper.c @@ -25,13 +25,13 @@ #include "exec/helper-proto.h" #include "helper-tcg.h" -void QEMU_NORETURN helper_raise_interrupt(CPUX86State *env, int intno, +G_NORETURN void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) { raise_interrupt(env, intno, 1, 0, next_eip_addend); } -void QEMU_NORETURN helper_raise_exception(CPUX86State *env, int exception_index) +G_NORETURN void helper_raise_exception(CPUX86State *env, int exception_index) { raise_exception(env, exception_index); } @@ -87,10 +87,11 @@ static int check_exception(CPUX86State *env, int intno, int *error_code, * env->eip value AFTER the interrupt instruction. It is only relevant if * is_int is TRUE. */ -static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, - int is_int, int error_code, - int next_eip_addend, - uintptr_t retaddr) +static G_NORETURN +void raise_interrupt2(CPUX86State *env, int intno, + int is_int, int error_code, + int next_eip_addend, + uintptr_t retaddr) { CPUState *cs = env_cpu(env); @@ -111,31 +112,31 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, /* shortcuts to generate exceptions */ -void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int, - int error_code, int next_eip_addend) +G_NORETURN void raise_interrupt(CPUX86State *env, int intno, int is_int, + int error_code, int next_eip_addend) { raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0); } -void QEMU_NORETURN raise_exception_err(CPUX86State *env, int exception_index, - int error_code) +G_NORETURN void raise_exception_err(CPUX86State *env, int exception_index, + int error_code) { raise_interrupt2(env, exception_index, 0, error_code, 0, 0); } -void QEMU_NORETURN raise_exception_err_ra(CPUX86State *env, int exception_index, - int error_code, uintptr_t retaddr) +G_NORETURN void raise_exception_err_ra(CPUX86State *env, int exception_index, + int error_code, uintptr_t retaddr) { raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr); } -void QEMU_NORETURN raise_exception(CPUX86State *env, int exception_index) +G_NORETURN void raise_exception(CPUX86State *env, int exception_index) { raise_interrupt2(env, exception_index, 0, 0, 0, 0); } -void QEMU_NORETURN raise_exception_ra(CPUX86State *env, int exception_index, - uintptr_t retaddr) +G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index, + uintptr_t retaddr) { raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); } diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index cdd8e9f947..30bc44fcf8 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -237,24 +237,37 @@ void helper_fldl_ST0(CPUX86State *env, uint64_t val) merge_exception_flags(env, old_flags); } +static FloatX80RoundPrec tmp_maximise_precision(float_status *st) +{ + FloatX80RoundPrec old = get_floatx80_rounding_precision(st); + set_floatx80_rounding_precision(floatx80_precision_x, st); + return old; +} + void helper_fildl_ST0(CPUX86State *env, int32_t val) { int new_fpstt; + FloatX80RoundPrec old = tmp_maximise_precision(&env->fp_status); new_fpstt = (env->fpstt - 1) & 7; env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ + + set_floatx80_rounding_precision(old, &env->fp_status); } void helper_fildll_ST0(CPUX86State *env, int64_t val) { int new_fpstt; + FloatX80RoundPrec old = tmp_maximise_precision(&env->fp_status); new_fpstt = (env->fpstt - 1) & 7; env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ + + set_floatx80_rounding_precision(old, &env->fp_status); } uint32_t helper_fsts_ST0(CPUX86State *env) @@ -2453,7 +2466,7 @@ static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, do_fstenv(env, ptr, data32, retaddr); - ptr += (14 << data32); + ptr += (target_ulong)14 << data32; for (i = 0; i < 8; i++) { tmp = ST(i); do_fstt(env, tmp, ptr, retaddr); @@ -2475,7 +2488,7 @@ static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, int i; do_fldenv(env, ptr, data32, retaddr); - ptr += (14 << data32); + ptr += (target_ulong)14 << data32; for (i = 0; i < 8; i++) { tmp = do_fldt(env, ptr, retaddr); diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index 0a4401e917..34167e2e29 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -69,27 +69,27 @@ static inline target_long lshift(target_long x, int n) void tcg_x86_init(void); /* excp_helper.c */ -void QEMU_NORETURN raise_exception(CPUX86State *env, int exception_index); -void QEMU_NORETURN raise_exception_ra(CPUX86State *env, int exception_index, - uintptr_t retaddr); -void QEMU_NORETURN raise_exception_err(CPUX86State *env, int exception_index, - int error_code); -void QEMU_NORETURN raise_exception_err_ra(CPUX86State *env, int exception_index, - int error_code, uintptr_t retaddr); -void QEMU_NORETURN raise_interrupt(CPUX86State *nenv, int intno, int is_int, - int error_code, int next_eip_addend); +G_NORETURN void raise_exception(CPUX86State *env, int exception_index); +G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index, + uintptr_t retaddr); +G_NORETURN void raise_exception_err(CPUX86State *env, int exception_index, + int error_code); +G_NORETURN void raise_exception_err_ra(CPUX86State *env, int exception_index, + int error_code, uintptr_t retaddr); +G_NORETURN void raise_interrupt(CPUX86State *nenv, int intno, int is_int, + int error_code, int next_eip_addend); /* cc_helper.c */ extern const uint8_t parity_table[256]; /* misc_helper.c */ void cpu_load_eflags(CPUX86State *env, int eflags, int update_mask); -void do_pause(CPUX86State *env) QEMU_NORETURN; +G_NORETURN void do_pause(CPUX86State *env); /* sysemu/svm_helper.c */ #ifndef CONFIG_USER_ONLY -void QEMU_NORETURN cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, - uint64_t exit_info_1, uintptr_t retaddr); +G_NORETURN void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, + uint64_t exit_info_1, uintptr_t retaddr); void do_vmexit(CPUX86State *env); #endif diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index 24a0eaa3d5..5f7a3061ca 100644 --- a/target/i386/tcg/misc_helper.c +++ b/target/i386/tcg/misc_helper.c @@ -81,7 +81,7 @@ void helper_rdtscp(CPUX86State *env) env->regs[R_ECX] = (uint32_t)(env->tsc_aux); } -void QEMU_NORETURN helper_rdpmc(CPUX86State *env) +G_NORETURN void helper_rdpmc(CPUX86State *env) { if (((env->cr[4] & CR4_PCE_MASK) == 0 ) && ((env->hflags & HF_CPL_MASK) != 0)) { @@ -94,7 +94,7 @@ void QEMU_NORETURN helper_rdpmc(CPUX86State *env) raise_exception_err(env, EXCP06_ILLOP, 0); } -void QEMU_NORETURN do_pause(CPUX86State *env) +G_NORETURN void do_pause(CPUX86State *env) { CPUState *cs = env_cpu(env); @@ -103,7 +103,7 @@ void QEMU_NORETURN do_pause(CPUX86State *env) cpu_loop_exit(cs); } -void QEMU_NORETURN helper_pause(CPUX86State *env, int next_eip_addend) +G_NORETURN void helper_pause(CPUX86State *env, int next_eip_addend) { cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0, GETPC()); env->eip += next_eip_addend; diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index baa905a0cd..bffd82923f 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -28,6 +28,42 @@ #include "helper-tcg.h" #include "seg_helper.h" +int get_pg_mode(CPUX86State *env) +{ + int pg_mode = 0; + if (!(env->cr[0] & CR0_PG_MASK)) { + return 0; + } + if (env->cr[0] & CR0_WP_MASK) { + pg_mode |= PG_MODE_WP; + } + if (env->cr[4] & CR4_PAE_MASK) { + pg_mode |= PG_MODE_PAE; + if (env->efer & MSR_EFER_NXE) { + pg_mode |= PG_MODE_NXE; + } + } + if (env->cr[4] & CR4_PSE_MASK) { + pg_mode |= PG_MODE_PSE; + } + if (env->cr[4] & CR4_SMEP_MASK) { + pg_mode |= PG_MODE_SMEP; + } + if (env->hflags & HF_LMA_MASK) { + pg_mode |= PG_MODE_LMA; + if (env->cr[4] & CR4_PKE_MASK) { + pg_mode |= PG_MODE_PKE; + } + if (env->cr[4] & CR4_PKS_MASK) { + pg_mode |= PG_MODE_PKS; + } + if (env->cr[4] & CR4_LA57_MASK) { + pg_mode |= PG_MODE_LA57; + } + } + return pg_mode; +} + /* return non zero if error */ static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, uint32_t *e2_ptr, int selector, @@ -794,7 +830,9 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) { X86CPU *cpu = env_archcpu(env); - int index; + int index, pg_mode; + target_ulong rsp; + int32_t sext; #if 0 printf("TR: base=" TARGET_FMT_lx " limit=%x\n", @@ -808,7 +846,17 @@ static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) if ((index + 7) > env->tr.limit) { raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc); } - return cpu_ldq_kernel(env, env->tr.base + index); + + rsp = cpu_ldq_kernel(env, env->tr.base + index); + + /* test virtual address sign extension */ + pg_mode = get_pg_mode(env); + sext = (int64_t)rsp >> (pg_mode & PG_MODE_LA57 ? 56 : 47); + if (sext != 0 && sext != -1) { + raise_exception_err(env, EXCP0C_STACK, 0); + } + + return rsp; } /* 64 bit interrupt */ diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c index 5ba739fbed..e1b6d88683 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/sysemu/excp_helper.c @@ -19,41 +19,9 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/exec-all.h" #include "tcg/helper-tcg.h" -int get_pg_mode(CPUX86State *env) -{ - int pg_mode = 0; - if (env->cr[0] & CR0_WP_MASK) { - pg_mode |= PG_MODE_WP; - } - if (env->cr[4] & CR4_PAE_MASK) { - pg_mode |= PG_MODE_PAE; - } - if (env->cr[4] & CR4_PSE_MASK) { - pg_mode |= PG_MODE_PSE; - } - if (env->cr[4] & CR4_PKE_MASK) { - pg_mode |= PG_MODE_PKE; - } - if (env->cr[4] & CR4_PKS_MASK) { - pg_mode |= PG_MODE_PKS; - } - if (env->cr[4] & CR4_SMEP_MASK) { - pg_mode |= PG_MODE_SMEP; - } - if (env->cr[4] & CR4_LA57_MASK) { - pg_mode |= PG_MODE_LA57; - } - if (env->hflags & HF_LMA_MASK) { - pg_mode |= PG_MODE_LMA; - } - if (env->efer & MSR_EFER_NXE) { - pg_mode |= PG_MODE_NXE; - } - return pg_mode; -} - #define PG_ERROR_OK (-1) typedef hwaddr (*MMUTranslateFunc)(CPUState *cs, hwaddr gphys, MMUAccessType access_type, @@ -278,9 +246,7 @@ do_check_protect_pse36: *prot |= PAGE_EXEC; } - if (!(pg_mode & PG_MODE_LMA)) { - pkr = 0; - } else if (ptep & PG_USER_MASK) { + if (ptep & PG_USER_MASK) { pkr = pg_mode & PG_MODE_PKE ? env->pkru : 0; } else { pkr = pg_mode & PG_MODE_PKS ? env->pkrs : 0; @@ -343,8 +309,7 @@ do_check_protect_pse36: if (is_user) error_code |= PG_ERROR_U_MASK; if (is_write1 == 2 && - (((pg_mode & PG_MODE_NXE) && (pg_mode & PG_MODE_PAE)) || - (pg_mode & PG_MODE_SMEP))) + ((pg_mode & PG_MODE_NXE) || (pg_mode & PG_MODE_SMEP))) error_code |= PG_ERROR_I_D_MASK; return error_code; } diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index 9ccaa054c4..1328aa656f 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -23,6 +23,7 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "exec/address-spaces.h" +#include "exec/exec-all.h" #include "tcg/helper-tcg.h" void helper_outb(CPUX86State *env, uint32_t port, uint32_t data) @@ -470,7 +471,8 @@ void helper_flush_page(CPUX86State *env, target_ulong addr) tlb_flush_page(env_cpu(env), addr); } -static void QEMU_NORETURN do_hlt(CPUX86State *env) +static G_NORETURN +void do_hlt(CPUX86State *env) { CPUState *cs = env_cpu(env); @@ -480,7 +482,7 @@ static void QEMU_NORETURN do_hlt(CPUX86State *env) cpu_loop_exit(cs); } -void QEMU_NORETURN helper_hlt(CPUX86State *env, int next_eip_addend) +G_NORETURN void helper_hlt(CPUX86State *env, int next_eip_addend) { cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0, GETPC()); env->eip += next_eip_addend; @@ -497,7 +499,7 @@ void helper_monitor(CPUX86State *env, target_ulong ptr) cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0, GETPC()); } -void QEMU_NORETURN helper_mwait(CPUX86State *env, int next_eip_addend) +G_NORETURN void helper_mwait(CPUX86State *env, int next_eip_addend) { CPUState *cs = env_cpu(env); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index ad8843f5aa..e74c4eabb1 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -365,7 +365,7 @@ static void gen_update_cc_op(DisasContext *s) #endif /* !TARGET_X86_64 */ -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define REG_B_OFFSET (sizeof(target_ulong) - 1) #define REG_H_OFFSET (sizeof(target_ulong) - 2) #define REG_W_OFFSET (sizeof(target_ulong) - 2) @@ -2600,15 +2600,17 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) gen_illegal_opcode(s); if (qemu_loglevel_mask(LOG_UNIMP)) { - FILE *logfile = qemu_log_lock(); - target_ulong pc = s->pc_start, end = s->pc; + FILE *logfile = qemu_log_trylock(); + if (logfile) { + target_ulong pc = s->pc_start, end = s->pc; - qemu_log("ILLOPC: " TARGET_FMT_lx ":", pc); - for (; pc < end; ++pc) { - qemu_log(" %02x", cpu_ldub_code(env, pc)); + fprintf(logfile, "ILLOPC: " TARGET_FMT_lx ":", pc); + for (; pc < end; ++pc) { + fprintf(logfile, " %02x", cpu_ldub_code(env, pc)); + } + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); } - qemu_log("\n"); - qemu_log_unlock(logfile); } } @@ -4529,10 +4531,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, sse_fn_ppi(s->ptr0, s->ptr1, tcg_const_i32(val)); break; case 0xc2: - /* compare insns */ - val = x86_ldub_code(env, s); - if (val >= 8) - goto unknown_op; + /* compare insns, bits 7:3 (7:5 for AVX) are ignored */ + val = x86_ldub_code(env, s) & 7; sse_fn_epp = sse_op_table4[val][b1]; tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); @@ -8710,12 +8710,12 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } static void i386_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu) + CPUState *cpu, FILE *logfile) { DisasContext *dc = container_of(dcbase, DisasContext, base); - qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); + target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); } static const TranslatorOps i386_tr_ops = { diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 6bc47c5309..e8dc4b3a47 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -64,8 +64,8 @@ static void whpx_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + cpu->thread = g_new0(QemuThread, 1); + cpu->halt_cond = g_new0(QemuCond, 1); qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/WHPX", cpu->cpu_index); @@ -83,17 +83,24 @@ static void whpx_kick_vcpu_thread(CPUState *cpu) } } +static bool whpx_vcpu_thread_is_idle(CPUState *cpu) +{ + return !whpx_apic_in_platform(); +} + static void whpx_accel_ops_class_init(ObjectClass *oc, void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); ops->create_vcpu_thread = whpx_start_vcpu_thread; ops->kick_vcpu_thread = whpx_kick_vcpu_thread; + ops->cpu_thread_is_idle = whpx_vcpu_thread_is_idle; ops->synchronize_post_reset = whpx_cpu_synchronize_post_reset; ops->synchronize_post_init = whpx_cpu_synchronize_post_init; ops->synchronize_state = whpx_cpu_synchronize_state; ops->synchronize_pre_loadvm = whpx_cpu_synchronize_pre_loadvm; + ops->synchronize_pre_resume = whpx_cpu_synchronize_pre_resume; } static const TypeInfo whpx_accel_ops_type = { diff --git a/target/i386/whpx/whpx-accel-ops.h b/target/i386/whpx/whpx-accel-ops.h index 2dee6d61ea..b5102dd1ee 100644 --- a/target/i386/whpx/whpx-accel-ops.h +++ b/target/i386/whpx/whpx-accel-ops.h @@ -21,6 +21,7 @@ void whpx_cpu_synchronize_state(CPUState *cpu); void whpx_cpu_synchronize_post_reset(CPUState *cpu); void whpx_cpu_synchronize_post_init(CPUState *cpu); void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu); +void whpx_cpu_synchronize_pre_resume(bool step_pending); /* state subset only touched by the VCPU itself during runtime */ #define WHPX_SET_RUNTIME_STATE 1 diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index ef896da0a2..b625ad5bbb 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -12,7 +12,7 @@ #include "cpu.h" #include "exec/address-spaces.h" #include "exec/ioport.h" -#include "qemu-common.h" +#include "exec/gdbstub.h" #include "qemu/accel.h" #include "sysemu/whpx.h" #include "sysemu/cpus.h" @@ -148,6 +148,87 @@ struct whpx_register_set { WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)]; }; +/* + * The current implementation of instruction stepping sets the TF flag + * in RFLAGS, causing the CPU to raise an INT1 after each instruction. + * This corresponds to the WHvX64ExceptionTypeDebugTrapOrFault exception. + * + * This approach has a few limitations: + * 1. Stepping over a PUSHF/SAHF instruction will save the TF flag + * along with the other flags, possibly restoring it later. It would + * result in another INT1 when the flags are restored, triggering + * a stop in gdb that could be cleared by doing another step. + * + * Stepping over a POPF/LAHF instruction will let it overwrite the + * TF flags, ending the stepping mode. + * + * 2. Stepping over an instruction raising an exception (e.g. INT, DIV, + * or anything that could result in a page fault) will save the flags + * to the stack, clear the TF flag, and let the guest execute the + * handler. Normally, the guest will restore the original flags, + * that will continue single-stepping. + * + * 3. Debuggers running on the guest may wish to set TF to do instruction + * stepping. INT1 events generated by it would be intercepted by us, + * as long as the gdb is connected to QEMU. + * + * In practice this means that: + * 1. Stepping through flags-modifying instructions may cause gdb to + * continue or stop in unexpected places. This will be fully recoverable + * and will not crash the target. + * + * 2. Stepping over an instruction that triggers an exception will step + * over the exception handler, not into it. + * + * 3. Debugging the guest via gdb, while running debugger on the guest + * at the same time may lead to unexpected effects. Removing all + * breakpoints set via QEMU will prevent any further interference + * with the guest-level debuggers. + * + * The limitations can be addressed as shown below: + * 1. PUSHF/SAHF/POPF/LAHF/IRET instructions can be emulated instead of + * stepping through them. The exact semantics of the instructions is + * defined in the "Combined Volume Set of Intel 64 and IA-32 + * Architectures Software Developer's Manuals", however it involves a + * fair amount of corner cases due to compatibility with real mode, + * virtual 8086 mode, and differences between 64-bit and 32-bit modes. + * + * 2. We could step into the guest's exception handlers using the following + * sequence: + * a. Temporarily enable catching of all exception types via + * whpx_set_exception_exit_bitmap(). + * b. Once an exception is intercepted, read the IDT/GDT and locate + * the original handler. + * c. Patch the original handler, injecting an INT3 at the beginning. + * d. Update the exception exit bitmap to only catch the + * WHvX64ExceptionTypeBreakpointTrap exception. + * e. Let the affected CPU run in the exclusive mode. + * f. Restore the original handler and the exception exit bitmap. + * Note that handling all corner cases related to IDT/GDT is harder + * than it may seem. See x86_cpu_get_phys_page_attrs_debug() for a + * rough idea. + * + * 3. In order to properly support guest-level debugging in parallel with + * the QEMU-level debugging, we would need to be able to pass some INT1 + * events to the guest. This could be done via the following methods: + * a. Using the WHvRegisterPendingEvent register. As of Windows 21H1, + * it seems to only work for interrupts and not software + * exceptions. + * b. Locating and patching the original handler by parsing IDT/GDT. + * This involves relatively complex logic outlined in the previous + * paragraph. + * c. Emulating the exception invocation (i.e. manually updating RIP, + * RFLAGS, and pushing the old values to stack). This is even more + * complicated than the previous option, since it involves checking + * CPL, gate attributes, and doing various adjustments depending + * on the current CPU mode, whether the CPL is changing, etc. + */ +typedef enum WhpxStepMode { + WHPX_STEP_NONE = 0, + /* Halt other VCPUs */ + WHPX_STEP_EXCLUSIVE, +} WhpxStepMode; + struct whpx_vcpu { WHV_EMULATOR_HANDLE emulator; bool window_registered; @@ -221,7 +302,7 @@ static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs) static int whpx_set_tsc(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; WHV_REGISTER_VALUE tsc_val; HRESULT hr; @@ -256,11 +337,26 @@ static int whpx_set_tsc(CPUState *cpu) return 0; } +/* + * The CR8 register in the CPU is mapped to the TPR register of the APIC, + * however, they use a slightly different encoding. Specifically: + * + * APIC.TPR[bits 7:4] = CR8[bits 3:0] + * + * This mechanism is described in section 10.8.6.1 of Volume 3 of Intel 64 + * and IA-32 Architectures Software Developer's Manual. + */ + +static uint64_t whpx_apic_tpr_to_cr8(uint64_t tpr) +{ + return tpr >> 4; +} + static void whpx_set_registers(CPUState *cpu, int level) { struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); struct whpx_register_set vcxt; HRESULT hr; @@ -284,7 +380,7 @@ static void whpx_set_registers(CPUState *cpu, int level) v86 = (env->eflags & VM_MASK); r86 = !(env->cr[0] & CR0_PE_MASK); - vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state); + vcpu->tpr = whpx_apic_tpr_to_cr8(cpu_get_apic_tpr(x86_cpu->apic_state)); vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state); idx = 0; @@ -428,7 +524,7 @@ static void whpx_set_registers(CPUState *cpu, int level) static int whpx_get_tsc(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; WHV_REGISTER_VALUE tsc_val; HRESULT hr; @@ -449,7 +545,7 @@ static void whpx_get_registers(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); struct whpx_register_set vcxt; uint64_t tpr, apic_base; @@ -475,6 +571,17 @@ static void whpx_get_registers(CPUState *cpu) hr); } + if (whpx_apic_in_platform()) { + /* + * Fetch the TPR value from the emulated APIC. It may get overwritten + * below with the value from CR8 returned by + * WHvGetVirtualProcessorRegisters(). + */ + whpx_apic_get(x86_cpu->apic_state); + vcpu->tpr = whpx_apic_tpr_to_cr8( + cpu_get_apic_tpr(x86_cpu->apic_state)); + } + idx = 0; /* Indexes for first 16 registers match between HV and QEMU definitions */ @@ -604,6 +711,8 @@ static void whpx_get_registers(CPUState *cpu) whpx_apic_get(x86_cpu->apic_state); } + x86_update_hflags(env); + return; } @@ -758,9 +867,520 @@ static int whpx_handle_portio(CPUState *cpu, return 0; } +/* + * Controls whether we should intercept various exceptions on the guest, + * namely breakpoint/single-step events. + * + * The 'exceptions' argument accepts a bitmask, e.g: + * (1 << WHvX64ExceptionTypeDebugTrapOrFault) | (...) + */ +static HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions) +{ + struct whpx_state *whpx = &whpx_global; + WHV_PARTITION_PROPERTY prop = { 0, }; + HRESULT hr; + + if (exceptions == whpx->exception_exit_bitmap) { + return S_OK; + } + + prop.ExceptionExitBitmap = exceptions; + + hr = whp_dispatch.WHvSetPartitionProperty( + whpx->partition, + WHvPartitionPropertyCodeExceptionExitBitmap, + &prop, + sizeof(WHV_PARTITION_PROPERTY)); + + if (SUCCEEDED(hr)) { + whpx->exception_exit_bitmap = exceptions; + } + + return hr; +} + + +/* + * This function is called before/after stepping over a single instruction. + * It will update the CPU registers to arm/disarm the instruction stepping + * accordingly. + */ +static HRESULT whpx_vcpu_configure_single_stepping(CPUState *cpu, + bool set, + uint64_t *exit_context_rflags) +{ + WHV_REGISTER_NAME reg_name; + WHV_REGISTER_VALUE reg_value; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + /* + * If we are trying to step over a single instruction, we need to set the + * TF bit in rflags. Otherwise, clear it. + */ + reg_name = WHvX64RegisterRflags; + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + ®_name, + 1, + ®_value); + + if (FAILED(hr)) { + error_report("WHPX: Failed to get rflags, hr=%08lx", hr); + return hr; + } + + if (exit_context_rflags) { + assert(*exit_context_rflags == reg_value.Reg64); + } + + if (set) { + /* Raise WHvX64ExceptionTypeDebugTrapOrFault after each instruction */ + reg_value.Reg64 |= TF_MASK; + } else { + reg_value.Reg64 &= ~TF_MASK; + } + + if (exit_context_rflags) { + *exit_context_rflags = reg_value.Reg64; + } + + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + ®_name, + 1, + ®_value); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set rflags," + " hr=%08lx", + hr); + return hr; + } + + reg_name = WHvRegisterInterruptState; + reg_value.Reg64 = 0; + + /* Suspend delivery of hardware interrupts during single-stepping. */ + reg_value.InterruptState.InterruptShadow = set != 0; + + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + ®_name, + 1, + ®_value); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set InterruptState," + " hr=%08lx", + hr); + return hr; + } + + if (!set) { + /* + * We have just finished stepping over a single instruction, + * and intercepted the INT1 generated by it. + * We need to now hide the INT1 from the guest, + * as it would not be expecting it. + */ + + reg_name = WHvX64RegisterPendingDebugException; + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + ®_name, + 1, + ®_value); + + if (FAILED(hr)) { + error_report("WHPX: Failed to get pending debug exceptions," + "hr=%08lx", hr); + return hr; + } + + if (reg_value.PendingDebugException.SingleStep) { + reg_value.PendingDebugException.SingleStep = 0; + + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + ®_name, + 1, + ®_value); + + if (FAILED(hr)) { + error_report("WHPX: Failed to clear pending debug exceptions," + "hr=%08lx", hr); + return hr; + } + } + + } + + return S_OK; +} + +/* Tries to find a breakpoint at the specified address. */ +static struct whpx_breakpoint *whpx_lookup_breakpoint_by_addr(uint64_t address) +{ + struct whpx_state *whpx = &whpx_global; + int i; + + if (whpx->breakpoints.breakpoints) { + for (i = 0; i < whpx->breakpoints.breakpoints->used; i++) { + if (address == whpx->breakpoints.breakpoints->data[i].address) { + return &whpx->breakpoints.breakpoints->data[i]; + } + } + } + + return NULL; +} + +/* + * Linux uses int3 (0xCC) during startup (see int3_selftest()) and for + * debugging user-mode applications. Since the WHPX API does not offer + * an easy way to pass the intercepted exception back to the guest, we + * resort to using INT1 instead, and let the guest always handle INT3. + */ +static const uint8_t whpx_breakpoint_instruction = 0xF1; + +/* + * The WHPX QEMU backend implements breakpoints by writing the INT1 + * instruction into memory (ignoring the DRx registers). This raises a few + * issues that need to be carefully handled: + * + * 1. Although unlikely, other parts of QEMU may set multiple breakpoints + * at the same location, and later remove them in arbitrary order. + * This should not cause memory corruption, and should only remove the + * physical breakpoint instruction when the last QEMU breakpoint is gone. + * + * 2. Writing arbitrary virtual memory may fail if it's not mapped to a valid + * physical location. Hence, physically adding/removing a breakpoint can + * theoretically fail at any time. We need to keep track of it. + * + * The function below rebuilds a list of low-level breakpoints (one per + * address, tracking the original instruction and any errors) from the list of + * high-level breakpoints (set via cpu_breakpoint_insert()). + * + * In order to optimize performance, this function stores the list of + * high-level breakpoints (a.k.a. CPU breakpoints) used to compute the + * low-level ones, so that it won't be re-invoked until these breakpoints + * change. + * + * Note that this function decides which breakpoints should be inserted into, + * memory, but doesn't actually do it. The memory accessing is done in + * whpx_apply_breakpoints(). + */ +static void whpx_translate_cpu_breakpoints( + struct whpx_breakpoints *breakpoints, + CPUState *cpu, + int cpu_breakpoint_count) +{ + CPUBreakpoint *bp; + int cpu_bp_index = 0; + + breakpoints->original_addresses = + g_renew(vaddr, breakpoints->original_addresses, cpu_breakpoint_count); + + breakpoints->original_address_count = cpu_breakpoint_count; + + int max_breakpoints = cpu_breakpoint_count + + (breakpoints->breakpoints ? breakpoints->breakpoints->used : 0); + + struct whpx_breakpoint_collection *new_breakpoints = + (struct whpx_breakpoint_collection *)g_malloc0( + sizeof(struct whpx_breakpoint_collection) + + max_breakpoints * sizeof(struct whpx_breakpoint)); + + new_breakpoints->allocated = max_breakpoints; + new_breakpoints->used = 0; + + /* + * 1. Preserve all old breakpoints that could not be automatically + * cleared when the CPU got stopped. + */ + if (breakpoints->breakpoints) { + int i; + for (i = 0; i < breakpoints->breakpoints->used; i++) { + if (breakpoints->breakpoints->data[i].state != WHPX_BP_CLEARED) { + new_breakpoints->data[new_breakpoints->used++] = + breakpoints->breakpoints->data[i]; + } + } + } + + /* 2. Map all CPU breakpoints to WHPX breakpoints */ + QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { + int i; + bool found = false; + + /* This will be used to detect changed CPU breakpoints later. */ + breakpoints->original_addresses[cpu_bp_index++] = bp->pc; + + for (i = 0; i < new_breakpoints->used; i++) { + /* + * WARNING: This loop has O(N^2) complexity, where N is the + * number of breakpoints. It should not be a bottleneck in + * real-world scenarios, since it only needs to run once after + * the breakpoints have been modified. + * If this ever becomes a concern, it can be optimized by storing + * high-level breakpoint objects in a tree or hash map. + */ + + if (new_breakpoints->data[i].address == bp->pc) { + /* There was already a breakpoint at this address. */ + if (new_breakpoints->data[i].state == WHPX_BP_CLEAR_PENDING) { + new_breakpoints->data[i].state = WHPX_BP_SET; + } else if (new_breakpoints->data[i].state == WHPX_BP_SET) { + new_breakpoints->data[i].state = WHPX_BP_SET_PENDING; + } + + found = true; + break; + } + } + + if (!found && new_breakpoints->used < new_breakpoints->allocated) { + /* No WHPX breakpoint at this address. Create one. */ + new_breakpoints->data[new_breakpoints->used].address = bp->pc; + new_breakpoints->data[new_breakpoints->used].state = + WHPX_BP_SET_PENDING; + new_breakpoints->used++; + } + } + + if (breakpoints->breakpoints) { + /* + * Free the previous breakpoint list. This can be optimized by keeping + * it as shadow buffer for the next computation instead of freeing + * it immediately. + */ + g_free(breakpoints->breakpoints); + } + + breakpoints->breakpoints = new_breakpoints; +} + +/* + * Physically inserts/removes the breakpoints by reading and writing the + * physical memory, keeping a track of the failed attempts. + * + * Passing resuming=true will try to set all previously unset breakpoints. + * Passing resuming=false will remove all inserted ones. + */ +static void whpx_apply_breakpoints( + struct whpx_breakpoint_collection *breakpoints, + CPUState *cpu, + bool resuming) +{ + int i, rc; + if (!breakpoints) { + return; + } + + for (i = 0; i < breakpoints->used; i++) { + /* Decide what to do right now based on the last known state. */ + WhpxBreakpointState state = breakpoints->data[i].state; + switch (state) { + case WHPX_BP_CLEARED: + if (resuming) { + state = WHPX_BP_SET_PENDING; + } + break; + case WHPX_BP_SET_PENDING: + if (!resuming) { + state = WHPX_BP_CLEARED; + } + break; + case WHPX_BP_SET: + if (!resuming) { + state = WHPX_BP_CLEAR_PENDING; + } + break; + case WHPX_BP_CLEAR_PENDING: + if (resuming) { + state = WHPX_BP_SET; + } + break; + } + + if (state == WHPX_BP_SET_PENDING) { + /* Remember the original instruction. */ + rc = cpu_memory_rw_debug(cpu, + breakpoints->data[i].address, + &breakpoints->data[i].original_instruction, + 1, + false); + + if (!rc) { + /* Write the breakpoint instruction. */ + rc = cpu_memory_rw_debug(cpu, + breakpoints->data[i].address, + (void *)&whpx_breakpoint_instruction, + 1, + true); + } + + if (!rc) { + state = WHPX_BP_SET; + } + + } + + if (state == WHPX_BP_CLEAR_PENDING) { + /* Restore the original instruction. */ + rc = cpu_memory_rw_debug(cpu, + breakpoints->data[i].address, + &breakpoints->data[i].original_instruction, + 1, + true); + + if (!rc) { + state = WHPX_BP_CLEARED; + } + } + + breakpoints->data[i].state = state; + } +} + +/* + * This function is called when the a VCPU is about to start and no other + * VCPUs have been started so far. Since the VCPU start order could be + * arbitrary, it doesn't have to be VCPU#0. + * + * It is used to commit the breakpoints into memory, and configure WHPX + * to intercept debug exceptions. + * + * Note that whpx_set_exception_exit_bitmap() cannot be called if one or + * more VCPUs are already running, so this is the best place to do it. + */ +static int whpx_first_vcpu_starting(CPUState *cpu) +{ + struct whpx_state *whpx = &whpx_global; + HRESULT hr; + + g_assert(qemu_mutex_iothread_locked()); + + if (!QTAILQ_EMPTY(&cpu->breakpoints) || + (whpx->breakpoints.breakpoints && + whpx->breakpoints.breakpoints->used)) { + CPUBreakpoint *bp; + int i = 0; + bool update_pending = false; + + QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { + if (i >= whpx->breakpoints.original_address_count || + bp->pc != whpx->breakpoints.original_addresses[i]) { + update_pending = true; + } + + i++; + } + + if (i != whpx->breakpoints.original_address_count) { + update_pending = true; + } + + if (update_pending) { + /* + * The CPU breakpoints have changed since the last call to + * whpx_translate_cpu_breakpoints(). WHPX breakpoints must + * now be recomputed. + */ + whpx_translate_cpu_breakpoints(&whpx->breakpoints, cpu, i); + } + + /* Actually insert the breakpoints into the memory. */ + whpx_apply_breakpoints(whpx->breakpoints.breakpoints, cpu, true); + } + + uint64_t exception_mask; + if (whpx->step_pending || + (whpx->breakpoints.breakpoints && + whpx->breakpoints.breakpoints->used)) { + /* + * We are either attempting to single-step one or more CPUs, or + * have one or more breakpoints enabled. Both require intercepting + * the WHvX64ExceptionTypeBreakpointTrap exception. + */ + + exception_mask = 1UL << WHvX64ExceptionTypeDebugTrapOrFault; + } else { + /* Let the guest handle all exceptions. */ + exception_mask = 0; + } + + hr = whpx_set_exception_exit_bitmap(exception_mask); + if (!SUCCEEDED(hr)) { + error_report("WHPX: Failed to update exception exit mask," + "hr=%08lx.", hr); + return 1; + } + + return 0; +} + +/* + * This function is called when the last VCPU has finished running. + * It is used to remove any previously set breakpoints from memory. + */ +static int whpx_last_vcpu_stopping(CPUState *cpu) +{ + whpx_apply_breakpoints(whpx_global.breakpoints.breakpoints, cpu, false); + return 0; +} + +/* Returns the address of the next instruction that is about to be executed. */ +static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid) +{ + if (cpu->vcpu_dirty) { + /* The CPU registers have been modified by other parts of QEMU. */ + CPUArchState *env = (CPUArchState *)(cpu->env_ptr); + return env->eip; + } else if (exit_context_valid) { + /* + * The CPU registers have not been modified by neither other parts + * of QEMU, nor this port by calling WHvSetVirtualProcessorRegisters(). + * This is the most common case. + */ + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + return vcpu->exit_ctx.VpContext.Rip; + } else { + /* + * The CPU registers have been modified by a call to + * WHvSetVirtualProcessorRegisters() and must be re-queried from + * the target. + */ + WHV_REGISTER_VALUE reg_value; + WHV_REGISTER_NAME reg_name = WHvX64RegisterRip; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + ®_name, + 1, + ®_value); + + if (FAILED(hr)) { + error_report("WHPX: Failed to get PC, hr=%08lx", hr); + return 0; + } + + return reg_value.Reg64; + } +} + static int whpx_handle_halt(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; int ret = 0; qemu_mutex_lock_iothread(); @@ -781,7 +1401,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) HRESULT hr; struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); int irq; uint8_t tpr; @@ -903,7 +1523,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) static void whpx_vcpu_post_run(CPUState *cpu) { struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); env->eflags = vcpu->exit_ctx.VpContext.Rflags; @@ -927,7 +1547,7 @@ static void whpx_vcpu_post_run(CPUState *cpu) static void whpx_vcpu_process_async_events(CPUState *cpu) { - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); @@ -969,17 +1589,75 @@ static int whpx_vcpu_run(CPUState *cpu) HRESULT hr; struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + struct whpx_breakpoint *stepped_over_bp = NULL; + WhpxStepMode exclusive_step_mode = WHPX_STEP_NONE; int ret; - whpx_vcpu_process_async_events(cpu); - if (cpu->halted && !whpx_apic_in_platform()) { - cpu->exception_index = EXCP_HLT; - qatomic_set(&cpu->exit_request, false); - return 0; + g_assert(qemu_mutex_iothread_locked()); + + if (whpx->running_cpus++ == 0) { + /* Insert breakpoints into memory, update exception exit bitmap. */ + ret = whpx_first_vcpu_starting(cpu); + if (ret != 0) { + return ret; + } + } + + if (whpx->breakpoints.breakpoints && + whpx->breakpoints.breakpoints->used > 0) + { + uint64_t pc = whpx_vcpu_get_pc(cpu, true); + stepped_over_bp = whpx_lookup_breakpoint_by_addr(pc); + if (stepped_over_bp && stepped_over_bp->state != WHPX_BP_SET) { + stepped_over_bp = NULL; + } + + if (stepped_over_bp) { + /* + * We are trying to run the instruction overwritten by an active + * breakpoint. We will temporarily disable the breakpoint, suspend + * other CPUs, and step over the instruction. + */ + exclusive_step_mode = WHPX_STEP_EXCLUSIVE; + } + } + + if (exclusive_step_mode == WHPX_STEP_NONE) { + whpx_vcpu_process_async_events(cpu); + if (cpu->halted && !whpx_apic_in_platform()) { + cpu->exception_index = EXCP_HLT; + qatomic_set(&cpu->exit_request, false); + return 0; + } } qemu_mutex_unlock_iothread(); - cpu_exec_start(cpu); + + if (exclusive_step_mode != WHPX_STEP_NONE) { + start_exclusive(); + g_assert(cpu == current_cpu); + g_assert(!cpu->running); + cpu->running = true; + + hr = whpx_set_exception_exit_bitmap( + 1UL << WHvX64ExceptionTypeDebugTrapOrFault); + if (!SUCCEEDED(hr)) { + error_report("WHPX: Failed to update exception exit mask, " + "hr=%08lx.", hr); + return 1; + } + + if (stepped_over_bp) { + /* Temporarily disable the triggered breakpoint. */ + cpu_memory_rw_debug(cpu, + stepped_over_bp->address, + &stepped_over_bp->original_instruction, + 1, + true); + } + } else { + cpu_exec_start(cpu); + } do { if (cpu->vcpu_dirty) { @@ -987,10 +1665,16 @@ static int whpx_vcpu_run(CPUState *cpu) cpu->vcpu_dirty = false; } - whpx_vcpu_pre_run(cpu); + if (exclusive_step_mode == WHPX_STEP_NONE) { + whpx_vcpu_pre_run(cpu); - if (qatomic_read(&cpu->exit_request)) { - whpx_vcpu_kick(cpu); + if (qatomic_read(&cpu->exit_request)) { + whpx_vcpu_kick(cpu); + } + } + + if (exclusive_step_mode != WHPX_STEP_NONE || cpu->singlestep_enabled) { + whpx_vcpu_configure_single_stepping(cpu, true, NULL); } hr = whp_dispatch.WHvRunVirtualProcessor( @@ -1004,6 +1688,12 @@ static int whpx_vcpu_run(CPUState *cpu) break; } + if (exclusive_step_mode != WHPX_STEP_NONE || cpu->singlestep_enabled) { + whpx_vcpu_configure_single_stepping(cpu, + false, + &vcpu->exit_ctx.VpContext.Rflags); + } + whpx_vcpu_post_run(cpu); switch (vcpu->exit_ctx.ExitReason) { @@ -1027,6 +1717,10 @@ static int whpx_vcpu_run(CPUState *cpu) break; case WHvRunVpExitReasonX64Halt: + /* + * WARNING: as of build 19043.1526 (21H1), this exit reason is no + * longer used. + */ ret = whpx_handle_halt(cpu); break; @@ -1125,10 +1819,19 @@ static int whpx_vcpu_run(CPUState *cpu) } case WHvRunVpExitReasonCanceled: - cpu->exception_index = EXCP_INTERRUPT; - ret = 1; + if (exclusive_step_mode != WHPX_STEP_NONE) { + /* + * We are trying to step over a single instruction, and + * likely got a request to stop from another thread. + * Delay it until we are done stepping + * over. + */ + ret = 0; + } else { + cpu->exception_index = EXCP_INTERRUPT; + ret = 1; + } break; - case WHvRunVpExitReasonX64MsrAccess: { WHV_REGISTER_VALUE reg_values[3] = {0}; WHV_REGISTER_NAME reg_names[3]; @@ -1232,11 +1935,36 @@ static int whpx_vcpu_run(CPUState *cpu) ret = 0; break; } + case WHvRunVpExitReasonException: + whpx_get_registers(cpu); + + if ((vcpu->exit_ctx.VpException.ExceptionType == + WHvX64ExceptionTypeDebugTrapOrFault) && + (vcpu->exit_ctx.VpException.InstructionByteCount >= 1) && + (vcpu->exit_ctx.VpException.InstructionBytes[0] == + whpx_breakpoint_instruction)) { + /* Stopped at a software breakpoint. */ + cpu->exception_index = EXCP_DEBUG; + } else if ((vcpu->exit_ctx.VpException.ExceptionType == + WHvX64ExceptionTypeDebugTrapOrFault) && + !cpu->singlestep_enabled) { + /* + * Just finished stepping over a breakpoint, but the + * gdb does not expect us to do single-stepping. + * Don't do anything special. + */ + cpu->exception_index = EXCP_INTERRUPT; + } else { + /* Another exception or debug event. Report it to GDB. */ + cpu->exception_index = EXCP_DEBUG; + } + + ret = 1; + break; case WHvRunVpExitReasonNone: case WHvRunVpExitReasonUnrecoverableException: case WHvRunVpExitReasonInvalidVpRegisterValue: case WHvRunVpExitReasonUnsupportedFeature: - case WHvRunVpExitReasonException: default: error_report("WHPX: Unexpected VP exit code %d", vcpu->exit_ctx.ExitReason); @@ -1249,10 +1977,32 @@ static int whpx_vcpu_run(CPUState *cpu) } while (!ret); - cpu_exec_end(cpu); + if (stepped_over_bp) { + /* Restore the breakpoint we stepped over */ + cpu_memory_rw_debug(cpu, + stepped_over_bp->address, + (void *)&whpx_breakpoint_instruction, + 1, + true); + } + + if (exclusive_step_mode != WHPX_STEP_NONE) { + g_assert(cpu_in_exclusive_context(cpu)); + cpu->running = false; + end_exclusive(); + + exclusive_step_mode = WHPX_STEP_NONE; + } else { + cpu_exec_end(cpu); + } + qemu_mutex_lock_iothread(); current_cpu = cpu; + if (--whpx->running_cpus == 0) { + whpx_last_vcpu_stopping(cpu); + } + qatomic_set(&cpu->exit_request, false); return ret < 0; @@ -1312,6 +2062,11 @@ void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); } +void whpx_cpu_synchronize_pre_resume(bool step_pending) +{ + whpx_global.step_pending = step_pending; +} + /* * Vcpu support. */ @@ -1333,7 +2088,7 @@ int whpx_init_vcpu(CPUState *cpu) struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = NULL; Error *local_error = NULL; - struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); UINT64 freq = 0; int ret; @@ -1354,7 +2109,7 @@ int whpx_init_vcpu(CPUState *cpu) } } - vcpu = g_malloc0(sizeof(struct whpx_vcpu)); + vcpu = g_new0(struct whpx_vcpu, 1); if (!vcpu) { error_report("WHPX: Failed to allocte VCPU context."); @@ -1544,15 +2299,15 @@ static void whpx_process_section(MemoryRegionSection *section, int add) return; } - delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask); - delta &= ~qemu_real_host_page_mask; + delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask()); + delta &= ~qemu_real_host_page_mask(); if (delta > size) { return; } start_pa += delta; size -= delta; - size &= qemu_real_host_page_mask; - if (!size || (start_pa & ~qemu_real_host_page_mask)) { + size &= qemu_real_host_page_mask(); + if (!size || (start_pa & ~qemu_real_host_page_mask())) { return; } @@ -1811,6 +2566,7 @@ static int whpx_accel_init(MachineState *ms) memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); prop.ExtendedVmExits.X64MsrExit = 1; prop.ExtendedVmExits.X64CpuidExit = 1; + prop.ExtendedVmExits.ExceptionExit = 1; if (whpx_apic_in_platform()) { prop.ExtendedVmExits.X64ApicInitSipiExitTrap = 1; } @@ -1839,6 +2595,19 @@ static int whpx_accel_init(MachineState *ms) goto error; } + /* + * We do not want to intercept any exceptions from the guest, + * until we actually start debugging with gdb. + */ + whpx->exception_exit_bitmap = -1; + hr = whpx_set_exception_exit_bitmap(0); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set exception exit bitmap, hr=%08lx", hr); + ret = -EINVAL; + goto error; + } + hr = whp_dispatch.WHvSetupPartition(whpx->partition); if (FAILED(hr)) { error_report("WHPX: Failed to setup partition, hr=%08lx", hr); diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c index bba36f3ec9..c15df35ad6 100644 --- a/target/i386/whpx/whpx-apic.c +++ b/target/i386/whpx/whpx-apic.c @@ -11,7 +11,6 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" diff --git a/target/i386/whpx/whpx-internal.h b/target/i386/whpx/whpx-internal.h index 908ababf6d..2416ec7922 100644 --- a/target/i386/whpx/whpx-internal.h +++ b/target/i386/whpx/whpx-internal.h @@ -5,9 +5,39 @@ #include #include +typedef enum WhpxBreakpointState { + WHPX_BP_CLEARED = 0, + WHPX_BP_SET_PENDING, + WHPX_BP_SET, + WHPX_BP_CLEAR_PENDING, +} WhpxBreakpointState; + +struct whpx_breakpoint { + vaddr address; + WhpxBreakpointState state; + uint8_t original_instruction; +}; + +struct whpx_breakpoint_collection { + int allocated, used; + struct whpx_breakpoint data[0]; +}; + +struct whpx_breakpoints { + int original_address_count; + vaddr *original_addresses; + + struct whpx_breakpoint_collection *breakpoints; +}; + struct whpx_state { uint64_t mem_quota; WHV_PARTITION_HANDLE partition; + uint64_t exception_exit_bitmap; + int32_t running_cpus; + struct whpx_breakpoints breakpoints; + bool step_pending; + bool kernel_irqchip_allowed; bool kernel_irqchip_required; bool apic_in_platform; diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index ac61a96344..996e9f3bfe 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -126,6 +126,20 @@ void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen) memcpy(pkru, &env->pkru, sizeof(env->pkru)); } + + e = &x86_ext_save_areas[XSTATE_XTILE_CFG_BIT]; + if (e->size && e->offset) { + XSaveXTILECFG *tilecfg = buf + e->offset; + + memcpy(tilecfg, &env->xtilecfg, sizeof(env->xtilecfg)); + } + + e = &x86_ext_save_areas[XSTATE_XTILE_DATA_BIT]; + if (e->size && e->offset && buflen >= e->size + e->offset) { + XSaveXTILEDATA *tiledata = buf + e->offset; + + memcpy(tiledata, &env->xtiledata, sizeof(env->xtiledata)); + } #endif } @@ -247,5 +261,19 @@ void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen) pkru = buf + e->offset; memcpy(&env->pkru, pkru, sizeof(env->pkru)); } + + e = &x86_ext_save_areas[XSTATE_XTILE_CFG_BIT]; + if (e->size && e->offset) { + const XSaveXTILECFG *tilecfg = buf + e->offset; + + memcpy(&env->xtilecfg, tilecfg, sizeof(env->xtilecfg)); + } + + e = &x86_ext_save_areas[XSTATE_XTILE_DATA_BIT]; + if (e->size && e->offset && buflen >= e->size + e->offset) { + const XSaveXTILEDATA *tiledata = buf + e->offset; + + memcpy(&env->xtiledata, tiledata, sizeof(env->xtiledata)); + } #endif } diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index 1ceb160ecb..cd9687192c 100644 --- a/target/m68k/cpu-qom.h +++ b/target/m68k/cpu-qom.h @@ -25,8 +25,7 @@ #define TYPE_M68K_CPU "m68k-cpu" -OBJECT_DECLARE_TYPE(M68kCPU, M68kCPUClass, - M68K_CPU) +OBJECT_DECLARE_CPU_TYPE(M68kCPU, M68kCPUClass, M68K_CPU) /* * M68kCPUClass: diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index a3423729ef..9b3bf7a448 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -22,6 +22,7 @@ #define M68K_CPU_H #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" #include "cpu-qom.h" #define OS_BYTE 0 @@ -79,7 +80,7 @@ typedef CPU_LDoubleU FPReg; -typedef struct CPUM68KState { +typedef struct CPUArchState { uint32_t dregs[8]; uint32_t aregs[8]; uint32_t pc; @@ -156,7 +157,7 @@ typedef struct CPUM68KState { * * A Motorola 68k CPU. */ -struct M68kCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -574,9 +575,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); -typedef CPUM68KState CPUArchState; -typedef M68kCPU ArchCPU; - #include "exec/cpu-all.h" /* TB flags */ diff --git a/target/m68k/helper.h b/target/m68k/helper.h index 9842eeaa95..0a6b4146f6 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -17,6 +17,7 @@ DEF_HELPER_4(cas2l_parallel, void, env, i32, i32, i32) #define dh_alias_fp ptr #define dh_ctype_fp FPReg * +#define dh_typecode_fp dh_typecode_ptr DEF_HELPER_3(exts32, void, env, fp, s32) DEF_HELPER_3(extf32, void, env, fp, f32) diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 44ec7e4612..37343d47e2 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -378,19 +378,17 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) arg0, arg1); return; } else { - qemu_timeval tv; struct gdb_timeval *p; - result = qemu_gettimeofday(&tv); - if (result != 0) { - if (!(p = lock_user(VERIFY_WRITE, - arg0, sizeof(struct gdb_timeval), 0))) { - /* FIXME - check error code? */ - result = -1; - } else { - p->tv_sec = cpu_to_be32(tv.tv_sec); - p->tv_usec = cpu_to_be64(tv.tv_usec); - unlock_user(p, arg0, sizeof(struct gdb_timeval)); - } + int64_t rt = g_get_real_time(); + p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); + if (!p) { + /* FIXME - check error code? */ + result = -1; + } else { + result = 0; + p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); + p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); + unlock_user(p, arg0, sizeof(struct gdb_timeval)); } } break; diff --git a/target/m68k/translate.c b/target/m68k/translate.c index af43c8eab8..4026572ed8 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -6257,10 +6257,11 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void m68k_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void m68k_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps m68k_tr_ops = { diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index e520eefb12..255b39a45d 100644 --- a/target/microblaze/cpu-qom.h +++ b/target/microblaze/cpu-qom.h @@ -25,8 +25,7 @@ #define TYPE_MICROBLAZE_CPU "microblaze-cpu" -OBJECT_DECLARE_TYPE(MicroBlazeCPU, MicroBlazeCPUClass, - MICROBLAZE_CPU) +OBJECT_DECLARE_CPU_TYPE(MicroBlazeCPU, MicroBlazeCPUClass, MICROBLAZE_CPU) /** * MicroBlazeCPUClass: diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index e9cd0b88de..1e84dd8f47 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -22,9 +22,9 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" -#include "fpu/softfloat-types.h" +#include "qemu/cpu-float.h" -typedef struct CPUMBState CPUMBState; +typedef struct CPUArchState CPUMBState; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" #endif @@ -239,7 +239,7 @@ typedef struct CPUMBState CPUMBState; #define USE_NON_SECURE_M_AXI_DC_MASK 0x4 #define USE_NON_SECURE_M_AXI_IC_MASK 0x8 -struct CPUMBState { +struct CPUArchState { uint32_t bvalue; /* TCG temporary, only valid during a TB */ uint32_t btarget; /* Full resolved branch destination */ @@ -339,7 +339,7 @@ typedef struct { * * A MicroBlaze CPU. */ -struct MicroBlazeCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; @@ -359,9 +359,9 @@ struct MicroBlazeCPU { void mb_cpu_do_interrupt(CPUState *cs); bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); #endif /* !CONFIG_USER_ONLY */ -void mb_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void mb_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); @@ -394,9 +394,6 @@ void mb_tcg_init(void); #define MMU_USER_IDX 2 /* See NB_MMU_MODES further up the file. */ -typedef CPUMBState CPUArchState; -typedef MicroBlazeCPU ArchCPU; - #include "exec/cpu-all.h" /* Ensure there is no overlap between the two masks. */ diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index b6b4b9ad60..1068bd2d52 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -20,6 +20,8 @@ #ifndef TARGET_MICROBLAZE_MMU_H #define TARGET_MICROBLAZE_MMU_H +#include "cpu.h" + #define MMU_R_PID 0 #define MMU_R_ZPR 1 #define MMU_R_TLBX 2 diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 2561b904b9..bf01384d33 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1833,10 +1833,11 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) } } -static void mb_tr_disas_log(const DisasContextBase *dcb, CPUState *cs) +static void mb_tr_disas_log(const DisasContextBase *dcb, + CPUState *cs, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcb->pc_first)); - log_target_disas(cs, dcb->pc_first, dcb->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcb->pc_first)); + target_disas(logfile, cs, dcb->pc_first, dcb->tb->size); } static const TranslatorOps mb_tr_ops = { diff --git a/target/mips/TODO b/target/mips/TODO deleted file mode 100644 index 1d782d8027..0000000000 --- a/target/mips/TODO +++ /dev/null @@ -1,51 +0,0 @@ -Unsolved issues/bugs in the mips/mipsel backend ------------------------------------------------ - -General -------- -- Unimplemented ASEs: - - MDMX - - SmartMIPS - - microMIPS DSP r1 & r2 encodings -- MT ASE only partially implemented and not functional -- Shadow register support only partially implemented, - lacks set switching on interrupt/exception. -- 34K ITC not implemented. -- A general lack of documentation, especially for technical internals. - Existing documentation is x86-centric. -- Reverse endianness bit not implemented -- The TLB emulation is very inefficient: - QEMU's softmmu implements a x86-style MMU, with separate entries - for read/write/execute, a TLB index which is just a modulo of the - virtual address, and a set of TLBs for each user/kernel/supervisor - MMU mode. - MIPS has a single entry for read/write/execute and only one MMU mode. - But it is fully associative with randomized entry indices, and uses - up to 256 ASID tags as additional matching criterion (which roughly - equates to 256 MMU modes). It also has a global flag which causes - entries to match regardless of ASID. - To cope with these differences, QEMU currently flushes the TLB at - each ASID change. Using the MMU modes to implement ASIDs hinges on - implementing the global bit efficiently. -- save/restore of the CPU state is not implemented (see machine.c). - -MIPS64 ------- -- Userland emulation (both n32 and n64) not functional. - -"Generic" 4Kc system emulation ------------------------------- -- Doesn't correspond to any real hardware. Should be removed some day, - U-Boot is the last remaining user. - -PICA 61 system emulation ------------------------- -- No framebuffer support yet. - -MALTA system emulation ----------------------- -- We fake firmware support instead of doing the real thing -- Real firmware (YAMON) falls over when trying to init RAM, presumably - due to lacking system controller emulation. -- Bonito system controller not implemented -- MSC1 system controller not implemented diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index 9c4a6ea45e..1aebd01df9 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -12,7 +12,7 @@ #else # define TARGET_LONG_BITS 32 #endif -#ifdef TARGET_MIPS64 +#ifdef TARGET_ABI_MIPSN64 #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 #else diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index dda0c911fa..e28b529607 100644 --- a/target/mips/cpu-qom.h +++ b/target/mips/cpu-qom.h @@ -29,8 +29,7 @@ #define TYPE_MIPS_CPU "mips-cpu" #endif -OBJECT_DECLARE_TYPE(MIPSCPU, MIPSCPUClass, - MIPS_CPU) +OBJECT_DECLARE_CPU_TYPE(MIPSCPU, MIPSCPUClass, MIPS_CPU) /** * MIPSCPUClass: diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 4aae23934b..ad74fbe636 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -189,7 +189,7 @@ static void mips_cpu_reset(DeviceState *dev) /* Reset registers to their default values */ env->CP0_PRid = env->cpu_model->CP0_PRid; env->CP0_Config0 = env->cpu_model->CP0_Config0; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN env->CP0_Config0 |= (1 << CP0C0_BE); #endif env->CP0_Config1 = env->cpu_model->CP0_Config1; @@ -418,7 +418,7 @@ static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info) CPUMIPSState *env = &cpu->env; if (!(env->insn_flags & ISA_NANOMIPS32)) { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN info->print_insn = print_insn_big_mips; #else info->print_insn = print_insn_little_mips; @@ -434,14 +434,13 @@ static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info) * Since commit 6af0bf9c7c3 this model assumes a CPU clocked at 200MHz. */ #define CPU_FREQ_HZ_DEFAULT 200000000 -#define CP0_COUNT_RATE_DEFAULT 2 static void mips_cp0_period_set(MIPSCPU *cpu) { CPUMIPSState *env = &cpu->env; env->cp0_count_ns = clock_ticks_to_ns(MIPS_CPU(cpu)->clock, - cpu->cp0_count_rate); + env->cpu_model->CCRes); assert(env->cp0_count_ns); } @@ -514,13 +513,6 @@ static ObjectClass *mips_cpu_class_by_name(const char *cpu_model) return oc; } -static Property mips_cpu_properties[] = { - /* CP0 timer running at half the clock of the CPU */ - DEFINE_PROP_UINT32("cp0-count-rate", MIPSCPU, cp0_count_rate, - CP0_COUNT_RATE_DEFAULT), - DEFINE_PROP_END_OF_LIST() -}; - #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" @@ -560,7 +552,6 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, mips_cpu_realizefn, &mcc->parent_realize); device_class_set_parent_reset(dc, mips_cpu_reset, &mcc->parent_reset); - device_class_set_props(dc, mips_cpu_properties); cc->class_by_name = mips_cpu_class_by_name; cc->has_work = mips_cpu_has_work; diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 56b1cbd091..5335ac10a3 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -35,7 +35,7 @@ union fpr_t { *define FP_ENDIAN_IDX to access the same location * in the fpr_t union regardless of the host endianness */ -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN # define FP_ENDIAN_IDX 1 #else # define FP_ENDIAN_IDX 0 @@ -524,8 +524,7 @@ struct TCState { }; struct MIPSITUState; -typedef struct CPUMIPSState CPUMIPSState; -struct CPUMIPSState { +typedef struct CPUArchState { TCState active_tc; CPUMIPSFPUContext active_fpu; @@ -1161,18 +1160,17 @@ struct CPUMIPSState { QEMUTimer *timer; /* Internal timer */ target_ulong exception_base; /* ExceptionBase input to the core */ uint64_t cp0_count_ns; /* CP0_Count clock period (in nanoseconds) */ -}; +} CPUMIPSState; /** * MIPSCPU: * @env: #CPUMIPSState * @clock: this CPU input clock (may be connected * to an output clock from another device). - * @cp0_count_rate: rate at which the coprocessor 0 counter increments * * A MIPS CPU. */ -struct MIPSCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -1180,14 +1178,6 @@ struct MIPSCPU { Clock *clock; CPUNegativeOffsetState neg; CPUMIPSState env; - /* - * The Count register acts as a timer, incrementing at a constant rate, - * whether or not an instruction is executed, retired, or any forward - * progress is made through the pipeline. The rate at which the counter - * increments is implementation dependent, and is a function of the - * pipeline clock of the processor, not the issue width of the processor. - */ - unsigned cp0_count_rate; }; @@ -1218,9 +1208,6 @@ static inline int cpu_mmu_index(CPUMIPSState *env, bool ifetch) return hflags_mmu_index(env->hflags); } -typedef CPUMIPSState CPUArchState; -typedef MIPSCPU ArchCPU; - #include "exec/cpu-all.h" /* Exceptions */ diff --git a/target/mips/internal.h b/target/mips/internal.h index daddb05fd4..57b312689a 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -12,6 +12,7 @@ #ifdef CONFIG_TCG #include "tcg/tcg-internal.h" #endif +#include "cpu.h" /* * MMU types, the first four entries have the same layout as the @@ -46,6 +47,15 @@ struct mips_def_t { target_ulong CP0_LLAddr_rw_bitmask; int CP0_LLAddr_shift; int32_t SYNCI_Step; + /* + * @CCRes: rate at which the coprocessor 0 counter increments + * + * The Count register acts as a timer, incrementing at a constant rate, + * whether or not an instruction is executed, retired, or any forward + * progress is made through the pipeline. The rate at which the counter + * increments is implementation dependent, and is a function of the + * pipeline clock of the processor, not the issue width of the processor. + */ int32_t CCRes; int32_t CP0_Status_rw_bitmask; int32_t CP0_TCStatus_rw_bitmask; @@ -133,14 +143,14 @@ struct r4k_tlb_t { struct CPUMIPSTLBContext { uint32_t nb_tlb; uint32_t tlb_in_use; - int (*map_address)(struct CPUMIPSState *env, hwaddr *physical, int *prot, + int (*map_address)(CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, MMUAccessType access_type); - void (*helper_tlbwi)(struct CPUMIPSState *env); - void (*helper_tlbwr)(struct CPUMIPSState *env); - void (*helper_tlbp)(struct CPUMIPSState *env); - void (*helper_tlbr)(struct CPUMIPSState *env); - void (*helper_tlbinv)(struct CPUMIPSState *env); - void (*helper_tlbinvf)(struct CPUMIPSState *env); + void (*helper_tlbwi)(CPUMIPSState *env); + void (*helper_tlbwr)(CPUMIPSState *env); + void (*helper_tlbp)(CPUMIPSState *env); + void (*helper_tlbr)(CPUMIPSState *env); + void (*helper_tlbinv)(CPUMIPSState *env); + void (*helper_tlbinvf)(CPUMIPSState *env); union { struct { r4k_tlb_t tlb[MIPS_TLB_MAX]; diff --git a/target/mips/kvm.c b/target/mips/kvm.c index 086debd9f0..caf70decd2 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -14,7 +14,6 @@ #include -#include "qemu-common.h" #include "cpu.h" #include "internal.h" #include "qemu/error-report.h" diff --git a/target/mips/tcg/lmmi_helper.c b/target/mips/tcg/lmmi_helper.c index abeb7736ae..2c8732525c 100644 --- a/target/mips/tcg/lmmi_helper.c +++ b/target/mips/tcg/lmmi_helper.c @@ -37,7 +37,7 @@ typedef union { } LMIValue; /* Some byte ordering issues can be mitigated by XORing in the following. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN # define BYTE_ORDER_XOR(N) N #else # define BYTE_ORDER_XOR(N) 0 diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 5667b1f0a1..4dde5d639a 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -4146,7 +4146,7 @@ void helper_msa_ilvev_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->b[8] = pws->b[9]; pwd->b[9] = pwt->b[9]; pwd->b[10] = pws->b[11]; @@ -4190,7 +4190,7 @@ void helper_msa_ilvev_h(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->h[4] = pws->h[5]; pwd->h[5] = pwt->h[5]; pwd->h[6] = pws->h[7]; @@ -4218,7 +4218,7 @@ void helper_msa_ilvev_w(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->w[2] = pws->w[3]; pwd->w[3] = pwt->w[3]; pwd->w[0] = pws->w[1]; @@ -4250,7 +4250,7 @@ void helper_msa_ilvod_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->b[7] = pwt->b[6]; pwd->b[6] = pws->b[6]; pwd->b[5] = pwt->b[4]; @@ -4294,7 +4294,7 @@ void helper_msa_ilvod_h(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->h[3] = pwt->h[2]; pwd->h[2] = pws->h[2]; pwd->h[1] = pwt->h[0]; @@ -4322,7 +4322,7 @@ void helper_msa_ilvod_w(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->w[1] = pwt->w[0]; pwd->w[0] = pws->w[0]; pwd->w[3] = pwt->w[2]; @@ -4354,7 +4354,7 @@ void helper_msa_ilvl_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->b[7] = pwt->b[15]; pwd->b[6] = pws->b[15]; pwd->b[5] = pwt->b[14]; @@ -4398,7 +4398,7 @@ void helper_msa_ilvl_h(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->h[3] = pwt->h[7]; pwd->h[2] = pws->h[7]; pwd->h[1] = pwt->h[6]; @@ -4426,7 +4426,7 @@ void helper_msa_ilvl_w(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->w[1] = pwt->w[3]; pwd->w[0] = pws->w[3]; pwd->w[3] = pwt->w[2]; @@ -4458,7 +4458,7 @@ void helper_msa_ilvr_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->b[8] = pws->b[0]; pwd->b[9] = pwt->b[0]; pwd->b[10] = pws->b[1]; @@ -4502,7 +4502,7 @@ void helper_msa_ilvr_h(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->h[4] = pws->h[0]; pwd->h[5] = pwt->h[0]; pwd->h[6] = pws->h[1]; @@ -4530,7 +4530,7 @@ void helper_msa_ilvr_w(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->w[2] = pws->w[0]; pwd->w[3] = pwt->w[0]; pwd->w[0] = pws->w[1]; @@ -4661,7 +4661,7 @@ void helper_msa_pckev_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->b[8] = pws->b[9]; pwd->b[10] = pws->b[13]; pwd->b[12] = pws->b[1]; @@ -4705,7 +4705,7 @@ void helper_msa_pckev_h(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->h[4] = pws->h[5]; pwd->h[6] = pws->h[1]; pwd->h[0] = pwt->h[5]; @@ -4733,7 +4733,7 @@ void helper_msa_pckev_w(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->w[2] = pws->w[3]; pwd->w[0] = pwt->w[3]; pwd->w[3] = pws->w[1]; @@ -4765,7 +4765,7 @@ void helper_msa_pckod_b(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->b[7] = pwt->b[6]; pwd->b[5] = pwt->b[2]; pwd->b[3] = pwt->b[14]; @@ -4810,7 +4810,7 @@ void helper_msa_pckod_h(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->h[3] = pwt->h[2]; pwd->h[1] = pwt->h[6]; pwd->h[7] = pws->h[2]; @@ -4838,7 +4838,7 @@ void helper_msa_pckod_w(CPUMIPSState *env, wr_t *pws = &(env->active_fpu.fpr[ws].wr); wr_t *pwt = &(env->active_fpu.fpr[wt].wr); -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN pwd->w[1] = pwt->w[0]; pwd->w[3] = pws->w[0]; pwd->w[0] = pwt->w[2]; @@ -5926,7 +5926,7 @@ void helper_msa_copy_s_b(CPUMIPSState *env, uint32_t rd, uint32_t ws, uint32_t n) { n %= 16; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 8) { n = 8 - n - 1; } else { @@ -5940,7 +5940,7 @@ void helper_msa_copy_s_h(CPUMIPSState *env, uint32_t rd, uint32_t ws, uint32_t n) { n %= 8; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 4) { n = 4 - n - 1; } else { @@ -5954,7 +5954,7 @@ void helper_msa_copy_s_w(CPUMIPSState *env, uint32_t rd, uint32_t ws, uint32_t n) { n %= 4; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 2) { n = 2 - n - 1; } else { @@ -5975,7 +5975,7 @@ void helper_msa_copy_u_b(CPUMIPSState *env, uint32_t rd, uint32_t ws, uint32_t n) { n %= 16; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 8) { n = 8 - n - 1; } else { @@ -5989,7 +5989,7 @@ void helper_msa_copy_u_h(CPUMIPSState *env, uint32_t rd, uint32_t ws, uint32_t n) { n %= 8; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 4) { n = 4 - n - 1; } else { @@ -6003,7 +6003,7 @@ void helper_msa_copy_u_w(CPUMIPSState *env, uint32_t rd, uint32_t ws, uint32_t n) { n %= 4; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 2) { n = 2 - n - 1; } else { @@ -6019,7 +6019,7 @@ void helper_msa_insert_b(CPUMIPSState *env, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); target_ulong rs = env->active_tc.gpr[rs_num]; n %= 16; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 8) { n = 8 - n - 1; } else { @@ -6035,7 +6035,7 @@ void helper_msa_insert_h(CPUMIPSState *env, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); target_ulong rs = env->active_tc.gpr[rs_num]; n %= 8; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 4) { n = 4 - n - 1; } else { @@ -6051,7 +6051,7 @@ void helper_msa_insert_w(CPUMIPSState *env, uint32_t wd, wr_t *pwd = &(env->active_fpu.fpr[wd].wr); target_ulong rs = env->active_tc.gpr[rs_num]; n %= 4; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN if (n < 2) { n = 2 - n - 1; } else { @@ -8218,7 +8218,7 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MEMOP_IDX(DF) #endif -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN static inline uint64_t bswap16x4(uint64_t x) { uint64_t m = 0x00ff00ff00ff00ffull; @@ -8258,7 +8258,7 @@ void helper_msa_ld_h(CPUMIPSState *env, uint32_t wd, */ d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN d0 = bswap16x4(d0); d1 = bswap16x4(d1); #endif @@ -8279,7 +8279,7 @@ void helper_msa_ld_w(CPUMIPSState *env, uint32_t wd, */ d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN d0 = bswap32x2(d0); d1 = bswap32x2(d1); #endif @@ -8345,7 +8345,7 @@ void helper_msa_st_h(CPUMIPSState *env, uint32_t wd, /* Store 8 bytes at a time. See helper_msa_ld_h. */ d0 = pwd->d[0]; d1 = pwd->d[1]; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN d0 = bswap16x4(d0); d1 = bswap16x4(d1); #endif @@ -8366,7 +8366,7 @@ void helper_msa_st_w(CPUMIPSState *env, uint32_t wd, /* Store 8 bytes at a time. See helper_msa_ld_w. */ d0 = pwd->d[0]; d1 = pwd->d[1]; -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN d0 = bswap32x2(d0); d1 = bswap32x2(d1); #endif diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index 466768aec4..993720b00c 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -18,18 +18,19 @@ void mips_tcg_init(void); void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); -void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); const char *mips_exception_name(int32_t exception); -void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, uint32_t exception, - int error_code, uintptr_t pc); +G_NORETURN void do_raise_exception_err(CPUMIPSState *env, uint32_t exception, + int error_code, uintptr_t pc); -static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env, - uint32_t exception, - uintptr_t pc) +static inline G_NORETURN +void do_raise_exception(CPUMIPSState *env, + uint32_t exception, + uintptr_t pc) { do_raise_exception_err(env, exception, 0, pc); } diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index b82a7ec6ad..6de5b66650 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -16141,10 +16141,11 @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void mips_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void mips_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps mips_tr_ops = { diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 4cade61e93..19b2409974 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -31,12 +31,12 @@ static void nios2_cpu_set_pc(CPUState *cs, vaddr value) Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - env->regs[R_PC] = value; + env->pc = value; } static bool nios2_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cs->interrupt_request & CPU_INTERRUPT_HARD; } static void nios2_cpu_reset(DeviceState *dev) @@ -46,39 +46,46 @@ static void nios2_cpu_reset(DeviceState *dev) Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu); CPUNios2State *env = &cpu->env; - if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", cs->cpu_index); - log_cpu_state(cs, 0); - } - ncc->parent_reset(dev); - memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS); - env->regs[R_PC] = cpu->reset_addr; + memset(env->ctrl, 0, sizeof(env->ctrl)); + env->pc = cpu->reset_addr; #if defined(CONFIG_USER_ONLY) /* Start in user mode with interrupts enabled. */ - env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE; + env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE; + memset(env->regs, 0, sizeof(env->regs)); #else - env->regs[CR_STATUS] = 0; + env->ctrl[CR_STATUS] = CR_STATUS_RSIE; + nios2_update_crs(env); + memset(env->shadow_regs, 0, sizeof(env->shadow_regs)); #endif } #ifndef CONFIG_USER_ONLY -static void nios2_cpu_set_irq(void *opaque, int irq, int level) +static void eic_set_irq(void *opaque, int irq, int level) +{ + Nios2CPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } +} + +static void iic_set_irq(void *opaque, int irq, int level) { Nios2CPU *cpu = opaque; CPUNios2State *env = &cpu->env; CPUState *cs = CPU(cpu); - env->regs[CR_IPENDING] = deposit32(env->regs[CR_IPENDING], irq, 1, !!level); + env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level); - env->irq_pending = env->regs[CR_IPENDING] & env->regs[CR_IENABLE]; - - if (env->irq_pending && (env->regs[CR_STATUS] & CR_STATUS_PIE)) { - env->irq_pending = 0; + if (env->ctrl[CR_IPENDING]) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else if (!env->irq_pending) { + } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } @@ -92,15 +99,6 @@ static void nios2_cpu_initfn(Object *obj) #if !defined(CONFIG_USER_ONLY) mmu_init(&cpu->env); - - /* - * These interrupt lines model the IIC (internal interrupt - * controller). QEMU does not currently support the EIC - * (external interrupt controller) -- if we did it would be - * a separate device in hw/intc with a custom interface to - * the CPU, and boards using it would not wire up these IRQ lines. - */ - qdev_init_gpio_in_named(DEVICE(cpu), nios2_cpu_set_irq, "IRQ", 32); #endif } @@ -109,35 +107,147 @@ static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model) return object_class_by_name(TYPE_NIOS2_CPU); } +static void realize_cr_status(CPUState *cs) +{ + Nios2CPU *cpu = NIOS2_CPU(cs); + + /* Begin with all fields of all registers are reserved. */ + memset(cpu->cr_state, 0, sizeof(cpu->cr_state)); + + /* + * The combination of writable and readonly is the set of all + * non-reserved fields. We apply writable as a mask to bits, + * and merge in existing readonly bits, before storing. + */ +#define WR_REG(C) cpu->cr_state[C].writable = -1 +#define RO_REG(C) cpu->cr_state[C].readonly = -1 +#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK +#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK + + WR_FIELD(CR_STATUS, PIE); + WR_REG(CR_ESTATUS); + WR_REG(CR_BSTATUS); + RO_REG(CR_CPUID); + RO_REG(CR_EXCEPTION); + WR_REG(CR_BADADDR); + + if (cpu->eic_present) { + WR_FIELD(CR_STATUS, RSIE); + RO_FIELD(CR_STATUS, NMI); + WR_FIELD(CR_STATUS, PRS); + RO_FIELD(CR_STATUS, CRS); + WR_FIELD(CR_STATUS, IL); + WR_FIELD(CR_STATUS, IH); + } else { + RO_FIELD(CR_STATUS, RSIE); + WR_REG(CR_IENABLE); + RO_REG(CR_IPENDING); + } + + if (cpu->mmu_present) { + WR_FIELD(CR_STATUS, U); + WR_FIELD(CR_STATUS, EH); + + WR_FIELD(CR_PTEADDR, VPN); + WR_FIELD(CR_PTEADDR, PTBASE); + + RO_FIELD(CR_TLBMISC, D); + RO_FIELD(CR_TLBMISC, PERM); + RO_FIELD(CR_TLBMISC, BAD); + RO_FIELD(CR_TLBMISC, DBL); + WR_FIELD(CR_TLBMISC, PID); + WR_FIELD(CR_TLBMISC, WE); + WR_FIELD(CR_TLBMISC, RD); + WR_FIELD(CR_TLBMISC, WAY); + + WR_REG(CR_TLBACC); + } + + /* + * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are + * unimplemented, so their corresponding control regs remain reserved. + */ + +#undef WR_REG +#undef RO_REG +#undef WR_FIELD +#undef RO_FIELD +} + static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); + Nios2CPU *cpu = NIOS2_CPU(cs); Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev); Error *local_err = NULL; +#ifndef CONFIG_USER_ONLY + if (cpu->eic_present) { + qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); + } else { + qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); + } +#endif + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; } + realize_cr_status(cs); qemu_init_vcpu(cs); cpu_reset(cs); + /* We have reserved storage for cpuid; might as well use it. */ + cpu->env.ctrl[CR_CPUID] = cs->cpu_index; + ncc->parent_realize(dev, errp); } #ifndef CONFIG_USER_ONLY +static bool eic_take_interrupt(Nios2CPU *cpu) +{ + CPUNios2State *env = &cpu->env; + const uint32_t status = env->ctrl[CR_STATUS]; + + if (cpu->rnmi) { + return !(status & CR_STATUS_NMI); + } + if (!(status & CR_STATUS_PIE)) { + return false; + } + if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) { + return false; + } + if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) { + return true; + } + return status & CR_STATUS_RSIE; +} + +static bool iic_take_interrupt(Nios2CPU *cpu) +{ + CPUNios2State *env = &cpu->env; + + if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) { + return false; + } + return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE]; +} + static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - if ((interrupt_request & CPU_INTERRUPT_HARD) && - (env->regs[CR_STATUS] & CR_STATUS_PIE)) { - cs->exception_index = EXCP_IRQ; - nios2_cpu_do_interrupt(cs); - return true; + if (interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu->eic_present + ? eic_take_interrupt(cpu) + : iic_take_interrupt(cpu)) { + cs->exception_index = EXCP_IRQ; + nios2_cpu_do_interrupt(cs); + return true; + } } return false; } @@ -153,23 +263,26 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { Nios2CPU *cpu = NIOS2_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); CPUNios2State *env = &cpu->env; + uint32_t val; - if (n > cc->gdb_num_core_regs) { + if (n < 32) { /* GP regs */ + val = env->regs[n]; + } else if (n == 32) { /* PC */ + val = env->pc; + } else if (n < 49) { /* Status regs */ + unsigned cr = n - 33; + if (nios2_cr_reserved(&cpu->cr_state[cr])) { + val = 0; + } else { + val = env->ctrl[n - 33]; + } + } else { + /* Invalid regs */ return 0; } - if (n < 32) { /* GP regs */ - return gdb_get_reg32(mem_buf, env->regs[n]); - } else if (n == 32) { /* PC */ - return gdb_get_reg32(mem_buf, env->regs[R_PC]); - } else if (n < 49) { /* Status regs */ - return gdb_get_reg32(mem_buf, env->regs[n - 1]); - } - - /* Invalid regs */ - return 0; + return gdb_get_reg32(mem_buf, val); } static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) @@ -177,23 +290,32 @@ static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) Nios2CPU *cpu = NIOS2_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); CPUNios2State *env = &cpu->env; + uint32_t val; if (n > cc->gdb_num_core_regs) { return 0; } + val = ldl_p(mem_buf); if (n < 32) { /* GP regs */ - env->regs[n] = ldl_p(mem_buf); + env->regs[n] = val; } else if (n == 32) { /* PC */ - env->regs[R_PC] = ldl_p(mem_buf); + env->pc = val; } else if (n < 49) { /* Status regs */ - env->regs[n - 1] = ldl_p(mem_buf); + unsigned cr = n - 33; + /* ??? Maybe allow the debugger to write to readonly fields. */ + val &= cpu->cr_state[cr].writable; + val |= cpu->cr_state[cr].readonly & env->ctrl[cr]; + env->ctrl[cr] = val; + } else { + g_assert_not_reached(); } return 4; } static Property nios2_properties[] = { + DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true), DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true), /* ALTR,pid-num-bits */ DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8), @@ -217,9 +339,7 @@ static const struct SysemuCPUOps nios2_sysemu_ops = { static const struct TCGCPUOps nios2_tcg_ops = { .initialize = nios2_tcg_init, -#ifdef CONFIG_USER_ONLY - .record_sigsegv = nios2_cpu_record_sigsegv, -#else +#ifndef CONFIG_USER_ONLY .tlb_fill = nios2_cpu_tlb_fill, .cpu_exec_interrupt = nios2_cpu_exec_interrupt, .do_interrupt = nios2_cpu_do_interrupt, diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index d2ba0c5bbd..f85581ee56 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -23,17 +23,17 @@ #include "exec/cpu-defs.h" #include "hw/core/cpu.h" +#include "hw/registerfields.h" #include "qom/object.h" -typedef struct CPUNios2State CPUNios2State; +typedef struct CPUArchState CPUNios2State; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" #endif #define TYPE_NIOS2_CPU "nios2-cpu" -OBJECT_DECLARE_TYPE(Nios2CPU, Nios2CPUClass, - NIOS2_CPU) +OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) /** * Nios2CPUClass: @@ -57,83 +57,114 @@ struct Nios2CPUClass { #define EXCEPTION_ADDRESS 0x00000004 #define FAST_TLB_MISS_ADDRESS 0x00000008 +#define NUM_GP_REGS 32 +#define NUM_CR_REGS 32 -/* GP regs + CR regs + PC */ -#define NUM_CORE_REGS (32 + 32 + 1) +#ifndef CONFIG_USER_ONLY +/* 63 shadow register sets; index 0 is the primary register set. */ +#define NUM_REG_SETS 64 +#endif /* General purpose register aliases */ -#define R_ZERO 0 -#define R_AT 1 -#define R_RET0 2 -#define R_RET1 3 -#define R_ARG0 4 -#define R_ARG1 5 -#define R_ARG2 6 -#define R_ARG3 7 -#define R_ET 24 -#define R_BT 25 -#define R_GP 26 -#define R_SP 27 -#define R_FP 28 -#define R_EA 29 -#define R_BA 30 -#define R_RA 31 +enum { + R_ZERO = 0, + R_AT = 1, + R_RET0 = 2, + R_RET1 = 3, + R_ARG0 = 4, + R_ARG1 = 5, + R_ARG2 = 6, + R_ARG3 = 7, + R_ET = 24, + R_BT = 25, + R_GP = 26, + R_SP = 27, + R_FP = 28, + R_EA = 29, + R_BA = 30, + R_SSTATUS = 30, + R_RA = 31, +}; /* Control register aliases */ -#define CR_BASE 32 -#define CR_STATUS (CR_BASE + 0) -#define CR_STATUS_PIE (1 << 0) -#define CR_STATUS_U (1 << 1) -#define CR_STATUS_EH (1 << 2) -#define CR_STATUS_IH (1 << 3) -#define CR_STATUS_IL (63 << 4) -#define CR_STATUS_CRS (63 << 10) -#define CR_STATUS_PRS (63 << 16) -#define CR_STATUS_NMI (1 << 22) -#define CR_STATUS_RSIE (1 << 23) -#define CR_ESTATUS (CR_BASE + 1) -#define CR_BSTATUS (CR_BASE + 2) -#define CR_IENABLE (CR_BASE + 3) -#define CR_IPENDING (CR_BASE + 4) -#define CR_CPUID (CR_BASE + 5) -#define CR_CTL6 (CR_BASE + 6) -#define CR_EXCEPTION (CR_BASE + 7) -#define CR_PTEADDR (CR_BASE + 8) -#define CR_PTEADDR_PTBASE_SHIFT 22 -#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT) -#define CR_PTEADDR_VPN_SHIFT 2 -#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT) -#define CR_TLBACC (CR_BASE + 9) -#define CR_TLBACC_IGN_SHIFT 25 -#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT) -#define CR_TLBACC_C (1 << 24) -#define CR_TLBACC_R (1 << 23) -#define CR_TLBACC_W (1 << 22) -#define CR_TLBACC_X (1 << 21) -#define CR_TLBACC_G (1 << 20) -#define CR_TLBACC_PFN_MASK 0x000FFFFF -#define CR_TLBMISC (CR_BASE + 10) -#define CR_TLBMISC_WAY_SHIFT 20 -#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT) -#define CR_TLBMISC_RD (1 << 19) -#define CR_TLBMISC_WR (1 << 18) -#define CR_TLBMISC_PID_SHIFT 4 -#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT) -#define CR_TLBMISC_DBL (1 << 3) -#define CR_TLBMISC_BAD (1 << 2) -#define CR_TLBMISC_PERM (1 << 1) -#define CR_TLBMISC_D (1 << 0) -#define CR_ENCINJ (CR_BASE + 11) -#define CR_BADADDR (CR_BASE + 12) -#define CR_CONFIG (CR_BASE + 13) -#define CR_MPUBASE (CR_BASE + 14) -#define CR_MPUACC (CR_BASE + 15) +enum { + CR_STATUS = 0, + CR_ESTATUS = 1, + CR_BSTATUS = 2, + CR_IENABLE = 3, + CR_IPENDING = 4, + CR_CPUID = 5, + CR_EXCEPTION = 7, + CR_PTEADDR = 8, + CR_TLBACC = 9, + CR_TLBMISC = 10, + CR_ENCINJ = 11, + CR_BADADDR = 12, + CR_CONFIG = 13, + CR_MPUBASE = 14, + CR_MPUACC = 15, +}; -/* Other registers */ -#define R_PC 64 +FIELD(CR_STATUS, PIE, 0, 1) +FIELD(CR_STATUS, U, 1, 1) +FIELD(CR_STATUS, EH, 2, 1) +FIELD(CR_STATUS, IH, 3, 1) +FIELD(CR_STATUS, IL, 4, 6) +FIELD(CR_STATUS, CRS, 10, 6) +FIELD(CR_STATUS, PRS, 16, 6) +FIELD(CR_STATUS, NMI, 22, 1) +FIELD(CR_STATUS, RSIE, 23, 1) +FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */ + +#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK +#define CR_STATUS_U R_CR_STATUS_U_MASK +#define CR_STATUS_EH R_CR_STATUS_EH_MASK +#define CR_STATUS_IH R_CR_STATUS_IH_MASK +#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK +#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK +#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK + +FIELD(CR_EXCEPTION, CAUSE, 2, 5) +FIELD(CR_EXCEPTION, ECCFTL, 31, 1) + +FIELD(CR_PTEADDR, VPN, 2, 20) +FIELD(CR_PTEADDR, PTBASE, 22, 10) + +FIELD(CR_TLBACC, PFN, 0, 20) +FIELD(CR_TLBACC, G, 20, 1) +FIELD(CR_TLBACC, X, 21, 1) +FIELD(CR_TLBACC, W, 22, 1) +FIELD(CR_TLBACC, R, 23, 1) +FIELD(CR_TLBACC, C, 24, 1) +FIELD(CR_TLBACC, IG, 25, 7) + +#define CR_TLBACC_C R_CR_TLBACC_C_MASK +#define CR_TLBACC_R R_CR_TLBACC_R_MASK +#define CR_TLBACC_W R_CR_TLBACC_W_MASK +#define CR_TLBACC_X R_CR_TLBACC_X_MASK +#define CR_TLBACC_G R_CR_TLBACC_G_MASK + +FIELD(CR_TLBMISC, D, 0, 1) +FIELD(CR_TLBMISC, PERM, 1, 1) +FIELD(CR_TLBMISC, BAD, 2, 1) +FIELD(CR_TLBMISC, DBL, 3, 1) +FIELD(CR_TLBMISC, PID, 4, 14) +FIELD(CR_TLBMISC, WE, 18, 1) +FIELD(CR_TLBMISC, RD, 19, 1) +FIELD(CR_TLBMISC, WAY, 20, 4) +FIELD(CR_TLBMISC, EE, 24, 1) + +#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK +#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK +#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK +#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK +#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK +#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK +#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK /* Exceptions */ #define EXCP_BREAK 0x1000 +#define EXCP_SEMIHOST 0x1001 #define EXCP_RESET 0 #define EXCP_PRESET 1 #define EXCP_IRQ 2 @@ -143,35 +174,46 @@ struct Nios2CPUClass { #define EXCP_UNALIGN 6 #define EXCP_UNALIGND 7 #define EXCP_DIV 8 -#define EXCP_SUPERA 9 +#define EXCP_SUPERA_X 9 #define EXCP_SUPERI 10 -#define EXCP_SUPERD 11 -#define EXCP_TLBD 12 -#define EXCP_TLBX 13 -#define EXCP_TLBR 14 -#define EXCP_TLBW 15 +#define EXCP_SUPERA_D 11 +#define EXCP_TLB_X 12 +#define EXCP_TLB_D (0x1000 | EXCP_TLB_X) +#define EXCP_PERM_X 13 +#define EXCP_PERM_R 14 +#define EXCP_PERM_W 15 #define EXCP_MPUI 16 #define EXCP_MPUD 17 -#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 - -struct CPUNios2State { - uint32_t regs[NUM_CORE_REGS]; +struct CPUArchState { +#ifdef CONFIG_USER_ONLY + uint32_t regs[NUM_GP_REGS]; +#else + uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS]; + /* Pointer into shadow_regs for the current register set. */ + uint32_t *regs; +#endif + uint32_t ctrl[NUM_CR_REGS]; + uint32_t pc; #if !defined(CONFIG_USER_ONLY) Nios2MMU mmu; - uint32_t irq_pending; #endif int error_code; }; +typedef struct { + uint32_t writable; + uint32_t readonly; +} ControlRegState; + /** * Nios2CPU: * @env: #CPUNios2State * * A Nios2 CPU. */ -struct Nios2CPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -179,7 +221,10 @@ struct Nios2CPU { CPUNegativeOffsetState neg; CPUNios2State env; + bool diverr_present; bool mmu_present; + bool eic_present; + uint32_t pid_num_bits; uint32_t tlb_num_ways; uint32_t tlb_num_entries; @@ -188,17 +233,41 @@ struct Nios2CPU { uint32_t reset_addr; uint32_t exception_addr; uint32_t fast_tlb_miss_addr; + + /* Bits within each control register which are reserved or readonly. */ + ControlRegState cr_state[NUM_CR_REGS]; + + /* External Interrupt Controller Interface */ + uint32_t rha; /* Requested handler address */ + uint32_t ril; /* Requested interrupt level */ + uint32_t rrs; /* Requested register set */ + bool rnmi; /* Requested nonmaskable interrupt */ }; +static inline bool nios2_cr_reserved(const ControlRegState *s) +{ + return (s->writable | s->readonly) == 0; +} + +static inline void nios2_update_crs(CPUNios2State *env) +{ +#ifndef CONFIG_USER_ONLY + unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); + env->regs = env->shadow_regs[crs]; +#endif +} + void nios2_tcg_init(void); void nios2_cpu_do_interrupt(CPUState *cs); void dump_mmu(CPUNios2State *env); void nios2_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); +G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env, + uintptr_t retaddr); void do_nios2_semihosting(CPUNios2State *env); @@ -214,36 +283,35 @@ void do_nios2_semihosting(CPUNios2State *env); static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch) { - return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX : + return (env->ctrl[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX : MMU_SUPERVISOR_IDX; } -#ifdef CONFIG_USER_ONLY -void nios2_cpu_record_sigsegv(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); -#else +#ifndef CONFIG_USER_ONLY bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); #endif -static inline int cpu_interrupts_enabled(CPUNios2State *env) -{ - return env->regs[CR_STATUS] & CR_STATUS_PIE; -} - typedef CPUNios2State CPUArchState; typedef Nios2CPU ArchCPU; #include "exec/cpu-all.h" +FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */ +FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */ +FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */ + static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) { - *pc = env->regs[R_PC]; + unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); + + *pc = env->pc; *cs_base = 0; - *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U)); + *flags = (env->ctrl[CR_STATUS] & CR_STATUS_U) + | (crs ? 0 : R_TBFLAGS_CRS0_MASK) + | (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK); } #endif /* NIOS2_CPU_H */ diff --git a/target/nios2/helper.c b/target/nios2/helper.c index e5c98650e1..bb3b09e5a7 100644 --- a/target/nios2/helper.c +++ b/target/nios2/helper.c @@ -28,176 +28,234 @@ #include "exec/helper-proto.h" #include "semihosting/semihost.h" -#if defined(CONFIG_USER_ONLY) + +static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, + uint32_t tlbmisc_set, bool is_break) +{ + CPUNios2State *env = &cpu->env; + CPUState *cs = CPU(cpu); + uint32_t old_status = env->ctrl[CR_STATUS]; + uint32_t new_status = old_status; + + /* With shadow regs, exceptions are always taken into CRS 0. */ + new_status &= ~R_CR_STATUS_CRS_MASK; + env->regs = env->shadow_regs[0]; + + if ((old_status & CR_STATUS_EH) == 0) { + int r_ea = R_EA, cr_es = CR_ESTATUS; + + if (is_break) { + r_ea = R_BA; + cr_es = CR_BSTATUS; + } + env->ctrl[cr_es] = old_status; + env->regs[r_ea] = env->pc; + + if (cpu->mmu_present) { + new_status |= CR_STATUS_EH; + + /* + * There are 4 bits that are always written. + * Explicitly clear them, to be set via the argument. + */ + env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | + CR_TLBMISC_PERM | + CR_TLBMISC_BAD | + CR_TLBMISC_DBL); + env->ctrl[CR_TLBMISC] |= tlbmisc_set; + } + + /* + * With shadow regs, and EH == 0, PRS is set from CRS. + * At least, so says Table 3-9, and some other text, + * though Table 3-38 says otherwise. + */ + new_status = FIELD_DP32(new_status, CR_STATUS, PRS, + FIELD_EX32(old_status, CR_STATUS, CRS)); + } + + new_status &= ~(CR_STATUS_PIE | CR_STATUS_U); + + env->ctrl[CR_STATUS] = new_status; + if (!is_break) { + env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE, + cs->exception_index); + } + env->pc = exception_addr; +} + +static void do_iic_irq(Nios2CPU *cpu) +{ + do_exception(cpu, cpu->exception_addr, 0, false); +} + +static void do_eic_irq(Nios2CPU *cpu) +{ + CPUNios2State *env = &cpu->env; + uint32_t old_status = env->ctrl[CR_STATUS]; + uint32_t new_status = old_status; + uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS); + uint32_t new_rs = cpu->rrs; + + new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs); + new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril); + new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi); + new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U); + new_status |= CR_STATUS_IH; + + if (!(new_status & CR_STATUS_EH)) { + new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs); + if (new_rs == 0) { + env->ctrl[CR_ESTATUS] = old_status; + } else { + if (new_rs != old_rs) { + old_status |= CR_STATUS_SRS; + } + env->shadow_regs[new_rs][R_SSTATUS] = old_status; + } + env->shadow_regs[new_rs][R_EA] = env->pc; + } + + env->ctrl[CR_STATUS] = new_status; + nios2_update_crs(env); + + env->pc = cpu->rha; +} void nios2_cpu_do_interrupt(CPUState *cs) { Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - cs->exception_index = -1; - env->regs[R_EA] = env->regs[R_PC] + 4; -} + uint32_t tlbmisc_set = 0; -void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t retaddr) -{ - /* FIXME: Disentangle kuser page from linux-user sigsegv handling. */ - cs->exception_index = 0xaa; - cpu_loop_exit_restore(cs, retaddr); -} + if (qemu_loglevel_mask(CPU_LOG_INT)) { + const char *name = NULL; -#else /* !CONFIG_USER_ONLY */ - -void nios2_cpu_do_interrupt(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; + switch (cs->exception_index) { + case EXCP_IRQ: + name = "interrupt"; + break; + case EXCP_TLB_X: + case EXCP_TLB_D: + if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { + name = "TLB MISS (double)"; + } else { + name = "TLB MISS (fast)"; + } + break; + case EXCP_PERM_R: + case EXCP_PERM_W: + case EXCP_PERM_X: + name = "TLB PERM"; + break; + case EXCP_SUPERA_X: + case EXCP_SUPERA_D: + name = "SUPERVISOR (address)"; + break; + case EXCP_SUPERI: + name = "SUPERVISOR (insn)"; + break; + case EXCP_ILLEGAL: + name = "ILLEGAL insn"; + break; + case EXCP_UNALIGN: + name = "Misaligned (data)"; + break; + case EXCP_UNALIGND: + name = "Misaligned (destination)"; + break; + case EXCP_DIV: + name = "DIV error"; + break; + case EXCP_TRAP: + name = "TRAP insn"; + break; + case EXCP_BREAK: + name = "BREAK insn"; + break; + case EXCP_SEMIHOST: + name = "SEMIHOST insn"; + break; + } + if (name) { + qemu_log("%s at pc=0x%08x\n", name, env->pc); + } else { + qemu_log("Unknown exception %d at pc=0x%08x\n", + cs->exception_index, env->pc); + } + } switch (cs->exception_index) { case EXCP_IRQ: - assert(env->regs[CR_STATUS] & CR_STATUS_PIE); - - qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]); - - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] |= CR_STATUS_IH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->exception_addr; - break; - - case EXCP_TLBD: - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n", - env->regs[R_PC]); - - /* Fast TLB miss */ - /* Variation from the spec. Table 3-35 of the cpu reference shows - * estatus not being changed for TLB miss but this appears to - * be incorrect. */ - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL; - env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; - - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->fast_tlb_miss_addr; + /* Note that PC is advanced for interrupts as well. */ + env->pc += 4; + if (cpu->eic_present) { + do_eic_irq(cpu); } else { - qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n", - env->regs[R_PC]); - - /* Double TLB miss */ - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL; - - env->regs[R_PC] = cpu->exception_addr; + do_iic_irq(cpu); } break; - case EXCP_TLBR: - case EXCP_TLBW: - case EXCP_TLBX: - qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]); - - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; + case EXCP_TLB_D: + tlbmisc_set = CR_TLBMISC_D; + /* fall through */ + case EXCP_TLB_X: + if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { + tlbmisc_set |= CR_TLBMISC_DBL; + /* + * Normally, we don't write to tlbmisc unless !EH, + * so do it manually for the double-tlb miss exception. + */ + env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | + CR_TLBMISC_PERM | + CR_TLBMISC_BAD); + env->ctrl[CR_TLBMISC] |= tlbmisc_set; + do_exception(cpu, cpu->exception_addr, 0, false); + } else { + tlbmisc_set |= CR_TLBMISC_WE; + do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false); } - - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->exception_addr; break; - case EXCP_SUPERA: + case EXCP_PERM_R: + case EXCP_PERM_W: + tlbmisc_set = CR_TLBMISC_D; + /* fall through */ + case EXCP_PERM_X: + tlbmisc_set |= CR_TLBMISC_PERM; + if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) { + tlbmisc_set |= CR_TLBMISC_WE; + } + do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); + break; + + case EXCP_SUPERA_D: + case EXCP_UNALIGN: + tlbmisc_set = CR_TLBMISC_D; + /* fall through */ + case EXCP_SUPERA_X: + case EXCP_UNALIGND: + tlbmisc_set |= CR_TLBMISC_BAD; + do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); + break; + case EXCP_SUPERI: - case EXCP_SUPERD: - qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n", - env->regs[R_PC]); - - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[R_EA] = env->regs[R_PC] + 4; - } - - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[R_PC] = cpu->exception_addr; - break; - case EXCP_ILLEGAL: + case EXCP_DIV: case EXCP_TRAP: - qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n", - env->regs[R_PC]); - - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[R_EA] = env->regs[R_PC] + 4; - } - - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[R_PC] = cpu->exception_addr; + do_exception(cpu, cpu->exception_addr, 0, false); break; case EXCP_BREAK: - qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n", - env->regs[R_PC]); - /* The semihosting instruction is "break 1". */ - if (semihosting_enabled() && - cpu_ldl_code(env, env->regs[R_PC]) == 0x003da07a) { - qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n"); - env->regs[R_PC] += 4; - do_nios2_semihosting(env); - break; - } + do_exception(cpu, cpu->exception_addr, 0, true); + break; - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_BSTATUS] = env->regs[CR_STATUS]; - env->regs[R_BA] = env->regs[R_PC] + 4; - } - - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[R_PC] = cpu->exception_addr; + case EXCP_SEMIHOST: + do_nios2_semihosting(env); break; default: - cpu_abort(cs, "unhandled exception type=%d\n", - cs->exception_index); - break; + cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); } } @@ -232,9 +290,9 @@ void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr, Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - env->regs[CR_BADADDR] = addr; - env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2; - helper_raise_exception(env, EXCP_UNALIGN); + env->ctrl[CR_BADADDR] = addr; + cs->exception_index = EXCP_UNALIGN; + nios2_cpu_loop_exit_advance(env, retaddr); } bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -243,7 +301,7 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, { Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - unsigned int excp = EXCP_TLBD; + unsigned int excp; target_ulong vaddr, paddr; Nios2MMULookup lu; unsigned int hit; @@ -270,9 +328,10 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (probe) { return false; } - cs->exception_index = EXCP_SUPERA; - env->regs[CR_BADADDR] = address; - cpu_loop_exit_restore(cs, retaddr); + cs->exception_index = (access_type == MMU_INST_FETCH + ? EXCP_SUPERA_X : EXCP_SUPERA_D); + env->ctrl[CR_BADADDR] = address; + nios2_cpu_loop_exit_advance(env, retaddr); } } @@ -291,25 +350,23 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } /* Permission violation */ - excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR : - access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX); + excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R : + access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X); + } else { + excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D); } if (probe) { return false; } - if (access_type == MMU_INST_FETCH) { - env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D; - } else { - env->regs[CR_TLBMISC] |= CR_TLBMISC_D; - } - env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK; - env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK; - env->mmu.pteaddr_wr = env->regs[CR_PTEADDR]; + env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D, + access_type != MMU_INST_FETCH); + env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN, + address >> TARGET_PAGE_BITS); + env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR]; cs->exception_index = excp; - env->regs[CR_BADADDR] = address; - cpu_loop_exit_restore(cs, retaddr); + env->ctrl[CR_BADADDR] = address; + nios2_cpu_loop_exit_advance(env, retaddr); } -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/helper.h b/target/nios2/helper.h index 6c8f0b5b35..1648d76ade 100644 --- a/target/nios2/helper.h +++ b/target/nios2/helper.h @@ -19,9 +19,14 @@ */ DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) +DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32) +DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_2(mmu_read_debug, void, env, i32) -DEF_HELPER_3(mmu_write, void, env, i32, i32) -DEF_HELPER_1(check_interrupts, void, env) +DEF_HELPER_3(eret, noreturn, env, i32, i32) +DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32) +DEF_HELPER_3(wrprs, void, env, i32, i32) +DEF_HELPER_2(mmu_write_tlbacc, void, env, i32) +DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32) +DEF_HELPER_2(mmu_write_pteaddr, void, env, i32) #endif diff --git a/target/nios2/meson.build b/target/nios2/meson.build index e643917db1..2bd60ba306 100644 --- a/target/nios2/meson.build +++ b/target/nios2/meson.build @@ -1,15 +1,17 @@ nios2_ss = ss.source_set() nios2_ss.add(files( 'cpu.c', - 'helper.c', - 'mmu.c', 'nios2-semi.c', 'op_helper.c', 'translate.c', )) nios2_softmmu_ss = ss.source_set() -nios2_softmmu_ss.add(files('monitor.c')) +nios2_softmmu_ss.add(files( + 'helper.c', + 'monitor.c', + 'mmu.c' +)) target_arch += {'nios2': nios2_ss} target_softmmu_arch += {'nios2': nios2_softmmu_ss} diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c index 2545c06761..d9b690b78e 100644 --- a/target/nios2/mmu.c +++ b/target/nios2/mmu.c @@ -23,37 +23,9 @@ #include "cpu.h" #include "exec/exec-all.h" #include "mmu.h" +#include "exec/helper-proto.h" +#include "trace/trace-target_nios2.h" -#if !defined(CONFIG_USER_ONLY) - -/* Define this to enable MMU debug messages */ -/* #define DEBUG_MMU */ - -#ifdef DEBUG_MMU -#define MMU_LOG(x) x -#else -#define MMU_LOG(x) -#endif - -void mmu_read_debug(CPUNios2State *env, uint32_t rn) -{ - switch (rn) { - case CR_TLBACC: - MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn])); - break; - - case CR_TLBMISC: - MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn])); - break; - - case CR_PTEADDR: - MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn])); - break; - - default: - break; - } -} /* rw - 0 = read, 1 = write, 2 = fetch. */ unsigned int mmu_translate(CPUNios2State *env, @@ -61,39 +33,28 @@ unsigned int mmu_translate(CPUNios2State *env, target_ulong vaddr, int rw, int mmu_idx) { Nios2CPU *cpu = env_archcpu(env); - int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4; + int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); int vpn = vaddr >> 12; + int way, n_ways = cpu->tlb_num_ways; - MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n", - vaddr, pid, vpn)); - - int way; - for (way = 0; way < cpu->tlb_num_ways; way++) { - - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - - MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n", - (way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask), - entry->tag, (entry->tag >> 12))); + for (way = 0; way < n_ways; way++) { + uint32_t index = (way * n_ways) + (vpn & env->mmu.tlb_entry_mask); + Nios2TLBEntry *entry = &env->mmu.tlb[index]; if (((entry->tag >> 12) != vpn) || (((entry->tag & (1 << 11)) == 0) && ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) != pid))) { + trace_nios2_mmu_translate_miss(vaddr, pid, index, entry->tag); continue; } + lu->vaddr = vaddr & TARGET_PAGE_MASK; - lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS; + lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS; lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) | ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) | ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0); - MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n", - (way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask), - lu->vaddr, lu->paddr, lu->prot)); + trace_nios2_mmu_translate_hit(vaddr, pid, index, lu->paddr, lu->prot); return 1; } return 0; @@ -104,141 +65,119 @@ static void mmu_flush_pid(CPUNios2State *env, uint32_t pid) CPUState *cs = env_cpu(env); Nios2CPU *cpu = env_archcpu(env); int idx; - MMU_LOG(qemu_log("TLB Flush PID %d\n", pid)); for (idx = 0; idx < cpu->tlb_num_entries; idx++) { Nios2TLBEntry *entry = &env->mmu.tlb[idx]; - MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n", - idx, entry->tag, entry->data)); - if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) && ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) == pid)) { uint32_t vaddr = entry->tag & TARGET_PAGE_MASK; - MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr)); - + trace_nios2_mmu_flush_pid_hit(pid, idx, vaddr); tlb_flush_page(cs, vaddr); + } else { + trace_nios2_mmu_flush_pid_miss(pid, idx, entry->tag); } } } -void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v) +void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) { CPUState *cs = env_cpu(env); Nios2CPU *cpu = env_archcpu(env); - MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v)); + trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG), + (v & CR_TLBACC_C) ? 'C' : '.', + (v & CR_TLBACC_R) ? 'R' : '.', + (v & CR_TLBACC_W) ? 'W' : '.', + (v & CR_TLBACC_X) ? 'X' : '.', + (v & CR_TLBACC_G) ? 'G' : '.', + FIELD_EX32(v, CR_TLBACC, PFN)); - switch (rn) { - case CR_TLBACC: - MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n", - v >> CR_TLBACC_IGN_SHIFT, - (v & CR_TLBACC_C) ? 'C' : '.', - (v & CR_TLBACC_R) ? 'R' : '.', - (v & CR_TLBACC_W) ? 'W' : '.', - (v & CR_TLBACC_X) ? 'X' : '.', - (v & CR_TLBACC_G) ? 'G' : '.', - v & CR_TLBACC_PFN_MASK)); + /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ + if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) { + int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY); + int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); + int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); + int g = FIELD_EX32(v, CR_TLBACC, G); + int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000; + Nios2TLBEntry *entry = + &env->mmu.tlb[(way * cpu->tlb_num_ways) + + (vpn & env->mmu.tlb_entry_mask)]; + uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid; + uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W | + CR_TLBACC_X | R_CR_TLBACC_PFN_MASK); - /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ - if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) { - int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT); - int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2; - int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4; - int g = (v & CR_TLBACC_G) ? 1 : 0; - int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0; - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid; - uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W | - CR_TLBACC_X | CR_TLBACC_PFN_MASK); - - if ((entry->tag != newTag) || (entry->data != newData)) { - if (entry->tag & (1 << 10)) { - /* Flush existing entry */ - MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n", - entry->tag & TARGET_PAGE_MASK)); - tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK); - } - entry->tag = newTag; - entry->data = newData; - MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n", - (way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask), - entry->tag, entry->data)); + if ((entry->tag != newTag) || (entry->data != newData)) { + if (entry->tag & (1 << 10)) { + /* Flush existing entry */ + tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK); } - /* Auto-increment tlbmisc.WAY */ - env->regs[CR_TLBMISC] = - (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) | - (((way + 1) & (cpu->tlb_num_ways - 1)) << - CR_TLBMISC_WAY_SHIFT); + entry->tag = newTag; + entry->data = newData; } - - /* Writes to TLBACC don't change the read-back value */ - env->mmu.tlbacc_wr = v; - break; - - case CR_TLBMISC: - MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n", - v >> CR_TLBMISC_WAY_SHIFT, - (v & CR_TLBMISC_RD) ? 'R' : '.', - (v & CR_TLBMISC_WR) ? 'W' : '.', - (v & CR_TLBMISC_DBL) ? '2' : '.', - (v & CR_TLBMISC_BAD) ? 'B' : '.', - (v & CR_TLBMISC_PERM) ? 'P' : '.', - (v & CR_TLBMISC_D) ? 'D' : '.', - (v & CR_TLBMISC_PID_MASK) >> 4)); - - if ((v & CR_TLBMISC_PID_MASK) != - (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) { - mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> - CR_TLBMISC_PID_SHIFT); - } - /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ - if (v & CR_TLBMISC_RD) { - int way = (v >> CR_TLBMISC_WAY_SHIFT); - int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2; - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - - env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK; - env->regs[CR_TLBACC] |= entry->data; - env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; - env->regs[CR_TLBMISC] = - (v & ~CR_TLBMISC_PID_MASK) | - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) << - CR_TLBMISC_PID_SHIFT); - env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK; - env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT; - MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, " - "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n", - way, vpn, entry->tag, entry->data, - env->regs[CR_TLBACC], env->regs[CR_TLBMISC], - env->regs[CR_PTEADDR])); - } else { - env->regs[CR_TLBMISC] = v; - } - - env->mmu.tlbmisc_wr = v; - break; - - case CR_PTEADDR: - MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n", - v >> CR_PTEADDR_PTBASE_SHIFT, - (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT)); - - /* Writes to PTEADDR don't change the read-back VPN value */ - env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) | - (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK); - env->mmu.pteaddr_wr = v; - break; - - default: - break; + /* Auto-increment tlbmisc.WAY */ + env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], + CR_TLBMISC, WAY, + (way + 1) & (cpu->tlb_num_ways - 1)); } + + /* Writes to TLBACC don't change the read-back value */ + env->mmu.tlbacc_wr = v; +} + +void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v) +{ + Nios2CPU *cpu = env_archcpu(env); + uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID); + uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); + uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY); + + trace_nios2_mmu_write_tlbmisc(way, + (v & CR_TLBMISC_RD) ? 'R' : '.', + (v & CR_TLBMISC_WE) ? 'W' : '.', + (v & CR_TLBMISC_DBL) ? '2' : '.', + (v & CR_TLBMISC_BAD) ? 'B' : '.', + (v & CR_TLBMISC_PERM) ? 'P' : '.', + (v & CR_TLBMISC_D) ? 'D' : '.', + new_pid); + + if (new_pid != old_pid) { + mmu_flush_pid(env, old_pid); + } + + /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ + if (v & CR_TLBMISC_RD) { + int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); + Nios2TLBEntry *entry = + &env->mmu.tlb[(way * cpu->tlb_num_ways) + + (vpn & env->mmu.tlb_entry_mask)]; + + env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK; + env->ctrl[CR_TLBACC] |= entry->data; + env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; + env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID, + entry->tag & + ((1 << cpu->pid_num_bits) - 1)); + env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], + CR_PTEADDR, VPN, + entry->tag >> TARGET_PAGE_BITS); + } else { + env->ctrl[CR_TLBMISC] = v; + } + + env->mmu.tlbmisc_wr = v; +} + +void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v) +{ + trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE), + FIELD_EX32(v, CR_PTEADDR, VPN)); + + /* Writes to PTEADDR don't change the read-back VPN value */ + env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) | + (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK)); + env->mmu.pteaddr_wr = v; } void mmu_init(CPUNios2State *env) @@ -246,8 +185,6 @@ void mmu_init(CPUNios2State *env) Nios2CPU *cpu = env_archcpu(env); Nios2MMU *mmu = &env->mmu; - MMU_LOG(qemu_log("mmu_init\n")); - mmu->tlb_entry_mask = (cpu->tlb_num_entries / cpu->tlb_num_ways) - 1; mmu->tlb = g_new0(Nios2TLBEntry, cpu->tlb_num_entries); } @@ -270,12 +207,10 @@ void dump_mmu(CPUNios2State *env) entry->tag >> 12, entry->tag & ((1 << cpu->pid_num_bits) - 1), (entry->tag & (1 << 11)) ? 'G' : '-', - entry->data & CR_TLBACC_PFN_MASK, + FIELD_EX32(entry->data, CR_TLBACC, PFN), (entry->data & CR_TLBACC_C) ? 'C' : '-', (entry->data & CR_TLBACC_R) ? 'R' : '-', (entry->data & CR_TLBACC_W) ? 'W' : '-', (entry->data & CR_TLBACC_X) ? 'X' : '-'); } } - -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/mmu.h b/target/nios2/mmu.h index 4f46fbb82e..5b085900fb 100644 --- a/target/nios2/mmu.h +++ b/target/nios2/mmu.h @@ -21,6 +21,8 @@ #ifndef NIOS2_MMU_H #define NIOS2_MMU_H +#include "cpu.h" + typedef struct Nios2TLBEntry { target_ulong tag; target_ulong data; @@ -44,7 +46,6 @@ void mmu_flip_um(CPUNios2State *env, unsigned int um); unsigned int mmu_translate(CPUNios2State *env, Nios2MMULookup *lu, target_ulong vaddr, int rw, int mmu_idx); -void mmu_read_debug(CPUNios2State *env, uint32_t rn); void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v); void mmu_init(CPUNios2State *env); diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index fe5598bae4..ec88474a73 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -28,7 +28,6 @@ #if defined(CONFIG_USER_ONLY) #include "qemu.h" #else -#include "qemu-common.h" #include "exec/softmmu-semi.h" #endif #include "qemu/log.h" @@ -400,20 +399,17 @@ void do_nios2_semihosting(CPUNios2State *env) arg0, 0); return; } else { - qemu_timeval tv; struct gdb_timeval *p; - result = qemu_gettimeofday(&tv); - if (result != 0) { - p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), - 0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - p->tv_sec = cpu_to_be32(tv.tv_sec); - p->tv_usec = cpu_to_be64(tv.tv_usec); - unlock_user(p, arg0, sizeof(struct gdb_timeval)); - } + int64_t rt = g_get_real_time(); + p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); + if (!p) { + result = -1; + errno = EFAULT; + } else { + result = 0; + p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); + p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); + unlock_user(p, arg0, sizeof(struct gdb_timeval)); } } break; diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c index a59003855a..2e30d0a908 100644 --- a/target/nios2/op_helper.c +++ b/target/nios2/op_helper.c @@ -21,41 +21,100 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "qemu/main-loop.h" -#if !defined(CONFIG_USER_ONLY) -void helper_mmu_read_debug(CPUNios2State *env, uint32_t rn) -{ - mmu_read_debug(env, rn); -} - -void helper_mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v) -{ - mmu_write(env, rn, v); -} - -static void nios2_check_interrupts(CPUNios2State *env) -{ - if (env->irq_pending && - (env->regs[CR_STATUS] & CR_STATUS_PIE)) { - env->irq_pending = 0; - cpu_interrupt(env_cpu(env), CPU_INTERRUPT_HARD); - } -} - -void helper_check_interrupts(CPUNios2State *env) -{ - qemu_mutex_lock_iothread(); - nios2_check_interrupts(env); - qemu_mutex_unlock_iothread(); -} -#endif /* !CONFIG_USER_ONLY */ - void helper_raise_exception(CPUNios2State *env, uint32_t index) { CPUState *cs = env_cpu(env); cs->exception_index = index; cpu_loop_exit(cs); } + +void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + /* + * Note that PC is advanced for all hardware exceptions. + * Do this here, rather than in restore_state_to_opc(), + * lest we affect QEMU internal exceptions, like EXCP_DEBUG. + */ + cpu_restore_state(cs, retaddr, true); + env->pc += 4; + cpu_loop_exit(cs); +} + +static void maybe_raise_div(CPUNios2State *env, uintptr_t ra) +{ + Nios2CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (cpu->diverr_present) { + cs->exception_index = EXCP_DIV; + nios2_cpu_loop_exit_advance(env, ra); + } +} + +int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den) +{ + if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) { + maybe_raise_div(env, GETPC()); + return num; /* undefined */ + } + return num / den; +} + +uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den) +{ + if (unlikely(den == 0)) { + maybe_raise_div(env, GETPC()); + return num; /* undefined */ + } + return num / den; +} + +#ifndef CONFIG_USER_ONLY +void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc) +{ + Nios2CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (unlikely(new_pc & 3)) { + env->ctrl[CR_BADADDR] = new_pc; + cs->exception_index = EXCP_UNALIGND; + nios2_cpu_loop_exit_advance(env, GETPC()); + } + + /* + * None of estatus, bstatus, or sstatus have constraints on write; + * do not allow reserved fields in status to be set. + * When shadow registers are enabled, eret *does* restore CRS. + * Rather than testing eic_present to decide, mask CRS out of + * the set of readonly fields. + */ + new_status &= cpu->cr_state[CR_STATUS].writable | + (cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK); + + env->ctrl[CR_STATUS] = new_status; + env->pc = new_pc; + nios2_update_crs(env); + cpu_loop_exit(cs); +} + +/* + * RDPRS and WRPRS are implemented out of line so that if PRS == CRS, + * all of the tcg global temporaries are synced back to ENV. + */ +uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno) +{ + unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); + return env->shadow_regs[prs][regno]; +} + +void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val) +{ + unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); + env->shadow_regs[prs][regno] = val; +} +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/trace-events b/target/nios2/trace-events new file mode 100644 index 0000000000..07f1f0a5e7 --- /dev/null +++ b/target/nios2/trace-events @@ -0,0 +1,10 @@ +# mmu.c +nios2_mmu_translate_miss(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t tag) "mmu_translate: MISS vaddr=0x%08x pid=%u TLB[%u] tag=0x%08x" +nios2_mmu_translate_hit(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t paddr, uint32_t prot) "mmu_translate: HIT vaddr=0x%08x pid=%u TLB[%u] paddr=0x%08x prot=0x%x" + +nios2_mmu_flush_pid_miss(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: MISS pid=%u TLB[%u] tag=0x%08x" +nios2_mmu_flush_pid_hit(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: HIT pid=%u TLB[%u] vaddr=0x%08x" + +nios2_mmu_write_tlbacc(uint32_t ig, char c, char r, char w, char x, char g, uint32_t pfn) "mmu_write_tlbacc: ig=0x%02x flags=%c%c%c%c%c pfn=0x%08x" +nios2_mmu_write_tlbmisc(uint32_t way, char r, char w, char t, char b, char p, char d, uint32_t pid) "mmu_write_tlbmisc: way=0x%x flags=%c%c%c%c%c%c pid=%u" +nios2_mmu_write_pteaddr(uint32_t ptb, uint32_t vpn) "mmu_write_pteaddr: ptbase=0x%03x vpn=0x%05x" diff --git a/target/nios2/translate.c b/target/nios2/translate.c index f9abc2fdd2..3a037a68cc 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -33,9 +33,9 @@ #include "exec/translator.h" #include "qemu/qemu-print.h" #include "exec/gen-icount.h" +#include "semihosting/semihost.h" /* is_jmp field values */ -#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ #define INSTRUCTION_FLG(func, flags) { (func), (flags) } @@ -52,32 +52,53 @@ #define INSN_R_TYPE 0x3A /* I-Type instruction parsing */ +typedef struct { + uint8_t op; + union { + uint16_t u; + int16_t s; + } imm16; + uint8_t b; + uint8_t a; +} InstrIType; + #define I_TYPE(instr, code) \ - struct { \ - uint8_t op; \ - union { \ - uint16_t u; \ - int16_t s; \ - } imm16; \ - uint8_t b; \ - uint8_t a; \ - } (instr) = { \ + InstrIType (instr) = { \ .op = extract32((code), 0, 6), \ .imm16.u = extract32((code), 6, 16), \ .b = extract32((code), 22, 5), \ .a = extract32((code), 27, 5), \ } +typedef target_ulong ImmFromIType(const InstrIType *); + +static target_ulong imm_unsigned(const InstrIType *i) +{ + return i->imm16.u; +} + +static target_ulong imm_signed(const InstrIType *i) +{ + return i->imm16.s; +} + +static target_ulong imm_shifted(const InstrIType *i) +{ + return i->imm16.u << 16; +} + /* R-Type instruction parsing */ +typedef struct { + uint8_t op; + uint8_t imm5; + uint8_t opx; + uint8_t c; + uint8_t b; + uint8_t a; +} InstrRType; + #define R_TYPE(instr, code) \ - struct { \ - uint8_t op; \ - uint8_t imm5; \ - uint8_t opx; \ - uint8_t c; \ - uint8_t b; \ - uint8_t a; \ - } (instr) = { \ + InstrRType (instr) = { \ .op = extract32((code), 0, 6), \ .imm5 = extract32((code), 6, 5), \ .opx = extract32((code), 11, 6), \ @@ -87,23 +108,36 @@ } /* J-Type instruction parsing */ +typedef struct { + uint8_t op; + uint32_t imm26; +} InstrJType; + #define J_TYPE(instr, code) \ - struct { \ - uint8_t op; \ - uint32_t imm26; \ - } (instr) = { \ + InstrJType (instr) = { \ .op = extract32((code), 0, 6), \ .imm26 = extract32((code), 6, 26), \ } +typedef void GenFn2i(TCGv, TCGv, target_long); +typedef void GenFn3(TCGv, TCGv, TCGv); +typedef void GenFn4(TCGv, TCGv, TCGv, TCGv); + typedef struct DisasContext { DisasContextBase base; - TCGv_i32 zero; target_ulong pc; int mem_idx; + uint32_t tb_flags; + TCGv sink; + const ControlRegState *cr_state; + bool eic_present; } DisasContext; -static TCGv cpu_R[NUM_CORE_REGS]; +static TCGv cpu_R[NUM_GP_REGS]; +static TCGv cpu_pc; +#ifndef CONFIG_USER_ONLY +static TCGv cpu_crs_R[NUM_GP_REGS]; +#endif typedef struct Nios2Instruction { void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); @@ -122,31 +156,57 @@ static uint8_t get_opxcode(uint32_t code) return instr.opx; } -static TCGv load_zero(DisasContext *dc) +static TCGv load_gpr(DisasContext *dc, unsigned reg) { - if (!dc->zero) { - dc->zero = tcg_const_i32(0); - } - return dc->zero; -} + assert(reg < NUM_GP_REGS); -static TCGv load_gpr(DisasContext *dc, uint8_t reg) -{ - if (likely(reg != R_ZERO)) { + /* + * With shadow register sets, register r0 does not necessarily contain 0, + * but it is overwhelmingly likely that it does -- software is supposed + * to have set r0 to 0 in every shadow register set before use. + */ + if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { + return tcg_constant_tl(0); + } + if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { return cpu_R[reg]; - } else { - return load_zero(dc); } +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + return cpu_crs_R[reg]; +#endif } -static void t_gen_helper_raise_exception(DisasContext *dc, - uint32_t index) +static TCGv dest_gpr(DisasContext *dc, unsigned reg) { - TCGv_i32 tmp = tcg_const_i32(index); + assert(reg < NUM_GP_REGS); - tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + /* + * The spec for shadow register sets isn't clear, but we assume that + * writes to r0 are discarded regardless of CRS. + */ + if (unlikely(reg == R_ZERO)) { + if (dc->sink == NULL) { + dc->sink = tcg_temp_new(); + } + return dc->sink; + } + if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { + return cpu_R[reg]; + } +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + return cpu_crs_R[reg]; +#endif +} + +static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index) +{ + /* Note that PC is advanced for all hardware exceptions. */ + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_helper_raise_exception(cpu_env, tcg_constant_i32(index)); dc->base.is_jmp = DISAS_NORETURN; } @@ -156,12 +216,36 @@ static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_R[R_PC], dest); + tcg_gen_movi_tl(cpu_pc, dest); tcg_gen_exit_tb(tb, n); } else { - tcg_gen_movi_tl(cpu_R[R_PC], dest); - tcg_gen_exit_tb(NULL, 0); + tcg_gen_movi_tl(cpu_pc, dest); + tcg_gen_lookup_and_goto_ptr(); } + dc->base.is_jmp = DISAS_NORETURN; +} + +static void gen_jumpr(DisasContext *dc, int regno, bool is_call) +{ + TCGLabel *l = gen_new_label(); + TCGv test = tcg_temp_new(); + TCGv dest = load_gpr(dc, regno); + + tcg_gen_andi_tl(test, dest, 3); + tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l); + tcg_temp_free(test); + + tcg_gen_mov_tl(cpu_pc, dest); + if (is_call) { + tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); + } + tcg_gen_lookup_and_goto_ptr(); + + gen_set_label(l); + tcg_gen_st_tl(dest, cpu_env, offsetof(CPUNios2State, ctrl[CR_BADADDR])); + t_gen_helper_raise_exception(dc, EXCP_UNALIGND); + + dc->base.is_jmp = DISAS_NORETURN; } static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) @@ -169,12 +253,14 @@ static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) t_gen_helper_raise_exception(dc, flags); } -static void gen_check_supervisor(DisasContext *dc) +static bool gen_check_supervisor(DisasContext *dc) { - if (dc->base.tb->flags & CR_STATUS_U) { + if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) { /* CPU in user mode, privileged instruction called, stop. */ t_gen_helper_raise_exception(dc, EXCP_SUPERI); + return false; } + return true; } /* @@ -193,12 +279,11 @@ static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) { J_TYPE(instr, code); gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); - dc->base.is_jmp = DISAS_NORETURN; } static void call(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next); + tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); jmpi(dc, code, flags); } @@ -211,27 +296,10 @@ static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) I_TYPE(instr, code); TCGv addr = tcg_temp_new(); - TCGv data; - - /* - * WARNING: Loads into R_ZERO are ignored, but we must generate the - * memory access itself to emulate the CPU precisely. Load - * from a protected page to R_ZERO will cause SIGSEGV on - * the Nios2 CPU. - */ - if (likely(instr.b != R_ZERO)) { - data = cpu_R[instr.b]; - } else { - data = tcg_temp_new(); - } + TCGv data = dest_gpr(dc, instr.b); tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); - - if (unlikely(instr.b == R_ZERO)) { - tcg_temp_free(data); - } - tcg_temp_free(addr); } @@ -253,7 +321,6 @@ static void br(DisasContext *dc, uint32_t code, uint32_t flags) I_TYPE(instr, code); gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4)); - dc->base.is_jmp = DISAS_NORETURN; } static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) @@ -261,48 +328,86 @@ static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) I_TYPE(instr, code); TCGLabel *l1 = gen_new_label(); - tcg_gen_brcond_tl(flags, cpu_R[instr.a], cpu_R[instr.b], l1); + tcg_gen_brcond_tl(flags, load_gpr(dc, instr.a), load_gpr(dc, instr.b), l1); gen_goto_tb(dc, 0, dc->base.pc_next); gen_set_label(l1); gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4)); - dc->base.is_jmp = DISAS_NORETURN; } /* Comparison instructions */ -#define gen_i_cmpxx(fname, op3) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - I_TYPE(instr, (code)); \ - tcg_gen_setcondi_tl(flags, cpu_R[instr.b], cpu_R[instr.a], (op3)); \ +static void do_i_cmpxx(DisasContext *dc, uint32_t insn, + TCGCond cond, ImmFromIType *imm) +{ + I_TYPE(instr, insn); + tcg_gen_setcondi_tl(cond, dest_gpr(dc, instr.b), + load_gpr(dc, instr.a), imm(&instr)); } -gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s) -gen_i_cmpxx(gen_cmpxxui, instr.imm16.u) +#define gen_i_cmpxx(fname, imm) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_i_cmpxx(dc, code, flags, imm); } + +gen_i_cmpxx(gen_cmpxxsi, imm_signed) +gen_i_cmpxx(gen_cmpxxui, imm_unsigned) /* Math/logic instructions */ -#define gen_i_math_logic(fname, insn, resimm, op3) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - I_TYPE(instr, (code)); \ - if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \ - return; \ - } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \ - tcg_gen_movi_tl(cpu_R[instr.b], (resimm) ? (op3) : 0); \ - } else { \ - tcg_gen_##insn##_tl(cpu_R[instr.b], cpu_R[instr.a], (op3)); \ - } \ +static void do_i_math_logic(DisasContext *dc, uint32_t insn, + GenFn2i *fn, ImmFromIType *imm, + bool x_op_0_eq_x) +{ + I_TYPE(instr, insn); + target_ulong val; + + if (unlikely(instr.b == R_ZERO)) { + /* Store to R_ZERO is ignored -- this catches the canonical NOP. */ + return; + } + + val = imm(&instr); + + if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { + /* This catches the canonical expansions of movi and movhi. */ + tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0); + } else { + fn(dest_gpr(dc, instr.b), load_gpr(dc, instr.a), val); + } } -gen_i_math_logic(addi, addi, 1, instr.imm16.s) -gen_i_math_logic(muli, muli, 0, instr.imm16.s) +#define gen_i_math_logic(fname, insn, x_op_0, imm) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_i_math_logic(dc, code, tcg_gen_##insn##_tl, imm, x_op_0); } -gen_i_math_logic(andi, andi, 0, instr.imm16.u) -gen_i_math_logic(ori, ori, 1, instr.imm16.u) -gen_i_math_logic(xori, xori, 1, instr.imm16.u) +gen_i_math_logic(addi, addi, 1, imm_signed) +gen_i_math_logic(muli, muli, 0, imm_signed) -gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16) -gen_i_math_logic(orhi , ori, 1, instr.imm16.u << 16) -gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16) +gen_i_math_logic(andi, andi, 0, imm_unsigned) +gen_i_math_logic(ori, ori, 1, imm_unsigned) +gen_i_math_logic(xori, xori, 1, imm_unsigned) + +gen_i_math_logic(andhi, andi, 0, imm_shifted) +gen_i_math_logic(orhi , ori, 1, imm_shifted) +gen_i_math_logic(xorhi, xori, 1, imm_shifted) + +/* rB <- prs.rA + sigma(IMM16) */ +static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags) +{ + if (!dc->eic_present) { + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); + return; + } + if (!gen_check_supervisor(dc)) { + return; + } + +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + I_TYPE(instr, code); + TCGv dest = dest_gpr(dc, instr.b); + gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a)); + tcg_gen_addi_tl(dest, dest, instr.imm16.s); +#endif +} /* Prototype only, defined below */ static void handle_r_type_instr(DisasContext *dc, uint32_t code, @@ -365,7 +470,7 @@ static const Nios2Instruction i_type_instructions[] = { INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */ INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ - INSTRUCTION_UNIMPLEMENTED(), /* rdprs */ + INSTRUCTION(rdprs), /* rdprs */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ INSTRUCTION_NOP(), /* flushd */ @@ -384,26 +489,51 @@ static const Nios2Instruction i_type_instructions[] = { */ static void eret(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_mov_tl(cpu_R[CR_STATUS], cpu_R[CR_ESTATUS]); - tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_EA]); + if (!gen_check_supervisor(dc)) { + return; + } - dc->base.is_jmp = DISAS_JUMP; +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { + TCGv tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS])); + gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_EA)); + tcg_temp_free(tmp); + } else { + gen_helper_eret(cpu_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA)); + } + dc->base.is_jmp = DISAS_NORETURN; +#endif } /* PC <- ra */ static void ret(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_RA]); - - dc->base.is_jmp = DISAS_JUMP; + gen_jumpr(dc, R_RA, false); } -/* PC <- ba */ +/* + * status <- bstatus + * PC <- ba + */ static void bret(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_BA]); + if (!gen_check_supervisor(dc)) { + return; + } - dc->base.is_jmp = DISAS_JUMP; +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + TCGv tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS])); + gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_BA)); + tcg_temp_free(tmp); + + dc->base.is_jmp = DISAS_NORETURN; +#endif } /* PC <- rA */ @@ -411,9 +541,7 @@ static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a)); - - dc->base.is_jmp = DISAS_JUMP; + gen_jumpr(dc, instr.a, false); } /* rC <- PC + 4 */ @@ -421,9 +549,7 @@ static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - if (likely(instr.c != R_ZERO)) { - tcg_gen_movi_tl(cpu_R[instr.c], dc->base.pc_next); - } + tcg_gen_movi_tl(dest_gpr(dc, instr.c), dc->base.pc_next); } /* @@ -434,77 +560,144 @@ static void callr(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a)); - tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next); - - dc->base.is_jmp = DISAS_JUMP; + gen_jumpr(dc, instr.a, true); } /* rC <- ctlN */ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) { + if (!gen_check_supervisor(dc)) { + return; + } + +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else R_TYPE(instr, code); + TCGv t1, t2, dest = dest_gpr(dc, instr.c); - gen_check_supervisor(dc); - - switch (instr.imm5 + CR_BASE) { - case CR_PTEADDR: - case CR_TLBACC: - case CR_TLBMISC: - { -#if !defined(CONFIG_USER_ONLY) - if (likely(instr.c != R_ZERO)) { - tcg_gen_mov_tl(cpu_R[instr.c], cpu_R[instr.imm5 + CR_BASE]); -#ifdef DEBUG_MMU - TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); - gen_helper_mmu_read_debug(cpu_R[instr.c], cpu_env, tmp); - tcg_temp_free_i32(tmp); -#endif - } -#endif - break; + /* Reserved registers read as zero. */ + if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) { + tcg_gen_movi_tl(dest, 0); + return; } + switch (instr.imm5) { + case CR_IPENDING: + /* + * The value of the ipending register is synthetic. + * In hw, this is the AND of a set of hardware irq lines + * with the ienable register. In qemu, we re-use the space + * of CR_IPENDING to store the set of irq lines, and so we + * must perform the AND here, and anywhere else we need the + * guest value of ipending. + */ + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + tcg_gen_ld_tl(t1, cpu_env, offsetof(CPUNios2State, ctrl[CR_IPENDING])); + tcg_gen_ld_tl(t2, cpu_env, offsetof(CPUNios2State, ctrl[CR_IENABLE])); + tcg_gen_and_tl(dest, t1, t2); + tcg_temp_free(t1); + tcg_temp_free(t2); + break; default: - if (likely(instr.c != R_ZERO)) { - tcg_gen_mov_tl(cpu_R[instr.c], cpu_R[instr.imm5 + CR_BASE]); - } + tcg_gen_ld_tl(dest, cpu_env, + offsetof(CPUNios2State, ctrl[instr.imm5])); break; } +#endif } /* ctlN <- rA */ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) { + if (!gen_check_supervisor(dc)) { + return; + } + +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else R_TYPE(instr, code); + TCGv v = load_gpr(dc, instr.a); + uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]); + uint32_t wr = dc->cr_state[instr.imm5].writable; + uint32_t ro = dc->cr_state[instr.imm5].readonly; - gen_check_supervisor(dc); + /* Skip reserved or readonly registers. */ + if (wr == 0) { + return; + } - switch (instr.imm5 + CR_BASE) { + switch (instr.imm5) { case CR_PTEADDR: + gen_helper_mmu_write_pteaddr(cpu_env, v); + break; case CR_TLBACC: + gen_helper_mmu_write_tlbacc(cpu_env, v); + break; case CR_TLBMISC: - { -#if !defined(CONFIG_USER_ONLY) - TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); - gen_helper_mmu_write(cpu_env, tmp, load_gpr(dc, instr.a)); - tcg_temp_free_i32(tmp); -#endif + gen_helper_mmu_write_tlbmisc(cpu_env, v); break; - } - + case CR_STATUS: + case CR_IENABLE: + /* If interrupts were enabled using WRCTL, trigger them. */ + dc->base.is_jmp = DISAS_UPDATE; + /* fall through */ default: - tcg_gen_mov_tl(cpu_R[instr.imm5 + CR_BASE], load_gpr(dc, instr.a)); + if (wr == -1) { + /* The register is entirely writable. */ + tcg_gen_st_tl(v, cpu_env, ofs); + } else { + /* + * The register is partially read-only or reserved: + * merge the value. + */ + TCGv n = tcg_temp_new(); + + tcg_gen_andi_tl(n, v, wr); + + if (ro != 0) { + TCGv o = tcg_temp_new(); + tcg_gen_ld_tl(o, cpu_env, ofs); + tcg_gen_andi_tl(o, o, ro); + tcg_gen_or_tl(n, n, o); + tcg_temp_free(o); + } + + tcg_gen_st_tl(n, cpu_env, ofs); + tcg_temp_free(n); + } break; } +#endif +} - /* If interrupts were enabled using WRCTL, trigger them. */ -#if !defined(CONFIG_USER_ONLY) - if ((instr.imm5 + CR_BASE) == CR_STATUS) { - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_check_interrupts(cpu_env); +/* prs.rC <- rA */ +static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags) +{ + if (!dc->eic_present) { + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); + return; + } + if (!gen_check_supervisor(dc)) { + return; + } + +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + R_TYPE(instr, code); + gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c), + load_gpr(dc, instr.a)); + /* + * The expected write to PRS[r0] is 0, from CRS[r0]. + * If not, and CRS == PRS (which we cannot tell from here), + * we may now have a non-zero value in our current r0. + * By ending the TB, we re-evaluate tb_flags and find out. + */ + if (instr.c == 0 + && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) { dc->base.is_jmp = DISAS_UPDATE; } #endif @@ -514,126 +707,95 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - if (likely(instr.c != R_ZERO)) { - tcg_gen_setcond_tl(flags, cpu_R[instr.c], cpu_R[instr.a], - cpu_R[instr.b]); - } + tcg_gen_setcond_tl(flags, dest_gpr(dc, instr.c), + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } /* Math/logic instructions */ -#define gen_r_math_logic(fname, insn, op3) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - R_TYPE(instr, (code)); \ - if (likely(instr.c != R_ZERO)) { \ - tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), (op3)); \ - } \ +static void do_ri_math_logic(DisasContext *dc, uint32_t insn, GenFn2i *fn) +{ + R_TYPE(instr, insn); + fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), instr.imm5); } -gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b)) - -gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b)) - -gen_r_math_logic(srai, sari_tl, instr.imm5) -gen_r_math_logic(srli, shri_tl, instr.imm5) -gen_r_math_logic(slli, shli_tl, instr.imm5) -gen_r_math_logic(roli, rotli_tl, instr.imm5) - -#define gen_r_mul(fname, insn) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - R_TYPE(instr, (code)); \ - if (likely(instr.c != R_ZERO)) { \ - TCGv t0 = tcg_temp_new(); \ - tcg_gen_##insn(t0, cpu_R[instr.c], \ - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \ - tcg_temp_free(t0); \ - } \ +static void do_rr_math_logic(DisasContext *dc, uint32_t insn, GenFn3 *fn) +{ + R_TYPE(instr, insn); + fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } -gen_r_mul(mulxss, muls2_tl) -gen_r_mul(mulxuu, mulu2_tl) -gen_r_mul(mulxsu, mulsu2_tl) +#define gen_ri_math_logic(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_ri_math_logic(dc, code, tcg_gen_##insn##_tl); } -#define gen_r_shift_s(fname, insn) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - R_TYPE(instr, (code)); \ - if (likely(instr.c != R_ZERO)) { \ - TCGv t0 = tcg_temp_new(); \ - tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \ - tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), t0); \ - tcg_temp_free(t0); \ - } \ +#define gen_rr_math_logic(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_rr_math_logic(dc, code, tcg_gen_##insn##_tl); } + +gen_rr_math_logic(add, add) +gen_rr_math_logic(sub, sub) +gen_rr_math_logic(mul, mul) + +gen_rr_math_logic(and, and) +gen_rr_math_logic(or, or) +gen_rr_math_logic(xor, xor) +gen_rr_math_logic(nor, nor) + +gen_ri_math_logic(srai, sari) +gen_ri_math_logic(srli, shri) +gen_ri_math_logic(slli, shli) +gen_ri_math_logic(roli, rotli) + +static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn) +{ + R_TYPE(instr, insn); + TCGv discard = tcg_temp_new(); + + fn(discard, dest_gpr(dc, instr.c), + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); + tcg_temp_free(discard); } -gen_r_shift_s(sra, sar_tl) -gen_r_shift_s(srl, shr_tl) -gen_r_shift_s(sll, shl_tl) -gen_r_shift_s(rol, rotl_tl) -gen_r_shift_s(ror, rotr_tl) +#define gen_rr_mul_high(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_rr_mul_high(dc, code, tcg_gen_##insn##_tl); } + +gen_rr_mul_high(mulxss, muls2) +gen_rr_mul_high(mulxuu, mulu2) +gen_rr_mul_high(mulxsu, mulsu2) + +static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn) +{ + R_TYPE(instr, insn); + TCGv sh = tcg_temp_new(); + + tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31); + fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh); + tcg_temp_free(sh); +} + +#define gen_rr_shift(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_rr_shift(dc, code, tcg_gen_##insn##_tl); } + +gen_rr_shift(sra, sar) +gen_rr_shift(srl, shr) +gen_rr_shift(sll, shl) +gen_rr_shift(rol, rotl) +gen_rr_shift(ror, rotr) static void divs(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, (code)); - - /* Stores into R_ZERO are ignored */ - if (unlikely(instr.c == R_ZERO)) { - return; - } - - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - TCGv t3 = tcg_temp_new(); - - tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a)); - tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b)); - tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); - tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); - tcg_gen_and_tl(t2, t2, t3); - tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); - tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); - tcg_gen_div_tl(cpu_R[instr.c], t0, t1); - tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]); - - tcg_temp_free(t3); - tcg_temp_free(t2); - tcg_temp_free(t1); - tcg_temp_free(t0); + gen_helper_divs(dest_gpr(dc, instr.c), cpu_env, + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } static void divu(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, (code)); - - /* Stores into R_ZERO are ignored */ - if (unlikely(instr.c == R_ZERO)) { - return; - } - - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); - - tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a)); - tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b)); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); - tcg_gen_divu_tl(cpu_R[instr.c], t0, t1); - tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]); - - tcg_temp_free(t3); - tcg_temp_free(t2); - tcg_temp_free(t1); - tcg_temp_free(t0); + gen_helper_divu(dest_gpr(dc, instr.c), cpu_env, + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } static void trap(DisasContext *dc, uint32_t code, uint32_t flags) @@ -651,6 +813,20 @@ static void trap(DisasContext *dc, uint32_t code, uint32_t flags) t_gen_helper_raise_exception(dc, EXCP_TRAP); } +static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags) +{ +#ifndef CONFIG_USER_ONLY + /* The semihosting instruction is "break 1". */ + R_TYPE(instr, code); + if (semihosting_enabled() && instr.imm5 == 1) { + t_gen_helper_raise_exception(dc, EXCP_SEMIHOST); + return; + } +#endif + + t_gen_helper_raise_exception(dc, EXCP_BREAK); +} + static const Nios2Instruction r_type_instructions[] = { INSTRUCTION_ILLEGAL(), INSTRUCTION(eret), /* eret */ @@ -672,7 +848,7 @@ static const Nios2Instruction r_type_instructions[] = { INSTRUCTION_ILLEGAL(), INSTRUCTION(slli), /* slli */ INSTRUCTION(sll), /* sll */ - INSTRUCTION_UNIMPLEMENTED(), /* wrprs */ + INSTRUCTION(wrprs), /* wrprs */ INSTRUCTION_ILLEGAL(), INSTRUCTION(or), /* or */ INSTRUCTION(mulxsu), /* mulxsu */ @@ -704,7 +880,7 @@ static const Nios2Instruction r_type_instructions[] = { INSTRUCTION(add), /* add */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */ + INSTRUCTION(gen_break), /* break */ INSTRUCTION_ILLEGAL(), INSTRUCTION(nop), /* nop */ INSTRUCTION_ILLEGAL(), @@ -737,7 +913,7 @@ illegal_op: t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); } -static const char * const regnames[] = { +static const char * const gr_regnames[NUM_GP_REGS] = { "zero", "at", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", @@ -746,16 +922,20 @@ static const char * const regnames[] = { "r20", "r21", "r22", "r23", "et", "bt", "gp", "sp", "fp", "ea", "ba", "ra", +}; + +#ifndef CONFIG_USER_ONLY +static const char * const cr_regnames[NUM_CR_REGS] = { "status", "estatus", "bstatus", "ienable", - "ipending", "cpuid", "reserved0", "exception", + "ipending", "cpuid", "res6", "exception", "pteaddr", "tlbacc", "tlbmisc", "reserved1", "badaddr", "config", "mpubase", "mpuacc", - "reserved2", "reserved3", "reserved4", "reserved5", - "reserved6", "reserved7", "reserved8", "reserved9", - "reserved10", "reserved11", "reserved12", "reserved13", - "reserved14", "reserved15", "reserved16", "reserved17", - "rpc" + "res16", "res17", "res18", "res19", + "res20", "res21", "res22", "res23", + "res24", "res25", "res26", "res27", + "res28", "res29", "res30", "res31", }; +#endif #include "exec/gen-icount.h" @@ -764,9 +944,13 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUNios2State *env = cs->env_ptr; + Nios2CPU *cpu = env_archcpu(env); int page_insns; dc->mem_idx = cpu_mmu_index(env, false); + dc->cr_state = cpu->cr_state; + dc->tb_flags = dc->base.tb->flags; + dc->eic_present = cpu->eic_present; /* Bound the number of insns to execute to those left on the page. */ page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; @@ -803,13 +987,13 @@ static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) return; } - dc->zero = NULL; + dc->sink = NULL; instr = &i_type_instructions[op]; instr->handler(dc, code, instr->flags); - if (dc->zero) { - tcg_temp_free(dc->zero); + if (dc->sink) { + tcg_temp_free(dc->sink); } } @@ -820,14 +1004,12 @@ static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* Indicate where the next block should start */ switch (dc->base.is_jmp) { case DISAS_TOO_MANY: - case DISAS_UPDATE: - /* Save the current PC back into the CPU register */ - tcg_gen_movi_tl(cpu_R[R_PC], dc->base.pc_next); - tcg_gen_exit_tb(NULL, 0); + gen_goto_tb(dc, 0, dc->base.pc_next); break; - case DISAS_JUMP: - /* The jump will already have updated the PC register */ + case DISAS_UPDATE: + /* Save the current PC, and return to the main loop. */ + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); tcg_gen_exit_tb(NULL, 0); break; @@ -840,10 +1022,11 @@ static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void nios2_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void nios2_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps nios2_tr_ops = { @@ -867,41 +1050,67 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) CPUNios2State *env = &cpu->env; int i; - if (!env) { - return; - } + qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc)); - qemu_fprintf(f, "IN: PC=%x %s\n", - env->regs[R_PC], lookup_symbol(env->regs[R_PC])); - - for (i = 0; i < NUM_CORE_REGS; i++) { - qemu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); + for (i = 0; i < NUM_GP_REGS; i++) { + qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]); if ((i + 1) % 4 == 0) { qemu_fprintf(f, "\n"); } } + #if !defined(CONFIG_USER_ONLY) - qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", - env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, - (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, - env->mmu.tlbacc_wr); + int j; + + for (i = j = 0; i < NUM_CR_REGS; i++) { + if (!nios2_cr_reserved(&cpu->cr_state[i])) { + qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]); + if (++j % 4 == 0) { + qemu_fprintf(f, "\n"); + } + } + } + if (j % 4 != 0) { + qemu_fprintf(f, "\n"); + } + if (cpu->mmu_present) { + qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", + env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK, + FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID), + env->mmu.tlbacc_wr); + } #endif qemu_fprintf(f, "\n\n"); } void nios2_tcg_init(void) { - int i; +#ifndef CONFIG_USER_ONLY + TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env, + offsetof(CPUNios2State, regs), "crs"); - for (i = 0; i < NUM_CORE_REGS; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUNios2State, regs[i]), - regnames[i]); + for (int i = 0; i < NUM_GP_REGS; i++) { + cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]); } + +#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N]) +#else +#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N]) +#endif + + for (int i = 0; i < NUM_GP_REGS; i++) { + cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof_regs0(i), + gr_regnames[i]); + } + +#undef offsetof_regs0 + + cpu_pc = tcg_global_mem_new(cpu_env, + offsetof(CPUNios2State, pc), "pc"); } void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, target_ulong *data) { - env->regs[R_PC] = data[0]; + env->pc = data[0]; } diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index ee069b080c..b9584f10d4 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -21,16 +21,13 @@ #define OPENRISC_CPU_H #include "exec/cpu-defs.h" +#include "fpu/softfloat-types.h" #include "hw/core/cpu.h" #include "qom/object.h" -/* cpu_openrisc_map_address_* in CPUOpenRISCTLBContext need this decl. */ -struct OpenRISCCPU; - #define TYPE_OPENRISC_CPU "or1k-cpu" -OBJECT_DECLARE_TYPE(OpenRISCCPU, OpenRISCCPUClass, - OPENRISC_CPU) +OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) /** * OpenRISCCPUClass: @@ -231,18 +228,18 @@ typedef struct CPUOpenRISCTLBContext { OpenRISCTLBEntry itlb[TLB_SIZE]; OpenRISCTLBEntry dtlb[TLB_SIZE]; - int (*cpu_openrisc_map_address_code)(struct OpenRISCCPU *cpu, + int (*cpu_openrisc_map_address_code)(OpenRISCCPU *cpu, hwaddr *physical, int *prot, target_ulong address, int rw); - int (*cpu_openrisc_map_address_data)(struct OpenRISCCPU *cpu, + int (*cpu_openrisc_map_address_data)(OpenRISCCPU *cpu, hwaddr *physical, int *prot, target_ulong address, int rw); } CPUOpenRISCTLBContext; #endif -typedef struct CPUOpenRISCState { +typedef struct CPUArchState { target_ulong shadow_gpr[16][32]; /* Shadow registers */ target_ulong pc; /* Program counter */ @@ -301,7 +298,7 @@ typedef struct CPUOpenRISCState { * * A OpenRISC CPU. */ -struct OpenRISCCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -348,9 +345,6 @@ void cpu_openrisc_count_stop(OpenRISCCPU *cpu); #define OPENRISC_CPU_TYPE_NAME(model) model OPENRISC_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_OPENRISC_CPU -typedef CPUOpenRISCState CPUArchState; -typedef OpenRISCCPU ArchCPU; - #include "exec/cpu-all.h" #define TB_FLAGS_SM SR_SM diff --git a/target/openrisc/exception.c b/target/openrisc/exception.c index 28c1fce523..8699c3dcea 100644 --- a/target/openrisc/exception.c +++ b/target/openrisc/exception.c @@ -22,7 +22,7 @@ #include "exec/exec-all.h" #include "exception.h" -void QEMU_NORETURN raise_exception(OpenRISCCPU *cpu, uint32_t excp) +G_NORETURN void raise_exception(OpenRISCCPU *cpu, uint32_t excp) { CPUState *cs = CPU(cpu); diff --git a/target/openrisc/exception.h b/target/openrisc/exception.h index 333bf84638..f62fc314c1 100644 --- a/target/openrisc/exception.h +++ b/target/openrisc/exception.h @@ -22,6 +22,6 @@ #include "cpu.h" -void QEMU_NORETURN raise_exception(OpenRISCCPU *cpu, uint32_t excp); +G_NORETURN void raise_exception(OpenRISCCPU *cpu, uint32_t excp); #endif /* TARGET_OPENRISC_EXCEPTION_H */ diff --git a/target/openrisc/exception_helper.c b/target/openrisc/exception_helper.c index d02a1cf0aa..1f5be4bed9 100644 --- a/target/openrisc/exception_helper.c +++ b/target/openrisc/exception_helper.c @@ -30,7 +30,8 @@ void HELPER(exception)(CPUOpenRISCState *env, uint32_t excp) raise_exception(cpu, excp); } -static void QEMU_NORETURN do_range(CPUOpenRISCState *env, uintptr_t pc) +static G_NORETURN +void do_range(CPUOpenRISCState *env, uintptr_t pc) { CPUState *cs = env_cpu(env); diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index ca79e609da..7b8ad43d5f 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1687,12 +1687,13 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void openrisc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void openrisc_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { DisasContext *s = container_of(dcbase, DisasContext, base); - qemu_log("IN: %s\n", lookup_symbol(s->base.pc_first)); - log_target_disas(cs, s->base.pc_first, s->base.tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(s->base.pc_first)); + target_disas(logfile, cs, s->base.pc_first, s->base.tb->size); } static const TranslatorOps openrisc_tr_ops = { diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index 993740897d..1139cead9f 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -161,7 +161,7 @@ static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) bool needs_byteswap; ppc_avr_t *avr = cpu_avr_ptr(&cpu->env, i); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN needs_byteswap = s->dump_info.d_endian == ELFDATA2LSB; #else needs_byteswap = s->dump_info.d_endian == ELFDATA2MSB; diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 98facee9fa..ad7e3c3db9 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -29,10 +29,9 @@ #define TYPE_POWERPC_CPU "powerpc-cpu" #endif -OBJECT_DECLARE_TYPE(PowerPCCPU, PowerPCCPUClass, - POWERPC_CPU) +OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) -typedef struct CPUPPCState CPUPPCState; +typedef struct CPUArchState CPUPPCState; typedef struct ppc_tb_t ppc_tb_t; typedef struct ppc_dcr_t ppc_dcr_t; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 5b01d409b3..c2b6c987c0 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -21,6 +21,7 @@ #define PPC_CPU_H #include "qemu/int128.h" +#include "qemu/cpu-float.h" #include "exec/cpu-defs.h" #include "cpu-qom.h" #include "qom/object.h" @@ -127,8 +128,10 @@ enum { /* ISA 3.00 additions */ POWERPC_EXCP_HVIRT = 101, POWERPC_EXCP_SYSCALL_VECTORED = 102, /* scv exception */ + POWERPC_EXCP_PERFM_EBB = 103, /* Performance Monitor EBB Exception */ + POWERPC_EXCP_EXTERNAL_EBB = 104, /* External EBB Exception */ /* EOL */ - POWERPC_EXCP_NB = 103, + POWERPC_EXCP_NB = 105, /* QEMU exceptions: special cases we want to stop translation */ POWERPC_EXCP_SYSCALL_USER = 0x203, /* System call in user mode only */ }; @@ -1075,7 +1078,7 @@ struct ppc_radix_page_info { #define PPC_CPU_OPCODES_LEN 0x40 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 -struct CPUPPCState { +struct CPUArchState { /* Most commonly used resources during translated code execution first */ target_ulong gpr[32]; /* general purpose registers */ target_ulong gprh[32]; /* storage for GPR MSB, used by the SPE extension */ @@ -1273,7 +1276,7 @@ typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass; * * A PowerPC CPU. */ -struct PowerPCCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -1475,9 +1478,6 @@ void ppc_compat_add_property(Object *obj, const char *name, uint32_t *compat_pvr, const char *basedesc); #endif /* defined(TARGET_PPC64) */ -typedef CPUPPCState CPUArchState; -typedef PowerPCCPU ArchCPU; - #include "exec/cpu-all.h" /*****************************************************************************/ @@ -2434,6 +2434,7 @@ enum { PPC_INTERRUPT_HMI, /* Hypervisor Maintenance interrupt */ PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */ PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */ + PPC_INTERRUPT_EBB, /* Event-based Branch exception */ }; /* Processor Compatibility mask (PCR) */ @@ -2491,13 +2492,18 @@ static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, } #endif -void QEMU_NORETURN raise_exception(CPUPPCState *env, uint32_t exception); -void QEMU_NORETURN raise_exception_ra(CPUPPCState *env, uint32_t exception, - uintptr_t raddr); -void QEMU_NORETURN raise_exception_err(CPUPPCState *env, uint32_t exception, - uint32_t error_code); -void QEMU_NORETURN raise_exception_err_ra(CPUPPCState *env, uint32_t exception, - uint32_t error_code, uintptr_t raddr); +G_NORETURN void raise_exception(CPUPPCState *env, uint32_t exception); +G_NORETURN void raise_exception_ra(CPUPPCState *env, uint32_t exception, + uintptr_t raddr); +G_NORETURN void raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code); +G_NORETURN void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, + uint32_t error_code, uintptr_t raddr); + +/* PERFM EBB helper*/ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +void raise_ebb_perfm_exception(CPUPPCState *env); +#endif #if !defined(CONFIG_USER_ONLY) static inline int booke206_tlbm_id(CPUPPCState *env, ppcmas_tlb_t *tlbm) @@ -2637,7 +2643,7 @@ static inline bool lsw_reg_in_range(int start, int nregs, int rx) } /* Accessors for FP, VMX and VSX registers */ -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define VsrB(i) u8[i] #define VsrSB(i) s8[i] #define VsrH(i) u16[i] diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 61d36b11a0..d42e2ba8e0 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2060,6 +2060,10 @@ static void init_excp_POWER8(CPUPPCState *env) env->excp_vectors[POWERPC_EXCP_FU] = 0x00000F60; env->excp_vectors[POWERPC_EXCP_HV_FU] = 0x00000F80; env->excp_vectors[POWERPC_EXCP_SDOOR_HV] = 0x00000E80; + + /* Userland exceptions without vector value in PowerISA v3.1 */ + env->excp_vectors[POWERPC_EXCP_PERFM_EBB] = 0x0; + env->excp_vectors[POWERPC_EXCP_EXTERNAL_EBB] = 0x0; #endif } @@ -5698,12 +5702,10 @@ static void register_power9_mmu_sprs(CPUPPCState *env) */ static void init_tcg_pmu_power8(CPUPPCState *env) { -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) /* Init PMU overflow timers */ - if (!kvm_enabled()) { + if (tcg_enabled()) { cpu_ppc_pmu_init(env); } -#endif } static void init_proc_book3s_common(CPUPPCState *env) @@ -6455,6 +6457,7 @@ static void init_proc_POWER10(CPUPPCState *env) register_power5p_common_sprs(env); register_power5p_lpar_sprs(env); register_power5p_ear_sprs(env); + register_power5p_tb_sprs(env); register_power6_common_sprs(env); register_power6_dbg_sprs(env); register_power8_tce_address_control_sprs(env); @@ -6465,6 +6468,7 @@ static void init_proc_POWER10(CPUPPCState *env) register_power8_pmu_user_sprs(env); register_power8_tm_sprs(env); register_power8_pspb_sprs(env); + register_power8_dpdes_sprs(env); register_vtb_sprs(env); register_power8_ic_sprs(env); register_power8_book4_sprs(env); @@ -7148,7 +7152,7 @@ static void ppc_cpu_reset(DeviceState *dev) #if defined(TARGET_PPC64) msr |= (target_ulong)1 << MSR_TM; /* Transactional memory */ #endif -#if !defined(TARGET_WORDS_BIGENDIAN) +#if !TARGET_BIG_ENDIAN msr |= (target_ulong)1 << MSR_LE; /* Little-endian user mode */ if (!((env->msr_mask >> MSR_LE) & 1)) { fprintf(stderr, "Selected CPU does not support little-endian.\n"); @@ -7167,14 +7171,14 @@ static void ppc_cpu_reset(DeviceState *dev) #if !defined(CONFIG_USER_ONLY) env->nip = env->hreset_vector | env->excp_prefix; -#if defined(CONFIG_TCG) - if (env->mmu_model != POWERPC_MMU_REAL) { - ppc_tlb_invalidate_all(env); - } -#endif /* CONFIG_TCG */ -#endif - pmu_update_summaries(env); + if (tcg_enabled()) { + if (env->mmu_model != POWERPC_MMU_REAL) { + ppc_tlb_invalidate_all(env); + } + pmu_update_summaries(env); + } +#endif hreg_compute_hflags(env); env->reserve_addr = (target_ulong)-1ULL; /* Be sure no exception or interrupt is pending */ diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 6538c56ab0..d3e2cfcd71 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1554,6 +1554,21 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)MSR_HVB; new_msr |= env->msr & ((target_ulong)1 << MSR_RI); break; + case POWERPC_EXCP_PERFM_EBB: /* Performance Monitor EBB Exception */ + case POWERPC_EXCP_EXTERNAL_EBB: /* External EBB Exception */ + env->spr[SPR_BESCR] &= ~BESCR_GE; + + /* + * Save NIP for rfebb insn in SPR_EBBRR. Next nip is + * stored in the EBB Handler SPR_EBBHR. + */ + env->spr[SPR_EBBRR] = env->nip; + powerpc_set_excp_state(cpu, env->spr[SPR_EBBHR], env->msr); + + /* + * This exception is handled in userspace. No need to proceed. + */ + return; case POWERPC_EXCP_THERM: /* Thermal interrupt */ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_VPUA: /* Vector assist exception */ @@ -1797,6 +1812,24 @@ static void ppc_hw_interrupt(CPUPPCState *env) powerpc_excp(cpu, POWERPC_EXCP_THERM); return; } + /* EBB exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_EBB)) { + /* + * EBB exception must be taken in problem state and + * with BESCR_GE set. + */ + if (msr_pr == 1 && env->spr[SPR_BESCR] & BESCR_GE) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EBB); + + if (env->spr[SPR_BESCR] & BESCR_PMEO) { + powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB); + } else if (env->spr[SPR_BESCR] & BESCR_EEO) { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB); + } + + return; + } + } } if (env->resume_as_sreset) { @@ -2033,6 +2066,54 @@ void helper_rfebb(CPUPPCState *env, target_ulong s) env->spr[SPR_BESCR] &= ~BESCR_GE; } } + +/* + * Triggers or queues an 'ebb_excp' EBB exception. All checks + * but FSCR, HFSCR and msr_pr must be done beforehand. + * + * PowerISA v3.1 isn't clear about whether an EBB should be + * postponed or cancelled if the EBB facility is unavailable. + * Our assumption here is that the EBB is cancelled if both + * FSCR and HFSCR EBB facilities aren't available. + */ +static void do_ebb(CPUPPCState *env, int ebb_excp) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = CPU(cpu); + + /* + * FSCR_EBB and FSCR_IC_EBB are the same bits used with + * HFSCR. + */ + helper_fscr_facility_check(env, FSCR_EBB, 0, FSCR_IC_EBB); + helper_hfscr_facility_check(env, FSCR_EBB, "EBB", FSCR_IC_EBB); + + if (ebb_excp == POWERPC_EXCP_PERFM_EBB) { + env->spr[SPR_BESCR] |= BESCR_PMEO; + } else if (ebb_excp == POWERPC_EXCP_EXTERNAL_EBB) { + env->spr[SPR_BESCR] |= BESCR_EEO; + } + + if (msr_pr == 1) { + powerpc_excp(cpu, ebb_excp); + } else { + env->pending_interrupts |= 1 << PPC_INTERRUPT_EBB; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } +} + +void raise_ebb_perfm_exception(CPUPPCState *env) +{ + bool perfm_ebb_enabled = env->spr[SPR_POWER_MMCR0] & MMCR0_EBE && + env->spr[SPR_BESCR] & BESCR_PME && + env->spr[SPR_BESCR] & BESCR_GE; + + if (!perfm_ebb_enabled) { + return; + } + + do_ebb(env, POWERPC_EXCP_PERFM_EBB); +} #endif /*****************************************************************************/ diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index bd76bee7f1..99281cc37a 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -2157,9 +2157,9 @@ VSX_TSQRT(xvtsqrtsp, 4, float32, VsrW(i), -126, 23) * various forms (madd, msub, nmadd, nmsub) * sfprf - set FPRF */ -#define VSX_MADD(op, nels, tp, fld, maddflgs, sfprf, r2sp) \ +#define VSX_MADD(op, nels, tp, fld, maddflgs, sfprf) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ - ppc_vsr_t *xa, ppc_vsr_t *b, ppc_vsr_t *c) \ + ppc_vsr_t *s1, ppc_vsr_t *s2, ppc_vsr_t *s3) \ { \ ppc_vsr_t t = *xt; \ int i; \ @@ -2169,20 +2169,7 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ for (i = 0; i < nels; i++) { \ float_status tstat = env->fp_status; \ set_float_exception_flags(0, &tstat); \ - if (r2sp && (tstat.float_rounding_mode == float_round_nearest_even)) {\ - /* \ - * Avoid double rounding errors by rounding the intermediate \ - * result to odd. \ - */ \ - set_float_rounding_mode(float_round_to_zero, &tstat); \ - t.fld = tp##_muladd(xa->fld, b->fld, c->fld, \ - maddflgs, &tstat); \ - t.fld |= (get_float_exception_flags(&tstat) & \ - float_flag_inexact) != 0; \ - } else { \ - t.fld = tp##_muladd(xa->fld, b->fld, c->fld, \ - maddflgs, &tstat); \ - } \ + t.fld = tp##_muladd(s1->fld, s3->fld, s2->fld, maddflgs, &tstat); \ env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ \ if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ @@ -2190,10 +2177,6 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ sfprf, GETPC()); \ } \ \ - if (r2sp) { \ - t.fld = do_frsp(env, t.fld, GETPC()); \ - } \ - \ if (sfprf) { \ helper_compute_fprf_float64(env, t.fld); \ } \ @@ -2202,75 +2185,113 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, GETPC()); \ } -VSX_MADD(xsmadddp, 1, float64, VsrD(0), MADD_FLGS, 1, 0) -VSX_MADD(xsmsubdp, 1, float64, VsrD(0), MSUB_FLGS, 1, 0) -VSX_MADD(xsnmadddp, 1, float64, VsrD(0), NMADD_FLGS, 1, 0) -VSX_MADD(xsnmsubdp, 1, float64, VsrD(0), NMSUB_FLGS, 1, 0) -VSX_MADD(xsmaddsp, 1, float64, VsrD(0), MADD_FLGS, 1, 1) -VSX_MADD(xsmsubsp, 1, float64, VsrD(0), MSUB_FLGS, 1, 1) -VSX_MADD(xsnmaddsp, 1, float64, VsrD(0), NMADD_FLGS, 1, 1) -VSX_MADD(xsnmsubsp, 1, float64, VsrD(0), NMSUB_FLGS, 1, 1) +VSX_MADD(XSMADDDP, 1, float64, VsrD(0), MADD_FLGS, 1) +VSX_MADD(XSMSUBDP, 1, float64, VsrD(0), MSUB_FLGS, 1) +VSX_MADD(XSNMADDDP, 1, float64, VsrD(0), NMADD_FLGS, 1) +VSX_MADD(XSNMSUBDP, 1, float64, VsrD(0), NMSUB_FLGS, 1) +VSX_MADD(XSMADDSP, 1, float64r32, VsrD(0), MADD_FLGS, 1) +VSX_MADD(XSMSUBSP, 1, float64r32, VsrD(0), MSUB_FLGS, 1) +VSX_MADD(XSNMADDSP, 1, float64r32, VsrD(0), NMADD_FLGS, 1) +VSX_MADD(XSNMSUBSP, 1, float64r32, VsrD(0), NMSUB_FLGS, 1) -VSX_MADD(xvmadddp, 2, float64, VsrD(i), MADD_FLGS, 0, 0) -VSX_MADD(xvmsubdp, 2, float64, VsrD(i), MSUB_FLGS, 0, 0) -VSX_MADD(xvnmadddp, 2, float64, VsrD(i), NMADD_FLGS, 0, 0) -VSX_MADD(xvnmsubdp, 2, float64, VsrD(i), NMSUB_FLGS, 0, 0) +VSX_MADD(xvmadddp, 2, float64, VsrD(i), MADD_FLGS, 0) +VSX_MADD(xvmsubdp, 2, float64, VsrD(i), MSUB_FLGS, 0) +VSX_MADD(xvnmadddp, 2, float64, VsrD(i), NMADD_FLGS, 0) +VSX_MADD(xvnmsubdp, 2, float64, VsrD(i), NMSUB_FLGS, 0) -VSX_MADD(xvmaddsp, 4, float32, VsrW(i), MADD_FLGS, 0, 0) -VSX_MADD(xvmsubsp, 4, float32, VsrW(i), MSUB_FLGS, 0, 0) -VSX_MADD(xvnmaddsp, 4, float32, VsrW(i), NMADD_FLGS, 0, 0) -VSX_MADD(xvnmsubsp, 4, float32, VsrW(i), NMSUB_FLGS, 0, 0) +VSX_MADD(xvmaddsp, 4, float32, VsrW(i), MADD_FLGS, 0) +VSX_MADD(xvmsubsp, 4, float32, VsrW(i), MSUB_FLGS, 0) +VSX_MADD(xvnmaddsp, 4, float32, VsrW(i), NMADD_FLGS, 0) +VSX_MADD(xvnmsubsp, 4, float32, VsrW(i), NMSUB_FLGS, 0) /* - * VSX_SCALAR_CMP_DP - VSX scalar floating point compare double precision + * VSX_MADDQ - VSX floating point quad-precision muliply/add * op - instruction mnemonic + * maddflgs - flags for the float*muladd routine that control the + * various forms (madd, msub, nmadd, nmsub) + * ro - round to odd + */ +#define VSX_MADDQ(op, maddflgs, ro) \ +void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *s1, ppc_vsr_t *s2,\ + ppc_vsr_t *s3) \ +{ \ + ppc_vsr_t t = *xt; \ + \ + helper_reset_fpstatus(env); \ + \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + if (ro) { \ + tstat.float_rounding_mode = float_round_to_odd; \ + } \ + t.f128 = float128_muladd(s1->f128, s3->f128, s2->f128, maddflgs, &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + float_invalid_op_madd(env, tstat.float_exception_flags, \ + false, GETPC()); \ + } \ + \ + helper_compute_fprf_float128(env, t.f128); \ + *xt = t; \ + do_float_check_status(env, GETPC()); \ +} + +VSX_MADDQ(XSMADDQP, MADD_FLGS, 0) +VSX_MADDQ(XSMADDQPO, MADD_FLGS, 1) +VSX_MADDQ(XSMSUBQP, MSUB_FLGS, 0) +VSX_MADDQ(XSMSUBQPO, MSUB_FLGS, 1) +VSX_MADDQ(XSNMADDQP, NMADD_FLGS, 0) +VSX_MADDQ(XSNMADDQPO, NMADD_FLGS, 1) +VSX_MADDQ(XSNMSUBQP, NMSUB_FLGS, 0) +VSX_MADDQ(XSNMSUBQPO, NMSUB_FLGS, 0) + +/* + * VSX_SCALAR_CMP - VSX scalar floating point compare + * op - instruction mnemonic + * tp - type * cmp - comparison operation - * exp - expected result of comparison + * fld - vsr_t field * svxvc - set VXVC bit */ -#define VSX_SCALAR_CMP_DP(op, cmp, exp, svxvc) \ -void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ - ppc_vsr_t *xa, ppc_vsr_t *xb) \ +#define VSX_SCALAR_CMP(op, tp, cmp, fld, svxvc) \ + void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ + ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ - bool vxsnan_flag = false, vxvc_flag = false, vex_flag = false; \ + int flags; \ + bool r, vxvc; \ \ - if (float64_is_signaling_nan(xa->VsrD(0), &env->fp_status) || \ - float64_is_signaling_nan(xb->VsrD(0), &env->fp_status)) { \ - vxsnan_flag = true; \ - if (fpscr_ve == 0 && svxvc) { \ - vxvc_flag = true; \ + helper_reset_fpstatus(env); \ + \ + if (svxvc) { \ + r = tp##_##cmp(xb->fld, xa->fld, &env->fp_status); \ + } else { \ + r = tp##_##cmp##_quiet(xb->fld, xa->fld, &env->fp_status); \ + } \ + \ + flags = get_float_exception_flags(&env->fp_status); \ + if (unlikely(flags & float_flag_invalid)) { \ + vxvc = svxvc; \ + if (flags & float_flag_invalid_snan) { \ + float_invalid_op_vxsnan(env, GETPC()); \ + vxvc &= fpscr_ve == 0; \ } \ - } else if (svxvc) { \ - vxvc_flag = float64_is_quiet_nan(xa->VsrD(0), &env->fp_status) || \ - float64_is_quiet_nan(xb->VsrD(0), &env->fp_status); \ - } \ - if (vxsnan_flag) { \ - float_invalid_op_vxsnan(env, GETPC()); \ - } \ - if (vxvc_flag) { \ - float_invalid_op_vxvc(env, 0, GETPC()); \ - } \ - vex_flag = fpscr_ve && (vxvc_flag || vxsnan_flag); \ - \ - if (!vex_flag) { \ - if (float64_##cmp(xb->VsrD(0), xa->VsrD(0), \ - &env->fp_status) == exp) { \ - t.VsrD(0) = -1; \ - t.VsrD(1) = 0; \ - } else { \ - t.VsrD(0) = 0; \ - t.VsrD(1) = 0; \ + if (vxvc) { \ + float_invalid_op_vxvc(env, 0, GETPC()); \ } \ } \ - *xt = t; \ + \ + memset(xt, 0, sizeof(*xt)); \ + memset(&xt->fld, -r, sizeof(xt->fld)); \ do_float_check_status(env, GETPC()); \ } -VSX_SCALAR_CMP_DP(xscmpeqdp, eq, 1, 0) -VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1) -VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1) -VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0) +VSX_SCALAR_CMP(XSCMPEQDP, float64, eq, VsrD(0), 0) +VSX_SCALAR_CMP(XSCMPGEDP, float64, le, VsrD(0), 1) +VSX_SCALAR_CMP(XSCMPGTDP, float64, lt, VsrD(0), 1) +VSX_SCALAR_CMP(XSCMPEQQP, float128, eq, f128, 0) +VSX_SCALAR_CMP(XSCMPGEQP, float128, le, f128, 1) +VSX_SCALAR_CMP(XSCMPGTQP, float128, lt, f128, 1) void helper_xscmpexpdp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -2494,40 +2515,37 @@ VSX_MAX_MIN(xsmindp, minnum, 1, float64, VsrD(0)) VSX_MAX_MIN(xvmindp, minnum, 2, float64, VsrD(i)) VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) -#define VSX_MAX_MINC(name, max) \ +#define VSX_MAX_MINC(name, max, tp, fld) \ void helper_##name(CPUPPCState *env, \ ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ ppc_vsr_t t = { }; \ - bool vxsnan_flag = false, vex_flag = false; \ + bool first; \ \ - if (unlikely(float64_is_any_nan(xa->VsrD(0)) || \ - float64_is_any_nan(xb->VsrD(0)))) { \ - if (float64_is_signaling_nan(xa->VsrD(0), &env->fp_status) || \ - float64_is_signaling_nan(xb->VsrD(0), &env->fp_status)) { \ - vxsnan_flag = true; \ - } \ - t.VsrD(0) = xb->VsrD(0); \ - } else if ((max && \ - !float64_lt(xa->VsrD(0), xb->VsrD(0), &env->fp_status)) || \ - (!max && \ - float64_lt(xa->VsrD(0), xb->VsrD(0), &env->fp_status))) { \ - t.VsrD(0) = xa->VsrD(0); \ + helper_reset_fpstatus(env); \ + \ + if (max) { \ + first = tp##_le_quiet(xb->fld, xa->fld, &env->fp_status); \ } else { \ - t.VsrD(0) = xb->VsrD(0); \ + first = tp##_lt_quiet(xa->fld, xb->fld, &env->fp_status); \ } \ \ - vex_flag = fpscr_ve & vxsnan_flag; \ - if (vxsnan_flag) { \ - float_invalid_op_vxsnan(env, GETPC()); \ + if (first) { \ + t.fld = xa->fld; \ + } else { \ + t.fld = xb->fld; \ + if (env->fp_status.float_exception_flags & float_flag_invalid_snan) { \ + float_invalid_op_vxsnan(env, GETPC()); \ + } \ } \ - if (!vex_flag) { \ - *xt = t; \ - } \ -} \ + \ + *xt = t; \ +} -VSX_MAX_MINC(xsmaxcdp, 1); -VSX_MAX_MINC(xsmincdp, 0); +VSX_MAX_MINC(XSMAXCDP, true, float64, VsrD(0)); +VSX_MAX_MINC(XSMINCDP, false, float64, VsrD(0)); +VSX_MAX_MINC(XSMAXCQP, true, float128, f128); +VSX_MAX_MINC(XSMINCQP, false, float128, f128); #define VSX_MAX_MINJ(name, max) \ void helper_##name(CPUPPCState *env, \ @@ -2581,8 +2599,8 @@ void helper_##name(CPUPPCState *env, \ } \ } \ -VSX_MAX_MINJ(xsmaxjdp, 1); -VSX_MAX_MINJ(xsminjdp, 0); +VSX_MAX_MINJ(XSMAXJDP, 1); +VSX_MAX_MINJ(XSMINJDP, 0); /* * VSX_CMP - VSX floating point compare @@ -2673,11 +2691,35 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ do_float_check_status(env, GETPC()); \ } -VSX_CVT_FP_TO_FP(xscvdpsp, 1, float64, float32, VsrD(0), VsrW(0), 1) VSX_CVT_FP_TO_FP(xscvspdp, 1, float32, float64, VsrW(0), VsrD(0), 1) -VSX_CVT_FP_TO_FP(xvcvdpsp, 2, float64, float32, VsrD(i), VsrW(2 * i), 0) VSX_CVT_FP_TO_FP(xvcvspdp, 2, float32, float64, VsrW(2 * i), VsrD(i), 0) +#define VSX_CVT_FP_TO_FP2(op, nels, stp, ttp, sfprf) \ +void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ +{ \ + ppc_vsr_t t = { }; \ + int i; \ + \ + for (i = 0; i < nels; i++) { \ + t.VsrW(2 * i) = stp##_to_##ttp(xb->VsrD(i), &env->fp_status); \ + if (unlikely(stp##_is_signaling_nan(xb->VsrD(i), \ + &env->fp_status))) { \ + float_invalid_op_vxsnan(env, GETPC()); \ + t.VsrW(2 * i) = ttp##_snan_to_qnan(t.VsrW(2 * i)); \ + } \ + if (sfprf) { \ + helper_compute_fprf_##ttp(env, t.VsrW(2 * i)); \ + } \ + t.VsrW(2 * i + 1) = t.VsrW(2 * i); \ + } \ + \ + *xt = t; \ + do_float_check_status(env, GETPC()); \ +} + +VSX_CVT_FP_TO_FP2(xvcvdpsp, 2, float64, float32, 0) +VSX_CVT_FP_TO_FP2(xscvdpsp, 1, float64, float32, 1) + /* * VSX_CVT_FP_TO_FP_VECTOR - VSX floating point/floating point conversion * op - instruction mnemonic @@ -2751,6 +2793,26 @@ VSX_CVT_FP_TO_FP_HP(xscvhpdp, 1, float16, float64, VsrH(3), VsrD(0), 1) VSX_CVT_FP_TO_FP_HP(xvcvsphp, 4, float32, float16, VsrW(i), VsrH(2 * i + 1), 0) VSX_CVT_FP_TO_FP_HP(xvcvhpsp, 4, float16, float32, VsrH(2 * i + 1), VsrW(i), 0) +void helper_XVCVSPBF16(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) +{ + ppc_vsr_t t = { }; + int i, status; + + helper_reset_fpstatus(env); + + for (i = 0; i < 4; i++) { + t.VsrH(2 * i + 1) = float32_to_bfloat16(xb->VsrW(i), &env->fp_status); + } + + status = get_float_exception_flags(&env->fp_status); + if (unlikely(status & float_flag_invalid_snan)) { + float_invalid_op_vxsnan(env, GETPC()); + } + + *xt = t; + do_float_check_status(env, GETPC()); +} + void helper_XSCVQPDP(CPUPPCState *env, uint32_t ro, ppc_vsr_t *xt, ppc_vsr_t *xb) { @@ -2853,22 +2915,76 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ VSX_CVT_FP_TO_INT(xscvdpsxds, 1, float64, int64, VsrD(0), VsrD(0), \ 0x8000000000000000ULL) -VSX_CVT_FP_TO_INT(xscvdpsxws, 1, float64, int32, VsrD(0), VsrW(1), \ - 0x80000000U) VSX_CVT_FP_TO_INT(xscvdpuxds, 1, float64, uint64, VsrD(0), VsrD(0), 0ULL) -VSX_CVT_FP_TO_INT(xscvdpuxws, 1, float64, uint32, VsrD(0), VsrW(1), 0U) VSX_CVT_FP_TO_INT(xvcvdpsxds, 2, float64, int64, VsrD(i), VsrD(i), \ 0x8000000000000000ULL) -VSX_CVT_FP_TO_INT(xvcvdpsxws, 2, float64, int32, VsrD(i), VsrW(2 * i), \ - 0x80000000U) VSX_CVT_FP_TO_INT(xvcvdpuxds, 2, float64, uint64, VsrD(i), VsrD(i), 0ULL) -VSX_CVT_FP_TO_INT(xvcvdpuxws, 2, float64, uint32, VsrD(i), VsrW(2 * i), 0U) VSX_CVT_FP_TO_INT(xvcvspsxds, 2, float32, int64, VsrW(2 * i), VsrD(i), \ 0x8000000000000000ULL) VSX_CVT_FP_TO_INT(xvcvspsxws, 4, float32, int32, VsrW(i), VsrW(i), 0x80000000U) VSX_CVT_FP_TO_INT(xvcvspuxds, 2, float32, uint64, VsrW(2 * i), VsrD(i), 0ULL) VSX_CVT_FP_TO_INT(xvcvspuxws, 4, float32, uint32, VsrW(i), VsrW(i), 0U) +#define VSX_CVT_FP_TO_INT128(op, tp, rnan) \ +void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ +{ \ + ppc_vsr_t t; \ + int flags; \ + \ + helper_reset_fpstatus(env); \ + t.s128 = float128_to_##tp##_round_to_zero(xb->f128, &env->fp_status); \ + flags = get_float_exception_flags(&env->fp_status); \ + if (unlikely(flags & float_flag_invalid)) { \ + t.VsrD(0) = float_invalid_cvt(env, flags, t.VsrD(0), rnan, 0, GETPC());\ + t.VsrD(1) = -(t.VsrD(0) & 1); \ + } \ + \ + *xt = t; \ + do_float_check_status(env, GETPC()); \ +} + +VSX_CVT_FP_TO_INT128(XSCVQPUQZ, uint128, 0) +VSX_CVT_FP_TO_INT128(XSCVQPSQZ, int128, 0x8000000000000000ULL); + +/* + * Likewise, except that the result is duplicated into both subwords. + * Power ISA v3.1 has Programming Notes for these insns: + * Previous versions of the architecture allowed the contents of + * word 0 of the result register to be undefined. However, all + * processors that support this instruction write the result into + * words 0 and 1 (and words 2 and 3) of the result register, as + * is required by this version of the architecture. + */ +#define VSX_CVT_FP_TO_INT2(op, nels, stp, ttp, rnan) \ +void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ +{ \ + int all_flags = env->fp_status.float_exception_flags, flags; \ + ppc_vsr_t t = { }; \ + int i; \ + \ + for (i = 0; i < nels; i++) { \ + env->fp_status.float_exception_flags = 0; \ + t.VsrW(2 * i) = stp##_to_##ttp##_round_to_zero(xb->VsrD(i), \ + &env->fp_status); \ + flags = env->fp_status.float_exception_flags; \ + if (unlikely(flags & float_flag_invalid)) { \ + t.VsrW(2 * i) = float_invalid_cvt(env, flags, t.VsrW(2 * i), \ + rnan, 0, GETPC()); \ + } \ + t.VsrW(2 * i + 1) = t.VsrW(2 * i); \ + all_flags |= flags; \ + } \ + \ + *xt = t; \ + env->fp_status.float_exception_flags = all_flags; \ + do_float_check_status(env, GETPC()); \ +} + +VSX_CVT_FP_TO_INT2(xscvdpsxws, 1, float64, int32, 0x80000000U) +VSX_CVT_FP_TO_INT2(xscvdpuxws, 1, float64, uint32, 0U) +VSX_CVT_FP_TO_INT2(xvcvdpsxws, 2, float64, int32, 0x80000000U) +VSX_CVT_FP_TO_INT2(xvcvdpuxws, 2, float64, uint32, 0U) + /* * VSX_CVT_FP_TO_INT_VECTOR - VSX floating point to integer conversion * op - instruction mnemonic @@ -2942,11 +3058,39 @@ VSX_CVT_INT_TO_FP(xvcvsxddp, 2, int64, float64, VsrD(i), VsrD(i), 0, 0) VSX_CVT_INT_TO_FP(xvcvuxddp, 2, uint64, float64, VsrD(i), VsrD(i), 0, 0) VSX_CVT_INT_TO_FP(xvcvsxwdp, 2, int32, float64, VsrW(2 * i), VsrD(i), 0, 0) VSX_CVT_INT_TO_FP(xvcvuxwdp, 2, uint64, float64, VsrW(2 * i), VsrD(i), 0, 0) -VSX_CVT_INT_TO_FP(xvcvsxdsp, 2, int64, float32, VsrD(i), VsrW(2 * i), 0, 0) -VSX_CVT_INT_TO_FP(xvcvuxdsp, 2, uint64, float32, VsrD(i), VsrW(2 * i), 0, 0) VSX_CVT_INT_TO_FP(xvcvsxwsp, 4, int32, float32, VsrW(i), VsrW(i), 0, 0) VSX_CVT_INT_TO_FP(xvcvuxwsp, 4, uint32, float32, VsrW(i), VsrW(i), 0, 0) +#define VSX_CVT_INT_TO_FP2(op, stp, ttp) \ +void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ +{ \ + ppc_vsr_t t = { }; \ + int i; \ + \ + for (i = 0; i < 2; i++) { \ + t.VsrW(2 * i) = stp##_to_##ttp(xb->VsrD(i), &env->fp_status); \ + t.VsrW(2 * i + 1) = t.VsrW(2 * i); \ + } \ + \ + *xt = t; \ + do_float_check_status(env, GETPC()); \ +} + +VSX_CVT_INT_TO_FP2(xvcvsxdsp, int64, float32) +VSX_CVT_INT_TO_FP2(xvcvuxdsp, uint64, float32) + +#define VSX_CVT_INT128_TO_FP(op, tp) \ +void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb)\ +{ \ + helper_reset_fpstatus(env); \ + xt->f128 = tp##_to_float128(xb->s128, &env->fp_status); \ + helper_compute_fprf_float128(env, xt->f128); \ + do_float_check_status(env, GETPC()); \ +} + +VSX_CVT_INT128_TO_FP(XSCVUQQP, uint128); +VSX_CVT_INT128_TO_FP(XSCVSQQP, int128); + /* * VSX_CVT_INT_TO_FP_VECTOR - VSX integer to floating point conversion * op - instruction mnemonic @@ -3055,27 +3199,6 @@ uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb) return xt; } -#define VSX_XXPERM(op, indexed) \ -void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ - ppc_vsr_t *xa, ppc_vsr_t *pcv) \ -{ \ - ppc_vsr_t t = *xt; \ - int i, idx; \ - \ - for (i = 0; i < 16; i++) { \ - idx = pcv->VsrB(i) & 0x1F; \ - if (indexed) { \ - idx = 31 - idx; \ - } \ - t.VsrB(i) = (idx <= 15) ? xa->VsrB(idx) \ - : xt->VsrB(idx - 16); \ - } \ - *xt = t; \ -} - -VSX_XXPERM(xxperm, 0) -VSX_XXPERM(xxpermr, 1) - void helper_xvxsigsp(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) { ppc_vsr_t t = { }; diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 105c2f7dd1..1252429a2a 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -87,9 +87,9 @@ static int ppc_gdb_register_len(int n) /* * We need to present the registers to gdb in the "current" memory * ordering. For user-only mode we get this for free; - * TARGET_WORDS_BIGENDIAN is set to the proper ordering for the + * TARGET_BIG_ENDIAN is set to the proper ordering for the * binary, and cannot be changed. For system mode, - * TARGET_WORDS_BIGENDIAN is always set, and we must check the current + * TARGET_BIG_ENDIAN is always set, and we must check the current * mode of the chip to see if we're running in little-endian. */ void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index ab008c9d4e..aa6773c4a5 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -127,9 +127,11 @@ DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * +#define dh_typecode_avr dh_typecode_ptr #define dh_alias_vsr ptr #define dh_ctype_vsr ppc_vsr_t * +#define dh_typecode_vsr dh_typecode_ptr DEF_HELPER_3(vavgub, void, avr, avr, avr) DEF_HELPER_3(vavguh, void, avr, avr, avr) @@ -140,46 +142,13 @@ DEF_HELPER_3(vabsduw, void, avr, avr, avr) DEF_HELPER_3(vavgsb, void, avr, avr, avr) DEF_HELPER_3(vavgsh, void, avr, avr, avr) DEF_HELPER_3(vavgsw, void, avr, avr, avr) -DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpequd, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpneb, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpneh, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnew, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnezb, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnezh, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnezw, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtub, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtuh, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtuw, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtud, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsb, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsh, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsw, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsd, void, env, avr, avr, avr) DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpbfp, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpequb_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpequh_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpequw_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpequd_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpneb_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpneh_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnew_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnezb_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnezh_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpnezw_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtub_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtuh_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtuw_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtud_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsb_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsh_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsw_dot, void, env, avr, avr, avr) -DEF_HELPER_4(vcmpgtsd_dot, void, env, avr, avr, avr) +DEF_HELPER_FLAGS_4(VCMPNEZB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VCMPNEZH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VCMPNEZW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) DEF_HELPER_4(vcmpeqfp_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgefp_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtfp_dot, void, env, avr, avr, avr) @@ -190,22 +159,18 @@ DEF_HELPER_3(vmrglw, void, avr, avr, avr) DEF_HELPER_3(vmrghb, void, avr, avr, avr) DEF_HELPER_3(vmrghh, void, avr, avr, avr) DEF_HELPER_3(vmrghw, void, avr, avr, avr) -DEF_HELPER_3(vmulesb, void, avr, avr, avr) -DEF_HELPER_3(vmulesh, void, avr, avr, avr) -DEF_HELPER_3(vmulesw, void, avr, avr, avr) -DEF_HELPER_3(vmuleub, void, avr, avr, avr) -DEF_HELPER_3(vmuleuh, void, avr, avr, avr) -DEF_HELPER_3(vmuleuw, void, avr, avr, avr) -DEF_HELPER_3(vmulosb, void, avr, avr, avr) -DEF_HELPER_3(vmulosh, void, avr, avr, avr) -DEF_HELPER_3(vmulosw, void, avr, avr, avr) -DEF_HELPER_3(vmuloub, void, avr, avr, avr) -DEF_HELPER_3(vmulouh, void, avr, avr, avr) -DEF_HELPER_3(vmulouw, void, avr, avr, avr) -DEF_HELPER_3(vmulhsw, void, avr, avr, avr) -DEF_HELPER_3(vmulhuw, void, avr, avr, avr) -DEF_HELPER_3(vmulhsd, void, avr, avr, avr) -DEF_HELPER_3(vmulhud, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULESB, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULESH, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULESW, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULEUB, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULEUH, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULEUW, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULOSB, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULOSH, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULOSW, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULOUB, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULOUH, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMULOUW, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_3(vslo, void, avr, avr, avr) DEF_HELPER_3(vsro, void, avr, avr, avr) DEF_HELPER_3(vsrv, void, avr, avr, avr) @@ -244,11 +209,10 @@ DEF_HELPER_4(VINSBLX, void, env, avr, i64, tl) DEF_HELPER_4(VINSHLX, void, env, avr, i64, tl) DEF_HELPER_4(VINSWLX, void, env, avr, i64, tl) DEF_HELPER_4(VINSDLX, void, env, avr, i64, tl) -DEF_HELPER_2(vextsb2w, void, avr, avr) -DEF_HELPER_2(vextsh2w, void, avr, avr) -DEF_HELPER_2(vextsb2d, void, avr, avr) -DEF_HELPER_2(vextsh2d, void, avr, avr) -DEF_HELPER_2(vextsw2d, void, avr, avr) +DEF_HELPER_FLAGS_2(VSTRIBL, TCG_CALL_NO_RWG, i32, avr, avr) +DEF_HELPER_FLAGS_2(VSTRIBR, TCG_CALL_NO_RWG, i32, avr, avr) +DEF_HELPER_FLAGS_2(VSTRIHL, TCG_CALL_NO_RWG, i32, avr, avr) +DEF_HELPER_FLAGS_2(VSTRIHR, TCG_CALL_NO_RWG, i32, avr, avr) DEF_HELPER_2(vnegw, void, avr, avr) DEF_HELPER_2(vnegd, void, avr, avr) DEF_HELPER_2(vupkhpx, void, avr, avr) @@ -261,9 +225,8 @@ DEF_HELPER_2(vupklsh, void, avr, avr) DEF_HELPER_2(vupklsw, void, avr, avr) DEF_HELPER_5(vmsumubm, void, env, avr, avr, avr, avr) DEF_HELPER_5(vmsummbm, void, env, avr, avr, avr, avr) -DEF_HELPER_5(vsel, void, env, avr, avr, avr, avr) -DEF_HELPER_5(vperm, void, env, avr, avr, avr, avr) -DEF_HELPER_5(vpermr, void, env, avr, avr, avr, avr) +DEF_HELPER_FLAGS_4(VPERM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_4(VPERMR, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) DEF_HELPER_4(vpkshss, void, env, avr, avr, avr) DEF_HELPER_4(vpkshus, void, env, avr, avr, avr) DEF_HELPER_4(vpkswss, void, env, avr, avr, avr) @@ -309,10 +272,10 @@ DEF_HELPER_4(vmaxfp, void, env, avr, avr, avr) DEF_HELPER_4(vminfp, void, env, avr, avr, avr) DEF_HELPER_3(vrefp, void, env, avr, avr) DEF_HELPER_3(vrsqrtefp, void, env, avr, avr) -DEF_HELPER_3(vrlwmi, void, avr, avr, avr) -DEF_HELPER_3(vrldmi, void, avr, avr, avr) -DEF_HELPER_3(vrldnm, void, avr, avr, avr) -DEF_HELPER_3(vrlwnm, void, avr, avr, avr) +DEF_HELPER_FLAGS_4(VRLWMI, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VRLDMI, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VRLDNM, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VRLWNM, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) DEF_HELPER_5(vmaddfp, void, env, avr, avr, avr, avr) DEF_HELPER_5(vnmsubfp, void, env, avr, avr, avr, avr) DEF_HELPER_3(vexptefp, void, env, avr, avr) @@ -392,14 +355,16 @@ DEF_HELPER_3(xssqrtdp, void, env, vsr, vsr) DEF_HELPER_3(xsrsqrtedp, void, env, vsr, vsr) DEF_HELPER_4(xstdivdp, void, env, i32, vsr, vsr) DEF_HELPER_3(xstsqrtdp, void, env, i32, vsr) -DEF_HELPER_5(xsmadddp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_5(xsmsubdp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_5(xsnmadddp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_5(xsnmsubdp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xscmpeqdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xscmpgtdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xscmpgedp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xscmpnedp, void, env, vsr, vsr, vsr) +DEF_HELPER_5(XSMADDDP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSMSUBDP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMADDDP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMSUBDP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_4(XSCMPEQDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSCMPGTDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSCMPGEDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSCMPEQQP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSCMPGTQP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSCMPGEQP, void, env, vsr, vsr, vsr) DEF_HELPER_4(xscmpexpdp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpexpqp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpodp, void, env, i32, vsr, vsr) @@ -408,10 +373,12 @@ DEF_HELPER_4(xscmpoqp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpuqp, void, env, i32, vsr, vsr) DEF_HELPER_4(xsmaxdp, void, env, vsr, vsr, vsr) DEF_HELPER_4(xsmindp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmaxcdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmincdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmaxjdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsminjdp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMAXCDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMINCDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMAXJDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMINJDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMAXCQP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMINCQP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xscvdphp, void, env, vsr, vsr) DEF_HELPER_4(xscvdpqp, void, env, i32, vsr, vsr) DEF_HELPER_3(xscvdpsp, void, env, vsr, vsr) @@ -421,6 +388,10 @@ DEF_HELPER_4(xscvqpsdz, void, env, i32, vsr, vsr) DEF_HELPER_4(xscvqpswz, void, env, i32, vsr, vsr) DEF_HELPER_4(xscvqpudz, void, env, i32, vsr, vsr) DEF_HELPER_4(xscvqpuwz, void, env, i32, vsr, vsr) +DEF_HELPER_3(XSCVQPUQZ, void, env, vsr, vsr) +DEF_HELPER_3(XSCVQPSQZ, void, env, vsr, vsr) +DEF_HELPER_3(XSCVUQQP, void, env, vsr, vsr) +DEF_HELPER_3(XSCVSQQP, void, env, vsr, vsr) DEF_HELPER_3(xscvhpdp, void, env, vsr, vsr) DEF_HELPER_4(xscvsdqp, void, env, i32, vsr, vsr) DEF_HELPER_3(xscvspdp, void, env, vsr, vsr) @@ -455,10 +426,19 @@ DEF_HELPER_3(xsresp, void, env, vsr, vsr) DEF_HELPER_2(xsrsp, i64, env, i64) DEF_HELPER_3(xssqrtsp, void, env, vsr, vsr) DEF_HELPER_3(xsrsqrtesp, void, env, vsr, vsr) -DEF_HELPER_5(xsmaddsp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_5(xsmsubsp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_5(xsnmaddsp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_5(xsnmsubsp, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSMADDSP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSMSUBSP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMADDSP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMSUBSP, void, env, vsr, vsr, vsr, vsr) + +DEF_HELPER_5(XSMADDQP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSMADDQPO, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSMSUBQP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSMSUBQPO, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMADDQP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMADDQPO, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMSUBQP, void, env, vsr, vsr, vsr, vsr) +DEF_HELPER_5(XSNMSUBQPO, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_4(xvadddp, void, env, vsr, vsr, vsr) DEF_HELPER_4(xvsubdp, void, env, vsr, vsr, vsr) @@ -516,6 +496,7 @@ DEF_HELPER_FLAGS_4(xvcmpnesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_3(xvcvspdp, void, env, vsr, vsr) DEF_HELPER_3(xvcvsphp, void, env, vsr, vsr) DEF_HELPER_3(xvcvhpsp, void, env, vsr, vsr) +DEF_HELPER_3(XVCVSPBF16, void, env, vsr, vsr) DEF_HELPER_3(xvcvspsxds, void, env, vsr, vsr) DEF_HELPER_3(xvcvspsxws, void, env, vsr, vsr) DEF_HELPER_3(xvcvspuxds, void, env, vsr, vsr) @@ -531,11 +512,27 @@ DEF_HELPER_3(xvrspic, void, env, vsr, vsr) DEF_HELPER_3(xvrspim, void, env, vsr, vsr) DEF_HELPER_3(xvrspip, void, env, vsr, vsr) DEF_HELPER_3(xvrspiz, void, env, vsr, vsr) -DEF_HELPER_4(xxperm, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xxpermr, void, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_2(XXGENPCVBM_be_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVBM_be_comp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVBM_le_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVBM_le_comp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVHM_be_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVHM_be_comp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVHM_le_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVHM_le_comp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVWM_be_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVWM_be_comp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVWM_le_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVWM_le_comp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVDM_be_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVDM_be_comp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVDM_le_exp, TCG_CALL_NO_RWG, void, vsr, avr) +DEF_HELPER_FLAGS_2(XXGENPCVDM_le_comp, TCG_CALL_NO_RWG, void, vsr, avr) DEF_HELPER_4(xxextractuw, void, env, vsr, vsr, i32) +DEF_HELPER_FLAGS_5(XXPERMX, TCG_CALL_NO_RWG, void, vsr, vsr, vsr, vsr, tl) DEF_HELPER_4(xxinsertw, void, env, vsr, vsr, i32) DEF_HELPER_3(xvxsigsp, void, env, vsr, vsr) +DEF_HELPER_FLAGS_5(XXEVAL, TCG_CALL_NO_RWG, void, vsr, vsr, vsr, vsr, i32) DEF_HELPER_5(XXBLENDVB, void, vsr, vsr, vsr, vsr, i32) DEF_HELPER_5(XXBLENDVH, void, vsr, vsr, vsr, vsr, i32) DEF_HELPER_5(XXBLENDVW, void, vsr, vsr, vsr, vsr, i32) @@ -708,6 +705,7 @@ DEF_HELPER_3(store_dbatu, void, env, i32, tl) #define dh_alias_fprp ptr #define dh_ctype_fprp ppc_fprp_t * +#define dh_typecode_fprp dh_typecode_ptr DEF_HELPER_4(DADD, void, env, fprp, fprp, fprp) DEF_HELPER_4(DADDQ, void, env, fprp, fprp, fprp) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 2a9c91a423..39372fe673 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -51,12 +51,27 @@ &VA vrt vra vrb rc @VA ...... vrt:5 vra:5 vrb:5 rc:5 ...... &VA +&VC vrt vra vrb rc:bool +@VC ...... vrt:5 vra:5 vrb:5 rc:1 .......... &VC + &VN vrt vra vrb sh @VN ...... vrt:5 vra:5 vrb:5 .. sh:3 ...... &VN &VX vrt vra vrb @VX ...... vrt:5 vra:5 vrb:5 .......... . &VX +&VX_bf bf vra vrb +@VX_bf ...... bf:3 .. vra:5 vrb:5 ........... &VX_bf + +&VX_mp rt mp:bool vrb +@VX_mp ...... rt:5 .... mp:1 vrb:5 ........... &VX_mp + +&VX_n rt vrb n +@VX_n ...... rt:5 .. n:3 vrb:5 ........... &VX_n + +&VX_tb_rc vrt vrb rc:bool +@VX_tb_rc ...... vrt:5 ..... vrb:5 rc:1 .......... &VX_tb_rc + &VX_uim4 vrt uim vrb @VX_uim4 ...... vrt:5 . uim:4 vrb:5 ........... &VX_uim4 @@ -76,6 +91,9 @@ @X_tp_a_bp_rc ...... ....0 ra:5 ....0 .......... rc:1 &X_rc rt=%x_frtp rb=%x_frbp +&X_tb rt rb +@X_tb ...... rt:5 ..... rb:5 .......... . &X_tb + &X_tb_rc rt rb rc:bool @X_tb_rc ...... rt:5 ..... rb:5 .......... rc:1 &X_tb_rc @@ -104,6 +122,9 @@ @X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl %x_xt 0:1 21:5 +&X_imm5 xt imm:uint8_t vrb +@X_imm5 ...... ..... imm:5 vrb:5 .......... . &X_imm5 xt=%x_xt + &X_imm8 xt imm:uint8_t @X_imm8 ...... ..... .. imm:8 .......... . &X_imm8 xt=%x_xt @@ -133,12 +154,25 @@ %xx_xt 0:1 21:5 %xx_xb 1:1 11:5 %xx_xa 2:1 16:5 -&XX2 xt xb uim:uint8_t -@XX2 ...... ..... ... uim:2 ..... ......... .. &XX2 xt=%xx_xt xb=%xx_xb +%xx_xc 3:1 6:5 +&XX2 xt xb +@XX2 ...... ..... ..... ..... ......... .. &XX2 xt=%xx_xt xb=%xx_xb + +&XX2_uim2 xt xb uim:uint8_t +@XX2_uim2 ...... ..... ... uim:2 ..... ......... .. &XX2_uim2 xt=%xx_xt xb=%xx_xb + +&XX2_bf_xb bf xb +@XX2_bf_xb ...... bf:3 .. ..... ..... ......... . . &XX2_bf_xb xb=%xx_xb &XX3 xt xa xb @XX3 ...... ..... ..... ..... ........ ... &XX3 xt=%xx_xt xa=%xx_xa xb=%xx_xb +&XX3_dm xt xa xb dm +@XX3_dm ...... ..... ..... ..... . dm:2 ..... ... &XX3_dm xt=%xx_xt xa=%xx_xa xb=%xx_xb + +&XX4 xt xa xb xc +@XX4 ...... ..... ..... ..... ..... .. .... &XX4 xt=%xx_xt xa=%xx_xa xb=%xx_xb xc=%xx_xc + &Z22_bf_fra bf fra dm @Z22_bf_fra ...... bf:3 .. fra:5 dm:6 ......... . &Z22_bf_fra @@ -373,8 +407,41 @@ DSCLIQ 111111 ..... ..... ...... 001000010 . @Z22_tap_sh_rc DSCRI 111011 ..... ..... ...... 001100010 . @Z22_ta_sh_rc DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc +## Vector Integer Instructions + +VCMPEQUB 000100 ..... ..... ..... . 0000000110 @VC +VCMPEQUH 000100 ..... ..... ..... . 0001000110 @VC +VCMPEQUW 000100 ..... ..... ..... . 0010000110 @VC +VCMPEQUD 000100 ..... ..... ..... . 0011000111 @VC +VCMPEQUQ 000100 ..... ..... ..... . 0111000111 @VC + +VCMPGTSB 000100 ..... ..... ..... . 1100000110 @VC +VCMPGTSH 000100 ..... ..... ..... . 1101000110 @VC +VCMPGTSW 000100 ..... ..... ..... . 1110000110 @VC +VCMPGTSD 000100 ..... ..... ..... . 1111000111 @VC +VCMPGTSQ 000100 ..... ..... ..... . 1110000111 @VC + +VCMPGTUB 000100 ..... ..... ..... . 1000000110 @VC +VCMPGTUH 000100 ..... ..... ..... . 1001000110 @VC +VCMPGTUW 000100 ..... ..... ..... . 1010000110 @VC +VCMPGTUD 000100 ..... ..... ..... . 1011000111 @VC +VCMPGTUQ 000100 ..... ..... ..... . 1010000111 @VC + +VCMPNEB 000100 ..... ..... ..... . 0000000111 @VC +VCMPNEH 000100 ..... ..... ..... . 0001000111 @VC +VCMPNEW 000100 ..... ..... ..... . 0010000111 @VC + +VCMPNEZB 000100 ..... ..... ..... . 0100000111 @VC +VCMPNEZH 000100 ..... ..... ..... . 0101000111 @VC +VCMPNEZW 000100 ..... ..... ..... . 0110000111 @VC + +VCMPSQ 000100 ... -- ..... ..... 00101000001 @VX_bf +VCMPUQ 000100 ... -- ..... ..... 00100000001 @VX_bf + ## Vector Bit Manipulation Instruction +VGNB 000100 ..... -- ... ..... 10011001100 @VX_n + VCFUGED 000100 ..... ..... ..... 10101001101 @VX VCLZDM 000100 ..... ..... ..... 11110000100 @VX VCTZDM 000100 ..... ..... ..... 11111000100 @VX @@ -419,6 +486,54 @@ VINSWVRX 000100 ..... ..... ..... 00110001111 @VX VSLDBI 000100 ..... ..... ..... 00 ... 010110 @VN VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN +VPERM 000100 ..... ..... ..... ..... 101011 @VA +VPERMR 000100 ..... ..... ..... ..... 111011 @VA + +VSEL 000100 ..... ..... ..... ..... 101010 @VA + +## Vector Integer Shift Instruction + +VSLB 000100 ..... ..... ..... 00100000100 @VX +VSLH 000100 ..... ..... ..... 00101000100 @VX +VSLW 000100 ..... ..... ..... 00110000100 @VX +VSLD 000100 ..... ..... ..... 10111000100 @VX +VSLQ 000100 ..... ..... ..... 00100000101 @VX + +VSRB 000100 ..... ..... ..... 01000000100 @VX +VSRH 000100 ..... ..... ..... 01001000100 @VX +VSRW 000100 ..... ..... ..... 01010000100 @VX +VSRD 000100 ..... ..... ..... 11011000100 @VX +VSRQ 000100 ..... ..... ..... 01000000101 @VX + +VSRAB 000100 ..... ..... ..... 01100000100 @VX +VSRAH 000100 ..... ..... ..... 01101000100 @VX +VSRAW 000100 ..... ..... ..... 01110000100 @VX +VSRAD 000100 ..... ..... ..... 01111000100 @VX +VSRAQ 000100 ..... ..... ..... 01100000101 @VX + +VRLB 000100 ..... ..... ..... 00000000100 @VX +VRLH 000100 ..... ..... ..... 00001000100 @VX +VRLW 000100 ..... ..... ..... 00010000100 @VX +VRLD 000100 ..... ..... ..... 00011000100 @VX +VRLQ 000100 ..... ..... ..... 00000000101 @VX + +VRLWMI 000100 ..... ..... ..... 00010000101 @VX +VRLDMI 000100 ..... ..... ..... 00011000101 @VX +VRLQMI 000100 ..... ..... ..... 00001000101 @VX + +VRLWNM 000100 ..... ..... ..... 00110000101 @VX +VRLDNM 000100 ..... ..... ..... 00111000101 @VX +VRLQNM 000100 ..... ..... ..... 00101000101 @VX + +## Vector Integer Arithmetic Instructions + +VEXTSB2W 000100 ..... 10000 ..... 11000000010 @VX_tb +VEXTSH2W 000100 ..... 10001 ..... 11000000010 @VX_tb +VEXTSB2D 000100 ..... 11000 ..... 11000000010 @VX_tb +VEXTSH2D 000100 ..... 11001 ..... 11000000010 @VX_tb +VEXTSW2D 000100 ..... 11010 ..... 11000000010 @VX_tb +VEXTSD2Q 000100 ..... 11011 ..... 11000000010 @VX_tb + ## Vector Mask Manipulation Instructions MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb @@ -440,8 +555,60 @@ VEXTRACTWM 000100 ..... 01010 ..... 11001000010 @VX_tb VEXTRACTDM 000100 ..... 01011 ..... 11001000010 @VX_tb VEXTRACTQM 000100 ..... 01100 ..... 11001000010 @VX_tb +VCNTMBB 000100 ..... 1100 . ..... 11001000010 @VX_mp +VCNTMBH 000100 ..... 1101 . ..... 11001000010 @VX_mp +VCNTMBW 000100 ..... 1110 . ..... 11001000010 @VX_mp +VCNTMBD 000100 ..... 1111 . ..... 11001000010 @VX_mp + +## Vector Multiply Instruction + +VMULESB 000100 ..... ..... ..... 01100001000 @VX +VMULOSB 000100 ..... ..... ..... 00100001000 @VX +VMULEUB 000100 ..... ..... ..... 01000001000 @VX +VMULOUB 000100 ..... ..... ..... 00000001000 @VX + +VMULESH 000100 ..... ..... ..... 01101001000 @VX +VMULOSH 000100 ..... ..... ..... 00101001000 @VX +VMULEUH 000100 ..... ..... ..... 01001001000 @VX +VMULOUH 000100 ..... ..... ..... 00001001000 @VX + +VMULESW 000100 ..... ..... ..... 01110001000 @VX +VMULOSW 000100 ..... ..... ..... 00110001000 @VX +VMULEUW 000100 ..... ..... ..... 01010001000 @VX +VMULOUW 000100 ..... ..... ..... 00010001000 @VX + +VMULESD 000100 ..... ..... ..... 01111001000 @VX +VMULOSD 000100 ..... ..... ..... 00111001000 @VX +VMULEUD 000100 ..... ..... ..... 01011001000 @VX +VMULOUD 000100 ..... ..... ..... 00011001000 @VX + +VMULHSW 000100 ..... ..... ..... 01110001001 @VX +VMULHUW 000100 ..... ..... ..... 01010001001 @VX +VMULHSD 000100 ..... ..... ..... 01111001001 @VX +VMULHUD 000100 ..... ..... ..... 01011001001 @VX +VMULLD 000100 ..... ..... ..... 00111001001 @VX + +## Vector Multiply-Sum Instructions + +VMSUMCUD 000100 ..... ..... ..... ..... 010111 @VA +VMSUMUDM 000100 ..... ..... ..... ..... 100011 @VA + +## Vector String Instructions + +VSTRIBL 000100 ..... 00000 ..... . 0000001101 @VX_tb_rc +VSTRIBR 000100 ..... 00001 ..... . 0000001101 @VX_tb_rc +VSTRIHL 000100 ..... 00010 ..... . 0000001101 @VX_tb_rc +VSTRIHR 000100 ..... 00011 ..... . 0000001101 @VX_tb_rc + +VCLRLB 000100 ..... ..... ..... 00110001101 @VX +VCLRRB 000100 ..... ..... ..... 00111001101 @VX + # VSX Load/Store Instructions +LXSD 111001 ..... ..... .............. 10 @DS +STXSD 111101 ..... ..... .............. 10 @DS +LXSSP 111001 ..... ..... .............. 11 @DS +STXSSP 111101 ..... ..... .............. 11 @DS LXV 111101 ..... ..... ............ . 001 @DQ_TSX STXV 111101 ..... ..... ............ . 101 @DQ_TSX LXVP 000110 ..... ..... ............ 0000 @DQ_TSXP @@ -450,11 +617,60 @@ LXVX 011111 ..... ..... ..... 0100 - 01100 . @X_TSX STXVX 011111 ..... ..... ..... 0110001100 . @X_TSX LXVPX 011111 ..... ..... ..... 0101001101 - @X_TSXP STXVPX 011111 ..... ..... ..... 0111001101 - @X_TSXP +LXVRBX 011111 ..... ..... ..... 0000001101 . @X_TSX +LXVRHX 011111 ..... ..... ..... 0000101101 . @X_TSX +LXVRWX 011111 ..... ..... ..... 0001001101 . @X_TSX +LXVRDX 011111 ..... ..... ..... 0001101101 . @X_TSX +STXVRBX 011111 ..... ..... ..... 0010001101 . @X_TSX +STXVRHX 011111 ..... ..... ..... 0010101101 . @X_TSX +STXVRWX 011111 ..... ..... ..... 0011001101 . @X_TSX +STXVRDX 011111 ..... ..... ..... 0011101101 . @X_TSX + +## VSX Scalar Multiply-Add Instructions + +XSMADDADP 111100 ..... ..... ..... 00100001 . . . @XX3 +XSMADDMDP 111100 ..... ..... ..... 00101001 . . . @XX3 +XSMADDASP 111100 ..... ..... ..... 00000001 . . . @XX3 +XSMADDMSP 111100 ..... ..... ..... 00001001 . . . @XX3 +XSMADDQP 111111 ..... ..... ..... 0110000100 . @X_rc + +XSMSUBADP 111100 ..... ..... ..... 00110001 . . . @XX3 +XSMSUBMDP 111100 ..... ..... ..... 00111001 . . . @XX3 +XSMSUBASP 111100 ..... ..... ..... 00010001 . . . @XX3 +XSMSUBMSP 111100 ..... ..... ..... 00011001 . . . @XX3 +XSMSUBQP 111111 ..... ..... ..... 0110100100 . @X_rc + +XSNMADDASP 111100 ..... ..... ..... 10000001 . . . @XX3 +XSNMADDMSP 111100 ..... ..... ..... 10001001 . . . @XX3 +XSNMADDADP 111100 ..... ..... ..... 10100001 . . . @XX3 +XSNMADDMDP 111100 ..... ..... ..... 10101001 . . . @XX3 +XSNMADDQP 111111 ..... ..... ..... 0111000100 . @X_rc + +XSNMSUBASP 111100 ..... ..... ..... 10010001 . . . @XX3 +XSNMSUBMSP 111100 ..... ..... ..... 10011001 . . . @XX3 +XSNMSUBADP 111100 ..... ..... ..... 10110001 . . . @XX3 +XSNMSUBMDP 111100 ..... ..... ..... 10111001 . . . @XX3 +XSNMSUBQP 111111 ..... ..... ..... 0111100100 . @X_rc ## VSX splat instruction XXSPLTIB 111100 ..... 00 ........ 0101101000 . @X_imm8 -XXSPLTW 111100 ..... ---.. ..... 010100100 . . @XX2 +XXSPLTW 111100 ..... ---.. ..... 010100100 . . @XX2_uim2 + +## VSX Permute Instructions + +XXPERM 111100 ..... ..... ..... 00011010 ... @XX3 +XXPERMR 111100 ..... ..... ..... 00111010 ... @XX3 +XXPERMDI 111100 ..... ..... ..... 0 .. 01010 ... @XX3_dm + +XXSEL 111100 ..... ..... ..... ..... 11 .... @XX4 + +## VSX Vector Generate PCV + +XXGENPCVBM 111100 ..... ..... ..... 1110010100 . @X_imm5 +XXGENPCVHM 111100 ..... ..... ..... 1110010101 . @X_imm5 +XXGENPCVWM 111100 ..... ..... ..... 1110110100 . @X_imm5 +XXGENPCVDM 111100 ..... ..... ..... 1110110101 . @X_imm5 ## VSX Vector Load Special Value Instruction @@ -466,10 +682,29 @@ XSMAXCDP 111100 ..... ..... ..... 10000000 ... @XX3 XSMINCDP 111100 ..... ..... ..... 10001000 ... @XX3 XSMAXJDP 111100 ..... ..... ..... 10010000 ... @XX3 XSMINJDP 111100 ..... ..... ..... 10011000 ... @XX3 +XSMAXCQP 111111 ..... ..... ..... 1010100100 - @X +XSMINCQP 111111 ..... ..... ..... 1011100100 - @X + +XSCMPEQDP 111100 ..... ..... ..... 00000011 ... @XX3 +XSCMPGEDP 111100 ..... ..... ..... 00010011 ... @XX3 +XSCMPGTDP 111100 ..... ..... ..... 00001011 ... @XX3 +XSCMPEQQP 111111 ..... ..... ..... 0001000100 - @X +XSCMPGEQP 111111 ..... ..... ..... 0011000100 - @X +XSCMPGTQP 111111 ..... ..... ..... 0011100100 - @X ## VSX Binary Floating-Point Convert Instructions XSCVQPDP 111111 ..... 10100 ..... 1101000100 . @X_tb_rc +XSCVQPUQZ 111111 ..... 00000 ..... 1101000100 - @X_tb +XSCVQPSQZ 111111 ..... 01000 ..... 1101000100 - @X_tb +XSCVUQQP 111111 ..... 00011 ..... 1101000100 - @X_tb +XSCVSQQP 111111 ..... 01011 ..... 1101000100 - @X_tb +XVCVBF16SPN 111100 ..... 10000 ..... 111011011 .. @XX2 +XVCVSPBF16 111100 ..... 10001 ..... 111011011 .. @XX2 + +## VSX Vector Test Least-Significant Bit by Byte Instruction + +XVTLSBB 111100 ... -- 00010 ..... 111011011 . - @XX2_bf_xb ### rfebb &XL_s s:uint8_t diff --git a/target/ppc/insn64.decode b/target/ppc/insn64.decode index 39e610913d..691e8fe6c0 100644 --- a/target/ppc/insn64.decode +++ b/target/ppc/insn64.decode @@ -32,6 +32,10 @@ ...... ..... ra:5 ................ \ &PLS_D si=%pls_si rt=%rt_tsxp +@8LS_D ...... .. . .. r:1 .. .................. \ + ...... rt:5 ra:5 ................ \ + &PLS_D si=%pls_si + # Format 8RR:D %8rr_si 32:s16 0:16 %8rr_xt 16:1 21:5 @@ -44,15 +48,25 @@ ...... ..... .... . ................ \ &8RR_D si=%8rr_si xt=%8rr_xt -# Format XX4 -&XX4 xt xa xb xc -%xx4_xt 0:1 21:5 -%xx4_xa 2:1 16:5 -%xx4_xb 1:1 11:5 -%xx4_xc 3:1 6:5 -@XX4 ........ ........ ........ ........ \ +# Format 8RR:XX4 +%8rr_xx_xt 0:1 21:5 +%8rr_xx_xa 2:1 16:5 +%8rr_xx_xb 1:1 11:5 +%8rr_xx_xc 3:1 6:5 +&8RR_XX4 xt xa xb xc +@8RR_XX4 ........ ........ ........ ........ \ ...... ..... ..... ..... ..... .. .... \ - &XX4 xt=%xx4_xt xa=%xx4_xa xb=%xx4_xb xc=%xx4_xc + &8RR_XX4 xt=%8rr_xx_xt xa=%8rr_xx_xa xb=%8rr_xx_xb xc=%8rr_xx_xc + +&8RR_XX4_imm xt xa xb xc imm +@8RR_XX4_imm ........ ........ ........ imm:8 \ + ...... ..... ..... ..... ..... .. .... \ + &8RR_XX4_imm xt=%8rr_xx_xt xa=%8rr_xx_xa xb=%8rr_xx_xb xc=%8rr_xx_xc + +&8RR_XX4_uim3 xt xa xb xc uim3 +@8RR_XX4_uim3 ...... .. .... .. ............... uim3:3 \ + ...... ..... ..... ..... ..... .. .... \ + &8RR_XX4_uim3 xt=%8rr_xx_xt xa=%8rr_xx_xa xb=%8rr_xx_xb xc=%8rr_xx_xc ### Fixed-Point Load Instructions @@ -170,6 +184,18 @@ PSTFD 000001 10 0--.-- .................. \ ### VSX instructions +PLXSD 000001 00 0--.-- .................. \ + 101010 ..... ..... ................ @8LS_D + +PSTXSD 000001 00 0--.-- .................. \ + 101110 ..... ..... ................ @8LS_D + +PLXSSP 000001 00 0--.-- .................. \ + 101011 ..... ..... ................ @8LS_D + +PSTXSSP 000001 00 0--.-- .................. \ + 101111 ..... ..... ................ @8LS_D + PLXV 000001 00 0--.-- .................. \ 11001 ...... ..... ................ @8LS_D_TSX PSTXV 000001 00 0--.-- .................. \ @@ -179,6 +205,9 @@ PLXVP 000001 00 0--.-- .................. \ PSTXVP 000001 00 0--.-- .................. \ 111110 ..... ..... ................ @8LS_D_TSXP +XXEVAL 000001 01 0000 -- ---------- ........ \ + 100010 ..... ..... ..... ..... 01 .... @8RR_XX4_imm + XXSPLTIDP 000001 01 0000 -- -- ................ \ 100000 ..... 0010 . ................ @8RR_D XXSPLTIW 000001 01 0000 -- -- ................ \ @@ -187,10 +216,13 @@ XXSPLTI32DX 000001 01 0000 -- -- ................ \ 100000 ..... 000 .. ................ @8RR_D_IX XXBLENDVD 000001 01 0000 -- ------------------ \ - 100001 ..... ..... ..... ..... 11 .... @XX4 + 100001 ..... ..... ..... ..... 11 .... @8RR_XX4 XXBLENDVW 000001 01 0000 -- ------------------ \ - 100001 ..... ..... ..... ..... 10 .... @XX4 + 100001 ..... ..... ..... ..... 10 .... @8RR_XX4 XXBLENDVH 000001 01 0000 -- ------------------ \ - 100001 ..... ..... ..... ..... 01 .... @XX4 + 100001 ..... ..... ..... ..... 01 .... @8RR_XX4 XXBLENDVB 000001 01 0000 -- ------------------ \ - 100001 ..... ..... ..... ..... 00 .... @XX4 + 100001 ..... ..... ..... ..... 00 .... @8RR_XX4 + +XXPERMX 000001 01 0000 -- --------------- ... \ + 100010 ..... ..... ..... ..... 00 .... @8RR_XX4_uim3 diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index d1b12788b2..8c1674510b 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -28,6 +28,7 @@ #include "fpu/softfloat.h" #include "qapi/error.h" #include "qemu/guest-random.h" +#include "tcg/tcg-gvec-desc.h" #include "helper_regs.h" /*****************************************************************************/ @@ -424,7 +425,7 @@ uint64_t helper_PEXTD(uint64_t src, uint64_t mask) /*****************************************************************************/ /* Altivec extension helpers */ -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define VECTOR_FOR_INORDER_I(index, element) \ for (index = 0; index < ARRAY_SIZE(r->element); index++) #else @@ -662,100 +663,18 @@ VCF(ux, uint32_to_float32, u32) VCF(sx, int32_to_float32, s32) #undef VCF -#define VCMP_DO(suffix, compare, element, record) \ - void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ - ppc_avr_t *a, ppc_avr_t *b) \ - { \ - uint64_t ones = (uint64_t)-1; \ - uint64_t all = ones; \ - uint64_t none = 0; \ - int i; \ - \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - uint64_t result = (a->element[i] compare b->element[i] ? \ - ones : 0x0); \ - switch (sizeof(a->element[0])) { \ - case 8: \ - r->u64[i] = result; \ - break; \ - case 4: \ - r->u32[i] = result; \ - break; \ - case 2: \ - r->u16[i] = result; \ - break; \ - case 1: \ - r->u8[i] = result; \ - break; \ - } \ - all &= result; \ - none |= result; \ - } \ - if (record) { \ - env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ - } \ - } -#define VCMP(suffix, compare, element) \ - VCMP_DO(suffix, compare, element, 0) \ - VCMP_DO(suffix##_dot, compare, element, 1) -VCMP(equb, ==, u8) -VCMP(equh, ==, u16) -VCMP(equw, ==, u32) -VCMP(equd, ==, u64) -VCMP(gtub, >, u8) -VCMP(gtuh, >, u16) -VCMP(gtuw, >, u32) -VCMP(gtud, >, u64) -VCMP(gtsb, >, s8) -VCMP(gtsh, >, s16) -VCMP(gtsw, >, s32) -VCMP(gtsd, >, s64) -#undef VCMP_DO -#undef VCMP - -#define VCMPNE_DO(suffix, element, etype, cmpzero, record) \ -void helper_vcmpne##suffix(CPUPPCState *env, ppc_avr_t *r, \ - ppc_avr_t *a, ppc_avr_t *b) \ -{ \ - etype ones = (etype)-1; \ - etype all = ones; \ - etype result, none = 0; \ - int i; \ - \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - if (cmpzero) { \ - result = ((a->element[i] == 0) \ - || (b->element[i] == 0) \ - || (a->element[i] != b->element[i]) ? \ - ones : 0x0); \ - } else { \ - result = (a->element[i] != b->element[i]) ? ones : 0x0; \ - } \ - r->element[i] = result; \ - all &= result; \ - none |= result; \ - } \ - if (record) { \ - env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ - } \ +#define VCMPNEZ(NAME, ELEM) \ +void helper_##NAME(ppc_vsr_t *t, ppc_vsr_t *a, ppc_vsr_t *b, uint32_t desc) \ +{ \ + for (int i = 0; i < ARRAY_SIZE(t->ELEM); i++) { \ + t->ELEM[i] = ((a->ELEM[i] == 0) || (b->ELEM[i] == 0) || \ + (a->ELEM[i] != b->ELEM[i])) ? -1 : 0; \ + } \ } - -/* - * VCMPNEZ - Vector compare not equal to zero - * suffix - instruction mnemonic suffix (b: byte, h: halfword, w: word) - * element - element type to access from vector - */ -#define VCMPNE(suffix, element, etype, cmpzero) \ - VCMPNE_DO(suffix, element, etype, cmpzero, 0) \ - VCMPNE_DO(suffix##_dot, element, etype, cmpzero, 1) -VCMPNE(zb, u8, uint8_t, 1) -VCMPNE(zh, u16, uint16_t, 1) -VCMPNE(zw, u32, uint32_t, 1) -VCMPNE(b, u8, uint8_t, 0) -VCMPNE(h, u16, uint16_t, 0) -VCMPNE(w, u32, uint32_t, 0) -#undef VCMPNE_DO -#undef VCMPNE +VCMPNEZ(VCMPNEZB, u8) +VCMPNEZ(VCMPNEZH, u16) +VCMPNEZ(VCMPNEZW, u32) +#undef VCMPNEZ #define VCMPFP_DO(suffix, compare, order, record) \ void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ @@ -1063,7 +982,7 @@ void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } #define VMUL_DO_EVN(name, mul_element, mul_access, prod_access, cast) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + void helper_V##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ @@ -1074,7 +993,7 @@ void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } #define VMUL_DO_ODD(name, mul_element, mul_access, prod_access, cast) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + void helper_V##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ @@ -1085,55 +1004,39 @@ void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } #define VMUL(suffix, mul_element, mul_access, prod_access, cast) \ - VMUL_DO_EVN(mule##suffix, mul_element, mul_access, prod_access, cast) \ - VMUL_DO_ODD(mulo##suffix, mul_element, mul_access, prod_access, cast) -VMUL(sb, s8, VsrSB, VsrSH, int16_t) -VMUL(sh, s16, VsrSH, VsrSW, int32_t) -VMUL(sw, s32, VsrSW, VsrSD, int64_t) -VMUL(ub, u8, VsrB, VsrH, uint16_t) -VMUL(uh, u16, VsrH, VsrW, uint32_t) -VMUL(uw, u32, VsrW, VsrD, uint64_t) + VMUL_DO_EVN(MULE##suffix, mul_element, mul_access, prod_access, cast) \ + VMUL_DO_ODD(MULO##suffix, mul_element, mul_access, prod_access, cast) +VMUL(SB, s8, VsrSB, VsrSH, int16_t) +VMUL(SH, s16, VsrSH, VsrSW, int32_t) +VMUL(SW, s32, VsrSW, VsrSD, int64_t) +VMUL(UB, u8, VsrB, VsrH, uint16_t) +VMUL(UH, u16, VsrH, VsrW, uint32_t) +VMUL(UW, u32, VsrW, VsrD, uint64_t) #undef VMUL_DO_EVN #undef VMUL_DO_ODD #undef VMUL -void helper_vmulhsw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +void helper_XXPERMX(ppc_vsr_t *t, ppc_vsr_t *s0, ppc_vsr_t *s1, ppc_vsr_t *pcv, + target_ulong uim) { - int i; + int i, idx; + ppc_vsr_t tmp = { .u64 = {0, 0} }; - for (i = 0; i < 4; i++) { - r->s32[i] = (int32_t)(((int64_t)a->s32[i] * (int64_t)b->s32[i]) >> 32); + for (i = 0; i < ARRAY_SIZE(t->u8); i++) { + if ((pcv->VsrB(i) >> 5) == uim) { + idx = pcv->VsrB(i) & 0x1f; + if (idx < ARRAY_SIZE(t->u8)) { + tmp.VsrB(i) = s0->VsrB(idx); + } else { + tmp.VsrB(i) = s1->VsrB(idx - ARRAY_SIZE(t->u8)); + } + } } + + *t = tmp; } -void helper_vmulhuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i; - - for (i = 0; i < 4; i++) { - r->u32[i] = (uint32_t)(((uint64_t)a->u32[i] * - (uint64_t)b->u32[i]) >> 32); - } -} - -void helper_vmulhsd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - uint64_t discard; - - muls64(&discard, &r->u64[0], a->s64[0], b->s64[0]); - muls64(&discard, &r->u64[1], a->s64[1], b->s64[1]); -} - -void helper_vmulhud(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - uint64_t discard; - - mulu64(&discard, &r->u64[0], a->u64[0], b->u64[0]); - mulu64(&discard, &r->u64[1], a->u64[1], b->u64[1]); -} - -void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, - ppc_avr_t *c) +void helper_VPERM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { ppc_avr_t result; int i; @@ -1151,8 +1054,7 @@ void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, *r = result; } -void helper_vpermr(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, - ppc_avr_t *c) +void helper_VPERMR(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { ppc_avr_t result; int i; @@ -1170,7 +1072,112 @@ void helper_vpermr(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, *r = result; } -#if defined(HOST_WORDS_BIGENDIAN) +#define XXGENPCV_BE_EXP(NAME, SZ) \ +void glue(helper_, glue(NAME, _be_exp))(ppc_vsr_t *t, ppc_vsr_t *b) \ +{ \ + ppc_vsr_t tmp; \ + \ + /* Initialize tmp with the result of an all-zeros mask */ \ + tmp.VsrD(0) = 0x1011121314151617; \ + tmp.VsrD(1) = 0x18191A1B1C1D1E1F; \ + \ + /* Iterate over the most significant byte of each element */ \ + for (int i = 0, j = 0; i < ARRAY_SIZE(b->u8); i += SZ) { \ + if (b->VsrB(i) & 0x80) { \ + /* Update each byte of the element */ \ + for (int k = 0; k < SZ; k++) { \ + tmp.VsrB(i + k) = j + k; \ + } \ + j += SZ; \ + } \ + } \ + \ + *t = tmp; \ +} + +#define XXGENPCV_BE_COMP(NAME, SZ) \ +void glue(helper_, glue(NAME, _be_comp))(ppc_vsr_t *t, ppc_vsr_t *b)\ +{ \ + ppc_vsr_t tmp = { .u64 = { 0, 0 } }; \ + \ + /* Iterate over the most significant byte of each element */ \ + for (int i = 0, j = 0; i < ARRAY_SIZE(b->u8); i += SZ) { \ + if (b->VsrB(i) & 0x80) { \ + /* Update each byte of the element */ \ + for (int k = 0; k < SZ; k++) { \ + tmp.VsrB(j + k) = i + k; \ + } \ + j += SZ; \ + } \ + } \ + \ + *t = tmp; \ +} + +#define XXGENPCV_LE_EXP(NAME, SZ) \ +void glue(helper_, glue(NAME, _le_exp))(ppc_vsr_t *t, ppc_vsr_t *b) \ +{ \ + ppc_vsr_t tmp; \ + \ + /* Initialize tmp with the result of an all-zeros mask */ \ + tmp.VsrD(0) = 0x1F1E1D1C1B1A1918; \ + tmp.VsrD(1) = 0x1716151413121110; \ + \ + /* Iterate over the most significant byte of each element */ \ + for (int i = 0, j = 0; i < ARRAY_SIZE(b->u8); i += SZ) { \ + /* Reverse indexing of "i" */ \ + const int idx = ARRAY_SIZE(b->u8) - i - SZ; \ + if (b->VsrB(idx) & 0x80) { \ + /* Update each byte of the element */ \ + for (int k = 0, rk = SZ - 1; k < SZ; k++, rk--) { \ + tmp.VsrB(idx + rk) = j + k; \ + } \ + j += SZ; \ + } \ + } \ + \ + *t = tmp; \ +} + +#define XXGENPCV_LE_COMP(NAME, SZ) \ +void glue(helper_, glue(NAME, _le_comp))(ppc_vsr_t *t, ppc_vsr_t *b)\ +{ \ + ppc_vsr_t tmp = { .u64 = { 0, 0 } }; \ + \ + /* Iterate over the most significant byte of each element */ \ + for (int i = 0, j = 0; i < ARRAY_SIZE(b->u8); i += SZ) { \ + if (b->VsrB(ARRAY_SIZE(b->u8) - i - SZ) & 0x80) { \ + /* Update each byte of the element */ \ + for (int k = 0, rk = SZ - 1; k < SZ; k++, rk--) { \ + /* Reverse indexing of "j" */ \ + const int idx = ARRAY_SIZE(b->u8) - j - SZ; \ + tmp.VsrB(idx + rk) = i + k; \ + } \ + j += SZ; \ + } \ + } \ + \ + *t = tmp; \ +} + +#define XXGENPCV(NAME, SZ) \ + XXGENPCV_BE_EXP(NAME, SZ) \ + XXGENPCV_BE_COMP(NAME, SZ) \ + XXGENPCV_LE_EXP(NAME, SZ) \ + XXGENPCV_LE_COMP(NAME, SZ) \ + +XXGENPCV(XXGENPCVBM, 1) +XXGENPCV(XXGENPCVHM, 2) +XXGENPCV(XXGENPCVWM, 4) +XXGENPCV(XXGENPCVDM, 8) + +#undef XXGENPCV_BE_EXP +#undef XXGENPCV_BE_COMP +#undef XXGENPCV_LE_EXP +#undef XXGENPCV_LE_COMP +#undef XXGENPCV + +#if HOST_BIG_ENDIAN #define VBPERMQ_INDEX(avr, i) ((avr)->u8[(i)]) #define VBPERMD_INDEX(i) (i) #define VBPERMQ_DW(index) (((index) & 0x40) != 0) @@ -1291,7 +1298,7 @@ void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define PKBIG 1 #else #define PKBIG 0 @@ -1300,7 +1307,7 @@ void helper_vpkpx(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { int i, j; ppc_avr_t result; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN const ppc_avr_t *x[2] = { a, b }; #else const ppc_avr_t *x[2] = { b, a }; @@ -1392,40 +1399,33 @@ void helper_vrsqrtefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) } } -#define VRLMI(name, size, element, insert) \ -void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ -{ \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - uint##size##_t src1 = a->element[i]; \ - uint##size##_t src2 = b->element[i]; \ - uint##size##_t src3 = r->element[i]; \ - uint##size##_t begin, end, shift, mask, rot_val; \ - \ - shift = extract##size(src2, 0, 6); \ - end = extract##size(src2, 8, 6); \ - begin = extract##size(src2, 16, 6); \ - rot_val = rol##size(src1, shift); \ - mask = mask_u##size(begin, end); \ - if (insert) { \ - r->element[i] = (rot_val & mask) | (src3 & ~mask); \ - } else { \ - r->element[i] = (rot_val & mask); \ - } \ - } \ +#define VRLMI(name, size, element, insert) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t desc) \ +{ \ + int i; \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + uint##size##_t src1 = a->element[i]; \ + uint##size##_t src2 = b->element[i]; \ + uint##size##_t src3 = r->element[i]; \ + uint##size##_t begin, end, shift, mask, rot_val; \ + \ + shift = extract##size(src2, 0, 6); \ + end = extract##size(src2, 8, 6); \ + begin = extract##size(src2, 16, 6); \ + rot_val = rol##size(src1, shift); \ + mask = mask_u##size(begin, end); \ + if (insert) { \ + r->element[i] = (rot_val & mask) | (src3 & ~mask); \ + } else { \ + r->element[i] = (rot_val & mask); \ + } \ + } \ } -VRLMI(vrldmi, 64, u64, 1); -VRLMI(vrlwmi, 32, u32, 1); -VRLMI(vrldnm, 64, u64, 0); -VRLMI(vrlwnm, 32, u32, 0); - -void helper_vsel(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, - ppc_avr_t *c) -{ - r->u64[0] = (a->u64[0] & ~c->u64[0]) | (b->u64[0] & c->u64[0]); - r->u64[1] = (a->u64[1] & ~c->u64[1]) | (b->u64[1] & c->u64[1]); -} +VRLMI(VRLDMI, 64, u64, 1); +VRLMI(VRLWMI, 32, u32, 1); +VRLMI(VRLDNM, 64, u64, 0); +VRLMI(VRLWNM, 32, u32, 0); void helper_vexptefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) { @@ -1516,7 +1516,7 @@ void helper_vslo(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { int sh = (b->VsrB(0xf) >> 3) & 0xf; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN memmove(&r->u8[0], &a->u8[sh], 16 - sh); memset(&r->u8[16 - sh], 0, sh); #else @@ -1525,7 +1525,7 @@ void helper_vslo(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) #endif } -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define ELEM_ADDR(VEC, IDX, SIZE) (&(VEC)->u8[IDX]) #else #define ELEM_ADDR(VEC, IDX, SIZE) (&(VEC)->u8[15 - (IDX)] - (SIZE) + 1) @@ -1554,7 +1554,7 @@ VINSX(W, uint32_t) VINSX(D, uint64_t) #undef ELEM_ADDR #undef VINSX -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define VEXTDVLX(NAME, SIZE) \ void helper_##NAME(CPUPPCState *env, ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, \ target_ulong index) \ @@ -1593,7 +1593,7 @@ VEXTDVLX(VEXTDUHVLX, 2) VEXTDVLX(VEXTDUWVLX, 4) VEXTDVLX(VEXTDDVLX, 8) #undef VEXTDVLX -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define VEXTRACT(suffix, element) \ void helper_vextract##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ { \ @@ -1619,6 +1619,34 @@ VEXTRACT(uw, u32) VEXTRACT(d, u64) #undef VEXTRACT +#define VSTRI(NAME, ELEM, NUM_ELEMS, LEFT) \ +uint32_t helper_##NAME(ppc_avr_t *t, ppc_avr_t *b) \ +{ \ + int i, idx, crf = 0; \ + \ + for (i = 0; i < NUM_ELEMS; i++) { \ + idx = LEFT ? i : NUM_ELEMS - i - 1; \ + if (b->Vsr##ELEM(idx)) { \ + t->Vsr##ELEM(idx) = b->Vsr##ELEM(idx); \ + } else { \ + crf = 0b0010; \ + break; \ + } \ + } \ + \ + for (; i < NUM_ELEMS; i++) { \ + idx = LEFT ? i : NUM_ELEMS - i - 1; \ + t->Vsr##ELEM(idx) = 0; \ + } \ + \ + return crf; \ +} +VSTRI(VSTRIBL, B, 16, true) +VSTRI(VSTRIBR, B, 16, false) +VSTRI(VSTRIHL, H, 8, true) +VSTRI(VSTRIHR, H, 8, false) +#undef VSTRI + void helper_xxextractuw(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb, uint32_t index) { @@ -1650,6 +1678,47 @@ void helper_xxinsertw(CPUPPCState *env, ppc_vsr_t *xt, *xt = t; } +void helper_XXEVAL(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c, + uint32_t desc) +{ + /* + * Instead of processing imm bit-by-bit, we'll skip the computation of + * conjunctions whose corresponding bit is unset. + */ + int bit, imm = simd_data(desc); + Int128 conj, disj = int128_zero(); + + /* Iterate over set bits from the least to the most significant bit */ + while (imm) { + /* + * Get the next bit to be processed with ctz64. Invert the result of + * ctz64 to match the indexing used by PowerISA. + */ + bit = 7 - ctzl(imm); + if (bit & 0x4) { + conj = a->s128; + } else { + conj = int128_not(a->s128); + } + if (bit & 0x2) { + conj = int128_and(conj, b->s128); + } else { + conj = int128_and(conj, int128_not(b->s128)); + } + if (bit & 0x1) { + conj = int128_and(conj, c->s128); + } else { + conj = int128_and(conj, int128_not(c->s128)); + } + disj = int128_or(disj, conj); + + /* Unset the least significant bit that is set */ + imm &= imm - 1; + } + + t->s128 = disj; +} + #define XXBLEND(name, sz) \ void glue(helper_XXBLENDV, name)(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, \ ppc_avr_t *c, uint32_t desc) \ @@ -1665,21 +1734,6 @@ XXBLEND(W, 32) XXBLEND(D, 64) #undef XXBLEND -#define VEXT_SIGNED(name, element, cast) \ -void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ -{ \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - r->element[i] = (cast)b->element[i]; \ - } \ -} -VEXT_SIGNED(vextsb2w, s32, int8_t) -VEXT_SIGNED(vextsb2d, s64, int8_t) -VEXT_SIGNED(vextsh2w, s32, int16_t) -VEXT_SIGNED(vextsh2d, s64, int16_t) -VEXT_SIGNED(vextsw2d, s64, int32_t) -#undef VEXT_SIGNED - #define VNEG(name, element) \ void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ { \ @@ -1696,7 +1750,7 @@ void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { int sh = (b->VsrB(0xf) >> 3) & 0xf; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN memmove(&r->u8[sh], &a->u8[0], 16 - sh); memset(&r->u8[0], 0, sh); #else @@ -1813,7 +1867,7 @@ void helper_vsum4ubs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } } -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define UPKHI 1 #define UPKLO 0 #else @@ -1920,7 +1974,7 @@ VGENERIC_DO(popcntd, u64) #undef VGENERIC_DO -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define QW_ONE { .u64 = { 0, 1 } } #else #define QW_ONE { .u64 = { 1, 0 } } diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 6aa9484f34..8094e0b033 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -286,9 +286,9 @@ void ppc_cpu_record_sigsegv(CPUState *cs, vaddr addr, bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); #endif #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index dc93b99189..a3c31b4e48 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -21,7 +21,6 @@ #include -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "cpu.h" @@ -418,7 +417,7 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp) * will be a normal mapping, not a special hugepage one used * for RAM. */ - if (qemu_real_host_page_size < 0x10000) { + if (qemu_real_host_page_size() < 0x10000) { error_setg(errp, "KVM can't supply 64kiB CI pages, which guest expects"); } @@ -632,7 +631,7 @@ static int kvm_put_fp(CPUState *cs) uint64_t *fpr = cpu_fpr_ptr(&cpu->env, i); uint64_t *vsrl = cpu_vsrl_ptr(&cpu->env, i); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN vsr[0] = float64_val(*fpr); vsr[1] = *vsrl; #else @@ -710,7 +709,7 @@ static int kvm_get_fp(CPUState *cs) strerror(errno)); return ret; } else { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN *fpr = vsr[0]; if (vsx) { *vsrl = vsr[1]; @@ -1681,7 +1680,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) break; #if defined(TARGET_PPC64) case KVM_EXIT_PAPR_HCALL: - trace_kvm_handle_papr_hcall(); + trace_kvm_handle_papr_hcall(run->papr_hcall.nr); run->papr_hcall.ret = spapr_hypercall(cpu, run->papr_hcall.nr, run->papr_hcall.args); diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 1b63146ed1..e673944597 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -2,6 +2,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "helper_regs.h" #include "mmu-hash64.h" #include "migration/cpu.h" @@ -20,7 +21,10 @@ static void post_load_update_msr(CPUPPCState *env) */ env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB); ppc_store_msr(env, msr); - pmu_update_summaries(env); + + if (tcg_enabled()) { + pmu_update_summaries(env); + } } static int get_avr(QEMUFile *f, void *pv, size_t size, diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 39945d9ea5..c4ff8fd632 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -32,7 +32,7 @@ static inline bool needs_byteswap(const CPUPPCState *env) { -#if defined(TARGET_WORDS_BIGENDIAN) +#if TARGET_BIG_ENDIAN return msr_le; #else return !msr_le; @@ -461,7 +461,7 @@ uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr, /*****************************************************************************/ /* Altivec extension helpers */ -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN #define HI_IDX 0 #define LO_IDX 1 #else diff --git a/target/ppc/meson.build b/target/ppc/meson.build index a49a8911e0..79beaff147 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -16,6 +16,7 @@ ppc_ss.add(when: 'CONFIG_TCG', if_true: files( 'misc_helper.c', 'timebase_helper.c', 'translate.c', + 'power8-pmu.c', )) ppc_ss.add(libdecnumber) @@ -51,7 +52,6 @@ ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files( 'mmu-book3s-v3.c', 'mmu-hash64.c', 'mmu-radix64.c', - 'power8-pmu.c', )) target_arch += {'ppc': ppc_ss} diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 67c38f065b..5414fd63c1 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -204,7 +204,8 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, /* Check if requested access type is allowed */ need_prot = prot_for_access_type(access_type); if (need_prot & ~*prot) { /* Page Protected for that Access */ - *fault_cause |= DSISR_PROTFAULT; + *fault_cause |= access_type == MMU_INST_FETCH ? SRR1_NOEXEC_GUARD : + DSISR_PROTFAULT; return true; } diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index 236e8e66e9..beeab5c494 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -222,6 +222,20 @@ static void pmu_update_overflow_timers(CPUPPCState *env) } } +static void pmu_delete_timers(CPUPPCState *env) +{ + QEMUTimer *pmc_overflow_timer; + int sprn; + + for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) { + pmc_overflow_timer = get_cyc_overflow_timer(env, sprn); + + if (pmc_overflow_timer) { + timer_del(pmc_overflow_timer); + } + } +} + void helper_store_mmcr0(CPUPPCState *env, target_ulong value) { bool hflags_pmcc0 = (value & MMCR0_PMCC0) != 0; @@ -271,12 +285,29 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; - if (!(env->spr[SPR_POWER_MMCR0] & MMCR0_EBE)) { - return; + pmu_update_cycles(env); + + if (env->spr[SPR_POWER_MMCR0] & MMCR0_FCECE) { + env->spr[SPR_POWER_MMCR0] &= ~MMCR0_FCECE; + env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; + + /* Changing MMCR0_FC requires a new HFLAGS_INSN_CNT calc */ + pmu_update_summaries(env); + + /* + * Delete all pending timers if we need to freeze + * the PMC. We'll restart them when the PMC starts + * running again. + */ + pmu_delete_timers(env); } - /* PMC interrupt not implemented yet */ - return; + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE) { + env->spr[SPR_POWER_MMCR0] &= ~MMCR0_PMAE; + env->spr[SPR_POWER_MMCR0] |= MMCR0_PMAO; + } + + raise_ebb_perfm_exception(env); } /* This helper assumes that the PMC is running. */ diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index a839199561..256d90f523 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -13,11 +13,11 @@ #ifndef POWER8_PMU #define POWER8_PMU -void cpu_ppc_pmu_init(CPUPPCState *env); - #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +void cpu_ppc_pmu_init(CPUPPCState *env); void pmu_update_summaries(CPUPPCState *env); #else +static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } static inline void pmu_update_summaries(CPUPPCState *env) { } #endif diff --git a/target/ppc/trace-events b/target/ppc/trace-events index 53b107f56e..a79f1b4370 100644 --- a/target/ppc/trace-events +++ b/target/ppc/trace-events @@ -23,7 +23,7 @@ kvm_failed_get_vpa(void) "Warning: Unable to get VPA information from KVM" kvm_handle_dcr_write(void) "handle dcr write" kvm_handle_dcr_read(void) "handle dcr read" kvm_handle_halt(void) "handle halt" -kvm_handle_papr_hcall(void) "handle PAPR hypercall" +kvm_handle_papr_hcall(uint64_t hcall) "0x%" PRIx64 kvm_handle_epr(void) "handle epr" kvm_handle_watchdog_expiry(void) "handle watchdog expiry" kvm_handle_debug_exception(void) "handle debug exception" diff --git a/target/ppc/translate.c b/target/ppc/translate.c index ecc5a104e0..fa34f81c30 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -193,7 +193,7 @@ struct DisasContext { /* Return true iff byteswap is needed in a scalar memop */ static inline bool need_byteswap(const DisasContext *ctx) { -#if defined(TARGET_WORDS_BIGENDIAN) +#if TARGET_BIG_ENDIAN return ctx->le_mode; #else return !ctx->le_mode; @@ -6604,10 +6604,29 @@ static int times_16(DisasContext *ctx, int x) #define TRANS(NAME, FUNC, ...) \ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ { return FUNC(ctx, a, __VA_ARGS__); } +#define TRANS_FLAGS(FLAGS, NAME, FUNC, ...) \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ + { \ + REQUIRE_INSNS_FLAGS(ctx, FLAGS); \ + return FUNC(ctx, a, __VA_ARGS__); \ + } +#define TRANS_FLAGS2(FLAGS2, NAME, FUNC, ...) \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ + { \ + REQUIRE_INSNS_FLAGS2(ctx, FLAGS2); \ + return FUNC(ctx, a, __VA_ARGS__); \ + } #define TRANS64(NAME, FUNC, ...) \ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ { REQUIRE_64BIT(ctx); return FUNC(ctx, a, __VA_ARGS__); } +#define TRANS64_FLAGS2(FLAGS2, NAME, FUNC, ...) \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ + { \ + REQUIRE_64BIT(ctx); \ + REQUIRE_INSNS_FLAGS2(ctx, FLAGS2); \ + return FUNC(ctx, a, __VA_ARGS__); \ + } /* TODO: More TRANS* helpers for extra insn_flags checks. */ @@ -6649,49 +6668,24 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/branch-impl.c.inc" -/* Handles lfdp, lxsd, lxssp */ +/* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { - switch (ctx->opcode & 0x3) { - case 0: /* lfdp */ + if ((ctx->opcode & 0x3) == 0) { if (ctx->insns_flags2 & PPC2_ISA205) { return gen_lfdp(ctx); } - break; - case 2: /* lxsd */ - if (ctx->insns_flags2 & PPC2_ISA300) { - return gen_lxsd(ctx); - } - break; - case 3: /* lxssp */ - if (ctx->insns_flags2 & PPC2_ISA300) { - return gen_lxssp(ctx); - } - break; } return gen_invalid(ctx); } -/* handles stfdp, lxv, stxsd, stxssp lxvx */ +/* Handles stfdp */ static void gen_dform3D(DisasContext *ctx) { - if ((ctx->opcode & 3) != 1) { /* DS-FORM */ - switch (ctx->opcode & 0x3) { - case 0: /* stfdp */ - if (ctx->insns_flags2 & PPC2_ISA205) { - return gen_stfdp(ctx); - } - break; - case 2: /* stxsd */ - if (ctx->insns_flags2 & PPC2_ISA300) { - return gen_stxsd(ctx); - } - break; - case 3: /* stxssp */ - if (ctx->insns_flags2 & PPC2_ISA300) { - return gen_stxssp(ctx); - } - break; + if ((ctx->opcode & 3) == 0) { /* DS-FORM */ + /* stfdp */ + if (ctx->insns_flags2 & PPC2_ISA205) { + return gen_stfdp(ctx); } } return gen_invalid(ctx); @@ -7775,10 +7769,11 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void ppc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void ppc_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps ppc_tr_ops = { diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index d5e02fd7f2..764ac45409 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -173,7 +173,7 @@ static void gen_mtvscr(DisasContext *ctx) val = tcg_temp_new_i32(); bofs = avr_full_offset(rB(ctx->opcode)); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN bofs += 3 * 4; #endif @@ -798,45 +798,7 @@ static void trans_vclzd(DisasContext *ctx) tcg_temp_free_i64(avr); } -GEN_VXFORM(vmuloub, 4, 0); -GEN_VXFORM(vmulouh, 4, 1); -GEN_VXFORM(vmulouw, 4, 2); GEN_VXFORM_V(vmuluwm, MO_32, tcg_gen_gvec_mul, 4, 2); -GEN_VXFORM_DUAL(vmulouw, PPC_ALTIVEC, PPC_NONE, - vmuluwm, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM(vmulosb, 4, 4); -GEN_VXFORM(vmulosh, 4, 5); -GEN_VXFORM(vmulosw, 4, 6); -GEN_VXFORM_V(vmulld, MO_64, tcg_gen_gvec_mul, 4, 7); -GEN_VXFORM(vmuleub, 4, 8); -GEN_VXFORM(vmuleuh, 4, 9); -GEN_VXFORM(vmuleuw, 4, 10); -GEN_VXFORM(vmulhuw, 4, 10); -GEN_VXFORM(vmulhud, 4, 11); -GEN_VXFORM_DUAL(vmuleuw, PPC_ALTIVEC, PPC_NONE, - vmulhuw, PPC_NONE, PPC2_ISA310); -GEN_VXFORM(vmulesb, 4, 12); -GEN_VXFORM(vmulesh, 4, 13); -GEN_VXFORM(vmulesw, 4, 14); -GEN_VXFORM(vmulhsw, 4, 14); -GEN_VXFORM_DUAL(vmulesw, PPC_ALTIVEC, PPC_NONE, - vmulhsw, PPC_NONE, PPC2_ISA310); -GEN_VXFORM(vmulhsd, 4, 15); -GEN_VXFORM_V(vslb, MO_8, tcg_gen_gvec_shlv, 2, 4); -GEN_VXFORM_V(vslh, MO_16, tcg_gen_gvec_shlv, 2, 5); -GEN_VXFORM_V(vslw, MO_32, tcg_gen_gvec_shlv, 2, 6); -GEN_VXFORM(vrlwnm, 2, 6); -GEN_VXFORM_DUAL(vslw, PPC_ALTIVEC, PPC_NONE, \ - vrlwnm, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_V(vsld, MO_64, tcg_gen_gvec_shlv, 2, 23); -GEN_VXFORM_V(vsrb, MO_8, tcg_gen_gvec_shrv, 2, 8); -GEN_VXFORM_V(vsrh, MO_16, tcg_gen_gvec_shrv, 2, 9); -GEN_VXFORM_V(vsrw, MO_32, tcg_gen_gvec_shrv, 2, 10); -GEN_VXFORM_V(vsrd, MO_64, tcg_gen_gvec_shrv, 2, 27); -GEN_VXFORM_V(vsrab, MO_8, tcg_gen_gvec_sarv, 2, 12); -GEN_VXFORM_V(vsrah, MO_16, tcg_gen_gvec_sarv, 2, 13); -GEN_VXFORM_V(vsraw, MO_32, tcg_gen_gvec_sarv, 2, 14); -GEN_VXFORM_V(vsrad, MO_64, tcg_gen_gvec_sarv, 2, 15); GEN_VXFORM(vsrv, 2, 28); GEN_VXFORM(vslv, 2, 29); GEN_VXFORM(vslo, 6, 16); @@ -844,6 +806,381 @@ GEN_VXFORM(vsro, 6, 17); GEN_VXFORM(vaddcuw, 0, 6); GEN_VXFORM(vsubcuw, 0, 22); +static bool do_vector_gvec3_VX(DisasContext *ctx, arg_VX *a, int vece, + void (*gen_gvec)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t)) +{ + REQUIRE_VECTOR(ctx); + + gen_gvec(vece, avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16); + + return true; +} + +TRANS_FLAGS(ALTIVEC, VSLB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_shlv); +TRANS_FLAGS(ALTIVEC, VSLH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_shlv); +TRANS_FLAGS(ALTIVEC, VSLW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_shlv); +TRANS_FLAGS2(ALTIVEC_207, VSLD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_shlv); + +TRANS_FLAGS(ALTIVEC, VSRB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_shrv); +TRANS_FLAGS(ALTIVEC, VSRH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_shrv); +TRANS_FLAGS(ALTIVEC, VSRW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_shrv); +TRANS_FLAGS2(ALTIVEC_207, VSRD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_shrv); + +TRANS_FLAGS(ALTIVEC, VSRAB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_sarv); +TRANS_FLAGS(ALTIVEC, VSRAH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_sarv); +TRANS_FLAGS(ALTIVEC, VSRAW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_sarv); +TRANS_FLAGS2(ALTIVEC_207, VSRAD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_sarv); + +TRANS_FLAGS(ALTIVEC, VRLB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_rotlv) +TRANS_FLAGS(ALTIVEC, VRLH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_rotlv) +TRANS_FLAGS(ALTIVEC, VRLW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_rotlv) +TRANS_FLAGS2(ALTIVEC_207, VRLD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_rotlv) + +static TCGv_vec do_vrl_mask_vec(unsigned vece, TCGv_vec vrb) +{ + TCGv_vec t0 = tcg_temp_new_vec_matching(vrb), + t1 = tcg_temp_new_vec_matching(vrb), + t2 = tcg_temp_new_vec_matching(vrb), + ones = tcg_constant_vec_matching(vrb, vece, -1); + + /* Extract b and e */ + tcg_gen_dupi_vec(vece, t2, (8 << vece) - 1); + + tcg_gen_shri_vec(vece, t0, vrb, 16); + tcg_gen_and_vec(vece, t0, t0, t2); + + tcg_gen_shri_vec(vece, t1, vrb, 8); + tcg_gen_and_vec(vece, t1, t1, t2); + + /* Compare b and e to negate the mask where begin > end */ + tcg_gen_cmp_vec(TCG_COND_GT, vece, t2, t0, t1); + + /* Create the mask with (~0 >> b) ^ ((~0 >> e) >> 1) */ + tcg_gen_shrv_vec(vece, t0, ones, t0); + tcg_gen_shrv_vec(vece, t1, ones, t1); + tcg_gen_shri_vec(vece, t1, t1, 1); + tcg_gen_xor_vec(vece, t0, t0, t1); + + /* negate the mask */ + tcg_gen_xor_vec(vece, t0, t0, t2); + + tcg_temp_free_vec(t1); + tcg_temp_free_vec(t2); + + return t0; +} + +static void gen_vrlnm_vec(unsigned vece, TCGv_vec vrt, TCGv_vec vra, + TCGv_vec vrb) +{ + TCGv_vec mask, n = tcg_temp_new_vec_matching(vrt); + + /* Create the mask */ + mask = do_vrl_mask_vec(vece, vrb); + + /* Extract n */ + tcg_gen_dupi_vec(vece, n, (8 << vece) - 1); + tcg_gen_and_vec(vece, n, vrb, n); + + /* Rotate and mask */ + tcg_gen_rotlv_vec(vece, vrt, vra, n); + tcg_gen_and_vec(vece, vrt, vrt, mask); + + tcg_temp_free_vec(n); + tcg_temp_free_vec(mask); +} + +static bool do_vrlnm(DisasContext *ctx, arg_VX *a, int vece) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_cmp_vec, INDEX_op_rotlv_vec, INDEX_op_sari_vec, + INDEX_op_shli_vec, INDEX_op_shri_vec, INDEX_op_shrv_vec, 0 + }; + static const GVecGen3 ops[2] = { + { + .fniv = gen_vrlnm_vec, + .fno = gen_helper_VRLWNM, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 + }, + { + .fniv = gen_vrlnm_vec, + .fno = gen_helper_VRLDNM, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 + } + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &ops[vece - 2]); + + return true; +} + +TRANS(VRLWNM, do_vrlnm, MO_32) +TRANS(VRLDNM, do_vrlnm, MO_64) + +static void gen_vrlmi_vec(unsigned vece, TCGv_vec vrt, TCGv_vec vra, + TCGv_vec vrb) +{ + TCGv_vec mask, n = tcg_temp_new_vec_matching(vrt), + tmp = tcg_temp_new_vec_matching(vrt); + + /* Create the mask */ + mask = do_vrl_mask_vec(vece, vrb); + + /* Extract n */ + tcg_gen_dupi_vec(vece, n, (8 << vece) - 1); + tcg_gen_and_vec(vece, n, vrb, n); + + /* Rotate and insert */ + tcg_gen_rotlv_vec(vece, tmp, vra, n); + tcg_gen_bitsel_vec(vece, vrt, mask, tmp, vrt); + + tcg_temp_free_vec(n); + tcg_temp_free_vec(tmp); + tcg_temp_free_vec(mask); +} + +static bool do_vrlmi(DisasContext *ctx, arg_VX *a, int vece) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_cmp_vec, INDEX_op_rotlv_vec, INDEX_op_sari_vec, + INDEX_op_shli_vec, INDEX_op_shri_vec, INDEX_op_shrv_vec, 0 + }; + static const GVecGen3 ops[2] = { + { + .fniv = gen_vrlmi_vec, + .fno = gen_helper_VRLWMI, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 + }, + { + .fniv = gen_vrlnm_vec, + .fno = gen_helper_VRLDMI, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 + } + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &ops[vece - 2]); + + return true; +} + +TRANS(VRLWMI, do_vrlmi, MO_32) +TRANS(VRLDMI, do_vrlmi, MO_64) + +static bool do_vector_shift_quad(DisasContext *ctx, arg_VX *a, bool right, + bool alg) +{ + TCGv_i64 hi, lo, t0, t1, n, zero = tcg_constant_i64(0); + + REQUIRE_VECTOR(ctx); + + n = tcg_temp_new_i64(); + hi = tcg_temp_new_i64(); + lo = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + t1 = tcg_const_i64(0); + + get_avr64(lo, a->vra, false); + get_avr64(hi, a->vra, true); + + get_avr64(n, a->vrb, true); + + tcg_gen_andi_i64(t0, n, 64); + if (right) { + tcg_gen_movcond_i64(TCG_COND_NE, lo, t0, zero, hi, lo); + if (alg) { + tcg_gen_sari_i64(t1, lo, 63); + } + tcg_gen_movcond_i64(TCG_COND_NE, hi, t0, zero, t1, hi); + } else { + tcg_gen_movcond_i64(TCG_COND_NE, hi, t0, zero, lo, hi); + tcg_gen_movcond_i64(TCG_COND_NE, lo, t0, zero, zero, lo); + } + tcg_gen_andi_i64(n, n, 0x3F); + + if (right) { + if (alg) { + tcg_gen_sar_i64(t0, hi, n); + } else { + tcg_gen_shr_i64(t0, hi, n); + } + } else { + tcg_gen_shl_i64(t0, lo, n); + } + set_avr64(a->vrt, t0, right); + + if (right) { + tcg_gen_shr_i64(lo, lo, n); + } else { + tcg_gen_shl_i64(hi, hi, n); + } + tcg_gen_xori_i64(n, n, 63); + if (right) { + tcg_gen_shl_i64(hi, hi, n); + tcg_gen_shli_i64(hi, hi, 1); + } else { + tcg_gen_shr_i64(lo, lo, n); + tcg_gen_shri_i64(lo, lo, 1); + } + tcg_gen_or_i64(hi, hi, lo); + set_avr64(a->vrt, hi, !right); + + tcg_temp_free_i64(hi); + tcg_temp_free_i64(lo); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(n); + + return true; +} + +TRANS_FLAGS2(ISA310, VSLQ, do_vector_shift_quad, false, false); +TRANS_FLAGS2(ISA310, VSRQ, do_vector_shift_quad, true, false); +TRANS_FLAGS2(ISA310, VSRAQ, do_vector_shift_quad, true, true); + +static void do_vrlq_mask(TCGv_i64 mh, TCGv_i64 ml, TCGv_i64 b, TCGv_i64 e) +{ + TCGv_i64 th, tl, t0, t1, zero = tcg_constant_i64(0), + ones = tcg_constant_i64(-1); + + th = tcg_temp_new_i64(); + tl = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + /* m = ~0 >> b */ + tcg_gen_andi_i64(t0, b, 64); + tcg_gen_movcond_i64(TCG_COND_NE, t1, t0, zero, zero, ones); + tcg_gen_andi_i64(t0, b, 0x3F); + tcg_gen_shr_i64(mh, t1, t0); + tcg_gen_shr_i64(ml, ones, t0); + tcg_gen_xori_i64(t0, t0, 63); + tcg_gen_shl_i64(t1, t1, t0); + tcg_gen_shli_i64(t1, t1, 1); + tcg_gen_or_i64(ml, t1, ml); + + /* t = ~0 >> e */ + tcg_gen_andi_i64(t0, e, 64); + tcg_gen_movcond_i64(TCG_COND_NE, t1, t0, zero, zero, ones); + tcg_gen_andi_i64(t0, e, 0x3F); + tcg_gen_shr_i64(th, t1, t0); + tcg_gen_shr_i64(tl, ones, t0); + tcg_gen_xori_i64(t0, t0, 63); + tcg_gen_shl_i64(t1, t1, t0); + tcg_gen_shli_i64(t1, t1, 1); + tcg_gen_or_i64(tl, t1, tl); + + /* t = t >> 1 */ + tcg_gen_extract2_i64(tl, tl, th, 1); + tcg_gen_shri_i64(th, th, 1); + + /* m = m ^ t */ + tcg_gen_xor_i64(mh, mh, th); + tcg_gen_xor_i64(ml, ml, tl); + + /* Negate the mask if begin > end */ + tcg_gen_movcond_i64(TCG_COND_GT, t0, b, e, ones, zero); + + tcg_gen_xor_i64(mh, mh, t0); + tcg_gen_xor_i64(ml, ml, t0); + + tcg_temp_free_i64(th); + tcg_temp_free_i64(tl); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +static bool do_vector_rotl_quad(DisasContext *ctx, arg_VX *a, bool mask, + bool insert) +{ + TCGv_i64 ah, al, vrb, n, t0, t1, zero = tcg_constant_i64(0); + + REQUIRE_VECTOR(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + + ah = tcg_temp_new_i64(); + al = tcg_temp_new_i64(); + vrb = tcg_temp_new_i64(); + n = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + get_avr64(ah, a->vra, true); + get_avr64(al, a->vra, false); + get_avr64(vrb, a->vrb, true); + + tcg_gen_mov_i64(t0, ah); + tcg_gen_andi_i64(t1, vrb, 64); + tcg_gen_movcond_i64(TCG_COND_NE, ah, t1, zero, al, ah); + tcg_gen_movcond_i64(TCG_COND_NE, al, t1, zero, t0, al); + tcg_gen_andi_i64(n, vrb, 0x3F); + + tcg_gen_shl_i64(t0, ah, n); + tcg_gen_shl_i64(t1, al, n); + + tcg_gen_xori_i64(n, n, 63); + + tcg_gen_shr_i64(al, al, n); + tcg_gen_shri_i64(al, al, 1); + tcg_gen_or_i64(t0, al, t0); + + tcg_gen_shr_i64(ah, ah, n); + tcg_gen_shri_i64(ah, ah, 1); + tcg_gen_or_i64(t1, ah, t1); + + if (mask || insert) { + tcg_gen_extract_i64(n, vrb, 8, 7); + tcg_gen_extract_i64(vrb, vrb, 16, 7); + + do_vrlq_mask(ah, al, vrb, n); + + tcg_gen_and_i64(t0, t0, ah); + tcg_gen_and_i64(t1, t1, al); + + if (insert) { + get_avr64(n, a->vrt, true); + get_avr64(vrb, a->vrt, false); + tcg_gen_andc_i64(n, n, ah); + tcg_gen_andc_i64(vrb, vrb, al); + tcg_gen_or_i64(t0, t0, n); + tcg_gen_or_i64(t1, t1, vrb); + } + } + + set_avr64(a->vrt, t0, true); + set_avr64(a->vrt, t1, false); + + tcg_temp_free_i64(ah); + tcg_temp_free_i64(al); + tcg_temp_free_i64(vrb); + tcg_temp_free_i64(n); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + + return true; +} + +TRANS(VRLQ, do_vector_rotl_quad, false, false) +TRANS(VRLQNM, do_vector_rotl_quad, true, false) +TRANS(VRLQMI, do_vector_rotl_quad, false, true) + #define GEN_VXFORM_SAT(NAME, VECE, NORM, SAT, OPC2, OPC3) \ static void glue(glue(gen_, NAME), _vec)(unsigned vece, TCGv_vec t, \ TCGv_vec sat, TCGv_vec a, \ @@ -909,20 +1246,7 @@ GEN_VXFORM3(vsubeuqm, 31, 0); GEN_VXFORM3(vsubecuq, 31, 0); GEN_VXFORM_DUAL(vsubeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ vsubecuq, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_V(vrlb, MO_8, tcg_gen_gvec_rotlv, 2, 0); -GEN_VXFORM_V(vrlh, MO_16, tcg_gen_gvec_rotlv, 2, 1); -GEN_VXFORM_V(vrlw, MO_32, tcg_gen_gvec_rotlv, 2, 2); -GEN_VXFORM(vrlwmi, 2, 2); -GEN_VXFORM_DUAL(vrlw, PPC_ALTIVEC, PPC_NONE, \ - vrlwmi, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_V(vrld, MO_64, tcg_gen_gvec_rotlv, 2, 3); -GEN_VXFORM(vrldmi, 2, 3); -GEN_VXFORM_DUAL(vrld, PPC_NONE, PPC2_ALTIVEC_207, \ - vrldmi, PPC_NONE, PPC2_ISA300) GEN_VXFORM_TRANS(vsl, 2, 7); -GEN_VXFORM(vrldnm, 2, 7); -GEN_VXFORM_DUAL(vsl, PPC_ALTIVEC, PPC_NONE, \ - vrldnm, PPC_NONE, PPC2_ISA300) GEN_VXFORM_TRANS(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); GEN_VXFORM_ENV(vpkuwum, 7, 1); @@ -1008,41 +1332,252 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ } \ } -GEN_VXRFORM(vcmpequb, 3, 0) -GEN_VXRFORM(vcmpequh, 3, 1) -GEN_VXRFORM(vcmpequw, 3, 2) -GEN_VXRFORM(vcmpequd, 3, 3) -GEN_VXRFORM(vcmpnezb, 3, 4) -GEN_VXRFORM(vcmpnezh, 3, 5) -GEN_VXRFORM(vcmpnezw, 3, 6) -GEN_VXRFORM(vcmpgtsb, 3, 12) -GEN_VXRFORM(vcmpgtsh, 3, 13) -GEN_VXRFORM(vcmpgtsw, 3, 14) -GEN_VXRFORM(vcmpgtsd, 3, 15) -GEN_VXRFORM(vcmpgtub, 3, 8) -GEN_VXRFORM(vcmpgtuh, 3, 9) -GEN_VXRFORM(vcmpgtuw, 3, 10) -GEN_VXRFORM(vcmpgtud, 3, 11) +static void do_vcmp_rc(int vrt) +{ + TCGv_i64 tmp, set, clr; + + tmp = tcg_temp_new_i64(); + set = tcg_temp_new_i64(); + clr = tcg_temp_new_i64(); + + get_avr64(tmp, vrt, true); + tcg_gen_mov_i64(set, tmp); + get_avr64(tmp, vrt, false); + tcg_gen_or_i64(clr, set, tmp); + tcg_gen_and_i64(set, set, tmp); + + tcg_gen_setcondi_i64(TCG_COND_EQ, clr, clr, 0); + tcg_gen_shli_i64(clr, clr, 1); + + tcg_gen_setcondi_i64(TCG_COND_EQ, set, set, -1); + tcg_gen_shli_i64(set, set, 3); + + tcg_gen_or_i64(tmp, set, clr); + tcg_gen_extrl_i64_i32(cpu_crf[6], tmp); + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(set); + tcg_temp_free_i64(clr); +} + +static bool do_vcmp(DisasContext *ctx, arg_VC *a, TCGCond cond, int vece) +{ + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_cmp(cond, vece, avr_full_offset(a->vrt), + avr_full_offset(a->vra), avr_full_offset(a->vrb), 16, 16); + + if (a->rc) { + do_vcmp_rc(a->vrt); + } + + return true; +} + +TRANS_FLAGS(ALTIVEC, VCMPEQUB, do_vcmp, TCG_COND_EQ, MO_8) +TRANS_FLAGS(ALTIVEC, VCMPEQUH, do_vcmp, TCG_COND_EQ, MO_16) +TRANS_FLAGS(ALTIVEC, VCMPEQUW, do_vcmp, TCG_COND_EQ, MO_32) +TRANS_FLAGS2(ALTIVEC_207, VCMPEQUD, do_vcmp, TCG_COND_EQ, MO_64) + +TRANS_FLAGS(ALTIVEC, VCMPGTSB, do_vcmp, TCG_COND_GT, MO_8) +TRANS_FLAGS(ALTIVEC, VCMPGTSH, do_vcmp, TCG_COND_GT, MO_16) +TRANS_FLAGS(ALTIVEC, VCMPGTSW, do_vcmp, TCG_COND_GT, MO_32) +TRANS_FLAGS2(ALTIVEC_207, VCMPGTSD, do_vcmp, TCG_COND_GT, MO_64) +TRANS_FLAGS(ALTIVEC, VCMPGTUB, do_vcmp, TCG_COND_GTU, MO_8) +TRANS_FLAGS(ALTIVEC, VCMPGTUH, do_vcmp, TCG_COND_GTU, MO_16) +TRANS_FLAGS(ALTIVEC, VCMPGTUW, do_vcmp, TCG_COND_GTU, MO_32) +TRANS_FLAGS2(ALTIVEC_207, VCMPGTUD, do_vcmp, TCG_COND_GTU, MO_64) + +TRANS_FLAGS2(ISA300, VCMPNEB, do_vcmp, TCG_COND_NE, MO_8) +TRANS_FLAGS2(ISA300, VCMPNEH, do_vcmp, TCG_COND_NE, MO_16) +TRANS_FLAGS2(ISA300, VCMPNEW, do_vcmp, TCG_COND_NE, MO_32) + +static void gen_vcmpnez_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t0, t1, zero; + + t0 = tcg_temp_new_vec_matching(t); + t1 = tcg_temp_new_vec_matching(t); + zero = tcg_constant_vec_matching(t, vece, 0); + + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t0, a, zero); + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t1, b, zero); + tcg_gen_cmp_vec(TCG_COND_NE, vece, t, a, b); + + tcg_gen_or_vec(vece, t, t, t0); + tcg_gen_or_vec(vece, t, t, t1); + + tcg_temp_free_vec(t0); + tcg_temp_free_vec(t1); +} + +static bool do_vcmpnez(DisasContext *ctx, arg_VC *a, int vece) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_cmp_vec, 0 + }; + static const GVecGen3 ops[3] = { + { + .fniv = gen_vcmpnez_vec, + .fno = gen_helper_VCMPNEZB, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vcmpnez_vec, + .fno = gen_helper_VCMPNEZH, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vcmpnez_vec, + .fno = gen_helper_VCMPNEZW, + .opt_opc = vecop_list, + .vece = MO_32 + } + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &ops[vece]); + + if (a->rc) { + do_vcmp_rc(a->vrt); + } + + return true; +} + +TRANS(VCMPNEZB, do_vcmpnez, MO_8) +TRANS(VCMPNEZH, do_vcmpnez, MO_16) +TRANS(VCMPNEZW, do_vcmpnez, MO_32) + +static bool trans_VCMPEQUQ(DisasContext *ctx, arg_VC *a) +{ + TCGv_i64 t0, t1, t2; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + + get_avr64(t0, a->vra, true); + get_avr64(t1, a->vrb, true); + tcg_gen_xor_i64(t2, t0, t1); + + get_avr64(t0, a->vra, false); + get_avr64(t1, a->vrb, false); + tcg_gen_xor_i64(t1, t0, t1); + + tcg_gen_or_i64(t1, t1, t2); + tcg_gen_setcondi_i64(TCG_COND_EQ, t1, t1, 0); + tcg_gen_neg_i64(t1, t1); + + set_avr64(a->vrt, t1, true); + set_avr64(a->vrt, t1, false); + + if (a->rc) { + tcg_gen_extrl_i64_i32(cpu_crf[6], t1); + tcg_gen_andi_i32(cpu_crf[6], cpu_crf[6], 0xa); + tcg_gen_xori_i32(cpu_crf[6], cpu_crf[6], 0x2); + } + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + + return true; +} + +static bool do_vcmpgtq(DisasContext *ctx, arg_VC *a, bool sign) +{ + TCGv_i64 t0, t1, t2; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + + get_avr64(t0, a->vra, false); + get_avr64(t1, a->vrb, false); + tcg_gen_setcond_i64(TCG_COND_GTU, t2, t0, t1); + + get_avr64(t0, a->vra, true); + get_avr64(t1, a->vrb, true); + tcg_gen_movcond_i64(TCG_COND_EQ, t2, t0, t1, t2, tcg_constant_i64(0)); + tcg_gen_setcond_i64(sign ? TCG_COND_GT : TCG_COND_GTU, t1, t0, t1); + + tcg_gen_or_i64(t1, t1, t2); + tcg_gen_neg_i64(t1, t1); + + set_avr64(a->vrt, t1, true); + set_avr64(a->vrt, t1, false); + + if (a->rc) { + tcg_gen_extrl_i64_i32(cpu_crf[6], t1); + tcg_gen_andi_i32(cpu_crf[6], cpu_crf[6], 0xa); + tcg_gen_xori_i32(cpu_crf[6], cpu_crf[6], 0x2); + } + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + + return true; +} + +TRANS(VCMPGTSQ, do_vcmpgtq, true) +TRANS(VCMPGTUQ, do_vcmpgtq, false) + +static bool do_vcmpq(DisasContext *ctx, arg_VX_bf *a, bool sign) +{ + TCGv_i64 vra, vrb; + TCGLabel *gt, *lt, *done; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + vra = tcg_temp_local_new_i64(); + vrb = tcg_temp_local_new_i64(); + gt = gen_new_label(); + lt = gen_new_label(); + done = gen_new_label(); + + get_avr64(vra, a->vra, true); + get_avr64(vrb, a->vrb, true); + tcg_gen_brcond_i64((sign ? TCG_COND_GT : TCG_COND_GTU), vra, vrb, gt); + tcg_gen_brcond_i64((sign ? TCG_COND_LT : TCG_COND_LTU), vra, vrb, lt); + + get_avr64(vra, a->vra, false); + get_avr64(vrb, a->vrb, false); + tcg_gen_brcond_i64(TCG_COND_GTU, vra, vrb, gt); + tcg_gen_brcond_i64(TCG_COND_LTU, vra, vrb, lt); + + tcg_gen_movi_i32(cpu_crf[a->bf], CRF_EQ); + tcg_gen_br(done); + + gen_set_label(gt); + tcg_gen_movi_i32(cpu_crf[a->bf], CRF_GT); + tcg_gen_br(done); + + gen_set_label(lt); + tcg_gen_movi_i32(cpu_crf[a->bf], CRF_LT); + tcg_gen_br(done); + + gen_set_label(done); + tcg_temp_free_i64(vra); + tcg_temp_free_i64(vrb); + + return true; +} + +TRANS(VCMPSQ, do_vcmpq, true) +TRANS(VCMPUQ, do_vcmpq, false) + GEN_VXRFORM(vcmpeqfp, 3, 3) GEN_VXRFORM(vcmpgefp, 3, 7) GEN_VXRFORM(vcmpgtfp, 3, 11) GEN_VXRFORM(vcmpbfp, 3, 15) -GEN_VXRFORM(vcmpneb, 3, 0) -GEN_VXRFORM(vcmpneh, 3, 1) -GEN_VXRFORM(vcmpnew, 3, 2) - -GEN_VXRFORM_DUAL(vcmpequb, PPC_ALTIVEC, PPC_NONE, \ - vcmpneb, PPC_NONE, PPC2_ISA300) -GEN_VXRFORM_DUAL(vcmpequh, PPC_ALTIVEC, PPC_NONE, \ - vcmpneh, PPC_NONE, PPC2_ISA300) -GEN_VXRFORM_DUAL(vcmpequw, PPC_ALTIVEC, PPC_NONE, \ - vcmpnew, PPC_NONE, PPC2_ISA300) -GEN_VXRFORM_DUAL(vcmpeqfp, PPC_ALTIVEC, PPC_NONE, \ - vcmpequd, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXRFORM_DUAL(vcmpbfp, PPC_ALTIVEC, PPC_NONE, \ - vcmpgtsd, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXRFORM_DUAL(vcmpgtfp, PPC_ALTIVEC, PPC_NONE, \ - vcmpgtud, PPC_NONE, PPC2_ALTIVEC_207) static void gen_vsplti(DisasContext *ctx, int vece) { @@ -1157,7 +1692,7 @@ static void gen_vsplt(DisasContext *ctx, int vece) /* Experimental testing shows that hardware masks the immediate. */ bofs += (uimm << vece) & 15; -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN bofs ^= 15; bofs &= ~((1 << vece) - 1); #endif @@ -1228,6 +1763,141 @@ GEN_VXFORM_DUAL(vsplth, PPC_ALTIVEC, PPC_NONE, GEN_VXFORM_DUAL(vspltw, PPC_ALTIVEC, PPC_NONE, vextractuw, PPC_NONE, PPC2_ISA300); +static bool trans_VGNB(DisasContext *ctx, arg_VX_n *a) +{ + /* + * Similar to do_vextractm, we'll use a sequence of mask-shift-or operations + * to gather the bits. The masks can be created with + * + * uint64_t mask(uint64_t n, uint64_t step) + * { + * uint64_t p = ((1UL << (1UL << step)) - 1UL) << ((n - 1UL) << step), + * plen = n << step, m = 0; + * for(int i = 0; i < 64/plen; i++) { + * m |= p; + * m = ror64(m, plen); + * } + * p >>= plen * DIV_ROUND_UP(64, plen) - 64; + * return m | p; + * } + * + * But since there are few values of N, we'll use a lookup table to avoid + * these calculations at runtime. + */ + static const uint64_t mask[6][5] = { + { + 0xAAAAAAAAAAAAAAAAULL, 0xccccccccccccccccULL, 0xf0f0f0f0f0f0f0f0ULL, + 0xff00ff00ff00ff00ULL, 0xffff0000ffff0000ULL + }, + { + 0x9249249249249249ULL, 0xC30C30C30C30C30CULL, 0xF00F00F00F00F00FULL, + 0xFF0000FF0000FF00ULL, 0xFFFF00000000FFFFULL + }, + { + /* For N >= 4, some mask operations can be elided */ + 0x8888888888888888ULL, 0, 0xf000f000f000f000ULL, 0, + 0xFFFF000000000000ULL + }, + { + 0x8421084210842108ULL, 0, 0xF0000F0000F0000FULL, 0, 0 + }, + { + 0x8208208208208208ULL, 0, 0xF00000F00000F000ULL, 0, 0 + }, + { + 0x8102040810204081ULL, 0, 0xF000000F000000F0ULL, 0, 0 + } + }; + uint64_t m; + int i, sh, nbits = DIV_ROUND_UP(64, a->n); + TCGv_i64 hi, lo, t0, t1; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + if (a->n < 2) { + /* + * "N can be any value between 2 and 7, inclusive." Otherwise, the + * result is undefined, so we don't need to change RT. Also, N > 7 is + * impossible since the immediate field is 3 bits only. + */ + return true; + } + + hi = tcg_temp_new_i64(); + lo = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + get_avr64(hi, a->vrb, true); + get_avr64(lo, a->vrb, false); + + /* Align the lower doubleword so we can use the same mask */ + tcg_gen_shli_i64(lo, lo, a->n * nbits - 64); + + /* + * Starting from the most significant bit, gather every Nth bit with a + * sequence of mask-shift-or operation. E.g.: for N=3 + * AxxBxxCxxDxxExxFxxGxxHxxIxxJxxKxxLxxMxxNxxOxxPxxQxxRxxSxxTxxUxxV + * & rep(0b100) + * A..B..C..D..E..F..G..H..I..J..K..L..M..N..O..P..Q..R..S..T..U..V + * << 2 + * .B..C..D..E..F..G..H..I..J..K..L..M..N..O..P..Q..R..S..T..U..V.. + * | + * AB.BC.CD.DE.EF.FG.GH.HI.IJ.JK.KL.LM.MN.NO.OP.PQ.QR.RS.ST.TU.UV.V + * & rep(0b110000) + * AB....CD....EF....GH....IJ....KL....MN....OP....QR....ST....UV.. + * << 4 + * ..CD....EF....GH....IJ....KL....MN....OP....QR....ST....UV...... + * | + * ABCD..CDEF..EFGH..GHIJ..IJKL..KLMN..MNOP..OPQR..QRST..STUV..UV.. + * & rep(0b111100000000) + * ABCD........EFGH........IJKL........MNOP........QRST........UV.. + * << 8 + * ....EFGH........IJKL........MNOP........QRST........UV.......... + * | + * ABCDEFGH....EFGHIJKL....IJKLMNOP....MNOPQRST....QRSTUV......UV.. + * & rep(0b111111110000000000000000) + * ABCDEFGH................IJKLMNOP................QRSTUV.......... + * << 16 + * ........IJKLMNOP................QRSTUV.......................... + * | + * ABCDEFGHIJKLMNOP........IJKLMNOPQRSTUV..........QRSTUV.......... + * & rep(0b111111111111111100000000000000000000000000000000) + * ABCDEFGHIJKLMNOP................................QRSTUV.......... + * << 32 + * ................QRSTUV.......................................... + * | + * ABCDEFGHIJKLMNOPQRSTUV..........................QRSTUV.......... + */ + for (i = 0, sh = a->n - 1; i < 5; i++, sh <<= 1) { + m = mask[a->n - 2][i]; + if (m) { + tcg_gen_andi_i64(hi, hi, m); + tcg_gen_andi_i64(lo, lo, m); + } + if (sh < 64) { + tcg_gen_shli_i64(t0, hi, sh); + tcg_gen_shli_i64(t1, lo, sh); + tcg_gen_or_i64(hi, t0, hi); + tcg_gen_or_i64(lo, t1, lo); + } + } + + tcg_gen_andi_i64(hi, hi, ~(~0ULL >> nbits)); + tcg_gen_andi_i64(lo, lo, ~(~0ULL >> nbits)); + tcg_gen_shri_i64(lo, lo, nbits); + tcg_gen_or_i64(hi, hi, lo); + tcg_gen_trunc_i64_tl(cpu_gpr[a->rt], hi); + + tcg_temp_free_i64(hi); + tcg_temp_free_i64(lo); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + + return true; +} + static bool do_vextdx(DisasContext *ctx, arg_VA *a, int size, bool right, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv)) { @@ -1722,6 +2392,124 @@ static bool trans_MTVSRBMI(DisasContext *ctx, arg_DX_b *a) return true; } +static bool do_vcntmb(DisasContext *ctx, arg_VX_mp *a, int vece) +{ + TCGv_i64 rt, vrb, mask; + rt = tcg_const_i64(0); + vrb = tcg_temp_new_i64(); + mask = tcg_constant_i64(dup_const(vece, 1ULL << ((8 << vece) - 1))); + + for (int i = 0; i < 2; i++) { + get_avr64(vrb, a->vrb, i); + if (a->mp) { + tcg_gen_and_i64(vrb, mask, vrb); + } else { + tcg_gen_andc_i64(vrb, mask, vrb); + } + tcg_gen_ctpop_i64(vrb, vrb); + tcg_gen_add_i64(rt, rt, vrb); + } + + tcg_gen_shli_i64(rt, rt, TARGET_LONG_BITS - 8 + vece); + tcg_gen_trunc_i64_tl(cpu_gpr[a->rt], rt); + + tcg_temp_free_i64(vrb); + tcg_temp_free_i64(rt); + + return true; +} + +TRANS(VCNTMBB, do_vcntmb, MO_8) +TRANS(VCNTMBH, do_vcntmb, MO_16) +TRANS(VCNTMBW, do_vcntmb, MO_32) +TRANS(VCNTMBD, do_vcntmb, MO_64) + +static bool do_vstri(DisasContext *ctx, arg_VX_tb_rc *a, + void (*gen_helper)(TCGv_i32, TCGv_ptr, TCGv_ptr)) +{ + TCGv_ptr vrt, vrb; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + vrt = gen_avr_ptr(a->vrt); + vrb = gen_avr_ptr(a->vrb); + + if (a->rc) { + gen_helper(cpu_crf[6], vrt, vrb); + } else { + TCGv_i32 discard = tcg_temp_new_i32(); + gen_helper(discard, vrt, vrb); + tcg_temp_free_i32(discard); + } + + tcg_temp_free_ptr(vrt); + tcg_temp_free_ptr(vrb); + + return true; +} + +TRANS(VSTRIBL, do_vstri, gen_helper_VSTRIBL) +TRANS(VSTRIBR, do_vstri, gen_helper_VSTRIBR) +TRANS(VSTRIHL, do_vstri, gen_helper_VSTRIHL) +TRANS(VSTRIHR, do_vstri, gen_helper_VSTRIHR) + +static bool do_vclrb(DisasContext *ctx, arg_VX *a, bool right) +{ + TCGv_i64 rb, mh, ml, tmp, + ones = tcg_constant_i64(-1), + zero = tcg_constant_i64(0); + + rb = tcg_temp_new_i64(); + mh = tcg_temp_new_i64(); + ml = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); + + tcg_gen_extu_tl_i64(rb, cpu_gpr[a->vrb]); + tcg_gen_andi_i64(tmp, rb, 7); + tcg_gen_shli_i64(tmp, tmp, 3); + if (right) { + tcg_gen_shr_i64(tmp, ones, tmp); + } else { + tcg_gen_shl_i64(tmp, ones, tmp); + } + tcg_gen_not_i64(tmp, tmp); + + if (right) { + tcg_gen_movcond_i64(TCG_COND_LTU, mh, rb, tcg_constant_i64(8), + tmp, ones); + tcg_gen_movcond_i64(TCG_COND_LTU, ml, rb, tcg_constant_i64(8), + zero, tmp); + tcg_gen_movcond_i64(TCG_COND_LTU, ml, rb, tcg_constant_i64(16), + ml, ones); + } else { + tcg_gen_movcond_i64(TCG_COND_LTU, ml, rb, tcg_constant_i64(8), + tmp, ones); + tcg_gen_movcond_i64(TCG_COND_LTU, mh, rb, tcg_constant_i64(8), + zero, tmp); + tcg_gen_movcond_i64(TCG_COND_LTU, mh, rb, tcg_constant_i64(16), + mh, ones); + } + + get_avr64(tmp, a->vra, true); + tcg_gen_and_i64(tmp, tmp, mh); + set_avr64(a->vrt, tmp, true); + + get_avr64(tmp, a->vra, false); + tcg_gen_and_i64(tmp, tmp, ml); + set_avr64(a->vrt, tmp, false); + + tcg_temp_free_i64(rb); + tcg_temp_free_i64(mh); + tcg_temp_free_i64(ml); + tcg_temp_free_i64(tmp); + + return true; +} + +TRANS(VCLRLB, do_vclrb, false) +TRANS(VCLRRB, do_vclrb, true) + #define GEN_VAFORM_PAIRED(name0, name1, opc2) \ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ { \ @@ -1765,28 +2553,65 @@ static void gen_vmladduhm(DisasContext *ctx) tcg_temp_free_ptr(rd); } -static void gen_vpermr(DisasContext *ctx) +static bool trans_VPERM(DisasContext *ctx, arg_VA *a) { - TCGv_ptr ra, rb, rc, rd; - if (unlikely(!ctx->altivec_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VPU); - return; - } - ra = gen_avr_ptr(rA(ctx->opcode)); - rb = gen_avr_ptr(rB(ctx->opcode)); - rc = gen_avr_ptr(rC(ctx->opcode)); - rd = gen_avr_ptr(rD(ctx->opcode)); - gen_helper_vpermr(cpu_env, rd, ra, rb, rc); - tcg_temp_free_ptr(ra); - tcg_temp_free_ptr(rb); - tcg_temp_free_ptr(rc); - tcg_temp_free_ptr(rd); + TCGv_ptr vrt, vra, vrb, vrc; + + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + + vrt = gen_avr_ptr(a->vrt); + vra = gen_avr_ptr(a->vra); + vrb = gen_avr_ptr(a->vrb); + vrc = gen_avr_ptr(a->rc); + + gen_helper_VPERM(vrt, vra, vrb, vrc); + + tcg_temp_free_ptr(vrt); + tcg_temp_free_ptr(vra); + tcg_temp_free_ptr(vrb); + tcg_temp_free_ptr(vrc); + + return true; +} + +static bool trans_VPERMR(DisasContext *ctx, arg_VA *a) +{ + TCGv_ptr vrt, vra, vrb, vrc; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VECTOR(ctx); + + vrt = gen_avr_ptr(a->vrt); + vra = gen_avr_ptr(a->vra); + vrb = gen_avr_ptr(a->vrb); + vrc = gen_avr_ptr(a->rc); + + gen_helper_VPERMR(vrt, vra, vrb, vrc); + + tcg_temp_free_ptr(vrt); + tcg_temp_free_ptr(vra); + tcg_temp_free_ptr(vrb); + tcg_temp_free_ptr(vrc); + + return true; +} + +static bool trans_VSEL(DisasContext *ctx, arg_VA *a) +{ + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_bitsel(MO_64, avr_full_offset(a->vrt), avr_full_offset(a->rc), + avr_full_offset(a->vrb), avr_full_offset(a->vra), + 16, 16); + + return true; } GEN_VAFORM_PAIRED(vmsumubm, vmsummbm, 18) GEN_VAFORM_PAIRED(vmsumuhm, vmsumuhs, 19) GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20) -GEN_VAFORM_PAIRED(vsel, vperm, 21) GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23) GEN_VXFORM_NOA(vclzb, 1, 28) @@ -1795,11 +2620,77 @@ GEN_VXFORM_TRANS(vclzw, 1, 30) GEN_VXFORM_TRANS(vclzd, 1, 31) GEN_VXFORM_NOA_2(vnegw, 1, 24, 6) GEN_VXFORM_NOA_2(vnegd, 1, 24, 7) -GEN_VXFORM_NOA_2(vextsb2w, 1, 24, 16) -GEN_VXFORM_NOA_2(vextsh2w, 1, 24, 17) -GEN_VXFORM_NOA_2(vextsb2d, 1, 24, 24) -GEN_VXFORM_NOA_2(vextsh2d, 1, 24, 25) -GEN_VXFORM_NOA_2(vextsw2d, 1, 24, 26) + +static void gen_vexts_i64(TCGv_i64 t, TCGv_i64 b, int64_t s) +{ + tcg_gen_sextract_i64(t, b, 0, 64 - s); +} + +static void gen_vexts_i32(TCGv_i32 t, TCGv_i32 b, int32_t s) +{ + tcg_gen_sextract_i32(t, b, 0, 32 - s); +} + +static void gen_vexts_vec(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t s) +{ + tcg_gen_shli_vec(vece, t, b, s); + tcg_gen_sari_vec(vece, t, t, s); +} + +static bool do_vexts(DisasContext *ctx, arg_VX_tb *a, unsigned vece, int64_t s) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, 0 + }; + + static const GVecGen2i op[2] = { + { + .fni4 = gen_vexts_i32, + .fniv = gen_vexts_vec, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vexts_i64, + .fniv = gen_vexts_vec, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_2i(avr_full_offset(a->vrt), avr_full_offset(a->vrb), + 16, 16, s, &op[vece - MO_32]); + + return true; +} + +TRANS(VEXTSB2W, do_vexts, MO_32, 24); +TRANS(VEXTSH2W, do_vexts, MO_32, 16); +TRANS(VEXTSB2D, do_vexts, MO_64, 56); +TRANS(VEXTSH2D, do_vexts, MO_64, 48); +TRANS(VEXTSW2D, do_vexts, MO_64, 32); + +static bool trans_VEXTSD2Q(DisasContext *ctx, arg_VX_tb *a) +{ + TCGv_i64 tmp; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tmp = tcg_temp_new_i64(); + + get_avr64(tmp, a->vrb, false); + set_avr64(a->vrt, tmp, false); + tcg_gen_sari_i64(tmp, tmp, 63); + set_avr64(a->vrt, tmp, true); + + tcg_temp_free_i64(tmp); + return true; +} + GEN_VXFORM_NOA_2(vctzb, 1, 24, 28) GEN_VXFORM_NOA_2(vctzh, 1, 24, 29) GEN_VXFORM_NOA_2(vctzw, 1, 24, 30) @@ -2104,6 +2995,247 @@ static bool trans_VPEXTD(DisasContext *ctx, arg_VX *a) return true; } +static bool trans_VMSUMUDM(DisasContext *ctx, arg_VA *a) +{ + TCGv_i64 rl, rh, src1, src2; + int dw; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VECTOR(ctx); + + rh = tcg_temp_new_i64(); + rl = tcg_temp_new_i64(); + src1 = tcg_temp_new_i64(); + src2 = tcg_temp_new_i64(); + + get_avr64(rl, a->rc, false); + get_avr64(rh, a->rc, true); + + for (dw = 0; dw < 2; dw++) { + get_avr64(src1, a->vra, dw); + get_avr64(src2, a->vrb, dw); + tcg_gen_mulu2_i64(src1, src2, src1, src2); + tcg_gen_add2_i64(rl, rh, rl, rh, src1, src2); + } + + set_avr64(a->vrt, rl, false); + set_avr64(a->vrt, rh, true); + + tcg_temp_free_i64(rl); + tcg_temp_free_i64(rh); + tcg_temp_free_i64(src1); + tcg_temp_free_i64(src2); + + return true; +} + +static bool trans_VMSUMCUD(DisasContext *ctx, arg_VA *a) +{ + TCGv_i64 tmp0, tmp1, prod1h, prod1l, prod0h, prod0l, zero; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tmp0 = tcg_temp_new_i64(); + tmp1 = tcg_temp_new_i64(); + prod1h = tcg_temp_new_i64(); + prod1l = tcg_temp_new_i64(); + prod0h = tcg_temp_new_i64(); + prod0l = tcg_temp_new_i64(); + zero = tcg_constant_i64(0); + + /* prod1 = vsr[vra+32].dw[1] * vsr[vrb+32].dw[1] */ + get_avr64(tmp0, a->vra, false); + get_avr64(tmp1, a->vrb, false); + tcg_gen_mulu2_i64(prod1l, prod1h, tmp0, tmp1); + + /* prod0 = vsr[vra+32].dw[0] * vsr[vrb+32].dw[0] */ + get_avr64(tmp0, a->vra, true); + get_avr64(tmp1, a->vrb, true); + tcg_gen_mulu2_i64(prod0l, prod0h, tmp0, tmp1); + + /* Sum lower 64-bits elements */ + get_avr64(tmp1, a->rc, false); + tcg_gen_add2_i64(tmp1, tmp0, tmp1, zero, prod1l, zero); + tcg_gen_add2_i64(tmp1, tmp0, tmp1, tmp0, prod0l, zero); + + /* + * Discard lower 64-bits, leaving the carry into bit 64. + * Then sum the higher 64-bit elements. + */ + get_avr64(tmp1, a->rc, true); + tcg_gen_add2_i64(tmp1, tmp0, tmp0, zero, tmp1, zero); + tcg_gen_add2_i64(tmp1, tmp0, tmp1, tmp0, prod1h, zero); + tcg_gen_add2_i64(tmp1, tmp0, tmp1, tmp0, prod0h, zero); + + /* Discard 64 more bits to complete the CHOP128(temp >> 128) */ + set_avr64(a->vrt, tmp0, false); + set_avr64(a->vrt, zero, true); + + tcg_temp_free_i64(tmp0); + tcg_temp_free_i64(tmp1); + tcg_temp_free_i64(prod1h); + tcg_temp_free_i64(prod1l); + tcg_temp_free_i64(prod0h); + tcg_temp_free_i64(prod0l); + + return true; +} + +static bool do_vx_helper(DisasContext *ctx, arg_VX *a, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + TCGv_ptr ra, rb, rd; + REQUIRE_VECTOR(ctx); + + ra = gen_avr_ptr(a->vra); + rb = gen_avr_ptr(a->vrb); + rd = gen_avr_ptr(a->vrt); + gen_helper(rd, ra, rb); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rb); + tcg_temp_free_ptr(rd); + + return true; +} + +static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even, + void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 vra, vrb, vrt0, vrt1; + REQUIRE_VECTOR(ctx); + + vra = tcg_temp_new_i64(); + vrb = tcg_temp_new_i64(); + vrt0 = tcg_temp_new_i64(); + vrt1 = tcg_temp_new_i64(); + + get_avr64(vra, a->vra, even); + get_avr64(vrb, a->vrb, even); + gen_mul(vrt0, vrt1, vra, vrb); + set_avr64(a->vrt, vrt0, false); + set_avr64(a->vrt, vrt1, true); + + tcg_temp_free_i64(vra); + tcg_temp_free_i64(vrb); + tcg_temp_free_i64(vrt0); + tcg_temp_free_i64(vrt1); + + return true; +} + +static bool trans_VMULLD(DisasContext *ctx, arg_VX *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_mul(MO_64, avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16); + + return true; +} + +TRANS_FLAGS(ALTIVEC, VMULESB, do_vx_helper, gen_helper_VMULESB) +TRANS_FLAGS(ALTIVEC, VMULOSB, do_vx_helper, gen_helper_VMULOSB) +TRANS_FLAGS(ALTIVEC, VMULEUB, do_vx_helper, gen_helper_VMULEUB) +TRANS_FLAGS(ALTIVEC, VMULOUB, do_vx_helper, gen_helper_VMULOUB) +TRANS_FLAGS(ALTIVEC, VMULESH, do_vx_helper, gen_helper_VMULESH) +TRANS_FLAGS(ALTIVEC, VMULOSH, do_vx_helper, gen_helper_VMULOSH) +TRANS_FLAGS(ALTIVEC, VMULEUH, do_vx_helper, gen_helper_VMULEUH) +TRANS_FLAGS(ALTIVEC, VMULOUH, do_vx_helper, gen_helper_VMULOUH) +TRANS_FLAGS2(ALTIVEC_207, VMULESW, do_vx_helper, gen_helper_VMULESW) +TRANS_FLAGS2(ALTIVEC_207, VMULOSW, do_vx_helper, gen_helper_VMULOSW) +TRANS_FLAGS2(ALTIVEC_207, VMULEUW, do_vx_helper, gen_helper_VMULEUW) +TRANS_FLAGS2(ALTIVEC_207, VMULOUW, do_vx_helper, gen_helper_VMULOUW) +TRANS_FLAGS2(ISA310, VMULESD, do_vx_vmuleo, true , tcg_gen_muls2_i64) +TRANS_FLAGS2(ISA310, VMULOSD, do_vx_vmuleo, false, tcg_gen_muls2_i64) +TRANS_FLAGS2(ISA310, VMULEUD, do_vx_vmuleo, true , tcg_gen_mulu2_i64) +TRANS_FLAGS2(ISA310, VMULOUD, do_vx_vmuleo, false, tcg_gen_mulu2_i64) + +static void do_vx_vmulhw_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, bool sign) +{ + TCGv_i64 hh, lh, temp; + + hh = tcg_temp_new_i64(); + lh = tcg_temp_new_i64(); + temp = tcg_temp_new_i64(); + + if (sign) { + tcg_gen_ext32s_i64(lh, a); + tcg_gen_ext32s_i64(temp, b); + } else { + tcg_gen_ext32u_i64(lh, a); + tcg_gen_ext32u_i64(temp, b); + } + tcg_gen_mul_i64(lh, lh, temp); + + if (sign) { + tcg_gen_sari_i64(hh, a, 32); + tcg_gen_sari_i64(temp, b, 32); + } else { + tcg_gen_shri_i64(hh, a, 32); + tcg_gen_shri_i64(temp, b, 32); + } + tcg_gen_mul_i64(hh, hh, temp); + + tcg_gen_shri_i64(lh, lh, 32); + tcg_gen_deposit_i64(t, hh, lh, 0, 32); + + tcg_temp_free_i64(hh); + tcg_temp_free_i64(lh); + tcg_temp_free_i64(temp); +} + +static void do_vx_vmulhd_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, bool sign) +{ + TCGv_i64 tlow; + + tlow = tcg_temp_new_i64(); + if (sign) { + tcg_gen_muls2_i64(tlow, t, a, b); + } else { + tcg_gen_mulu2_i64(tlow, t, a, b); + } + + tcg_temp_free_i64(tlow); +} + +static bool do_vx_mulh(DisasContext *ctx, arg_VX *a, bool sign, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, bool)) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); + + TCGv_i64 vra, vrb, vrt; + int i; + + vra = tcg_temp_new_i64(); + vrb = tcg_temp_new_i64(); + vrt = tcg_temp_new_i64(); + + for (i = 0; i < 2; i++) { + get_avr64(vra, a->vra, i); + get_avr64(vrb, a->vrb, i); + get_avr64(vrt, a->vrt, i); + + func(vrt, vra, vrb, sign); + + set_avr64(a->vrt, vrt, i); + } + + tcg_temp_free_i64(vra); + tcg_temp_free_i64(vrb); + tcg_temp_free_i64(vrt); + + return true; + +} + +TRANS(VMULHSW, do_vx_mulh, true , do_vx_vmulhw_i64) +TRANS(VMULHSD, do_vx_mulh, true , do_vx_vmulhd_i64) +TRANS(VMULHUW, do_vx_mulh, false, do_vx_vmulhw_i64) +TRANS(VMULHUD, do_vx_mulh, false, do_vx_vmulhd_i64) + #undef GEN_VR_LDX #undef GEN_VR_STX #undef GEN_VR_LVE diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 25ee715b43..d960648d52 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -101,33 +101,7 @@ GEN_VXFORM_DUAL(vmrgow, vextuwlx, 6, 26, PPC_NONE, PPC2_ALTIVEC_207), GEN_VXFORM_300(vextubrx, 6, 28), GEN_VXFORM_300(vextuhrx, 6, 29), GEN_VXFORM_DUAL(vmrgew, vextuwrx, 6, 30, PPC_NONE, PPC2_ALTIVEC_207), -GEN_VXFORM(vmuloub, 4, 0), -GEN_VXFORM(vmulouh, 4, 1), -GEN_VXFORM_DUAL(vmulouw, vmuluwm, 4, 2, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vmulosb, 4, 4), -GEN_VXFORM(vmulosh, 4, 5), -GEN_VXFORM_207(vmulosw, 4, 6), -GEN_VXFORM_310(vmulld, 4, 7), -GEN_VXFORM(vmuleub, 4, 8), -GEN_VXFORM(vmuleuh, 4, 9), -GEN_VXFORM_DUAL(vmuleuw, vmulhuw, 4, 10, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_310(vmulhud, 4, 11), -GEN_VXFORM(vmulesb, 4, 12), -GEN_VXFORM(vmulesh, 4, 13), -GEN_VXFORM_DUAL(vmulesw, vmulhsw, 4, 14, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_310(vmulhsd, 4, 15), -GEN_VXFORM(vslb, 2, 4), -GEN_VXFORM(vslh, 2, 5), -GEN_VXFORM_DUAL(vslw, vrlwnm, 2, 6, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_207(vsld, 2, 23), -GEN_VXFORM(vsrb, 2, 8), -GEN_VXFORM(vsrh, 2, 9), -GEN_VXFORM(vsrw, 2, 10), -GEN_VXFORM_207(vsrd, 2, 27), -GEN_VXFORM(vsrab, 2, 12), -GEN_VXFORM(vsrah, 2, 13), -GEN_VXFORM(vsraw, 2, 14), -GEN_VXFORM_207(vsrad, 2, 15), +GEN_VXFORM_207(vmuluwm, 4, 2), GEN_VXFORM_300(vsrv, 2, 28), GEN_VXFORM_300(vslv, 2, 29), GEN_VXFORM(vslo, 6, 16), @@ -158,11 +132,7 @@ GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), GEN_VXFORM_DUAL(vsubuqm, bcdtrunc, 0, 20, PPC2_ALTIVEC_207, PPC2_ISA300), GEN_VXFORM_DUAL(vsubcuq, bcdutrunc, 0, 21, PPC2_ALTIVEC_207, PPC2_ISA300), GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), -GEN_VXFORM(vrlb, 2, 0), -GEN_VXFORM(vrlh, 2, 1), -GEN_VXFORM_DUAL(vrlw, vrlwmi, 2, 2, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vrld, vrldmi, 2, 3, PPC_NONE, PPC2_ALTIVEC_207), -GEN_VXFORM_DUAL(vsl, vrldnm, 2, 7, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vsl, 2, 7), GEN_VXFORM(vsr, 2, 11), GEN_VXFORM(vpkuhum, 7, 0), GEN_VXFORM(vpkuwum, 7, 1), @@ -198,22 +168,10 @@ GEN_HANDLER2_E(name, str, 0x4, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ISA300), GEN_VXRFORM1_300(name, name, #name, opc2, opc3) \ GEN_VXRFORM1_300(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4))) -GEN_VXRFORM_300(vcmpnezb, 3, 4) -GEN_VXRFORM_300(vcmpnezh, 3, 5) -GEN_VXRFORM_300(vcmpnezw, 3, 6) -GEN_VXRFORM(vcmpgtsb, 3, 12) -GEN_VXRFORM(vcmpgtsh, 3, 13) -GEN_VXRFORM(vcmpgtsw, 3, 14) -GEN_VXRFORM(vcmpgtub, 3, 8) -GEN_VXRFORM(vcmpgtuh, 3, 9) -GEN_VXRFORM(vcmpgtuw, 3, 10) -GEN_VXRFORM_DUAL(vcmpeqfp, vcmpequd, 3, 3, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM(vcmpeqfp, 3, 3) GEN_VXRFORM(vcmpgefp, 3, 7) -GEN_VXRFORM_DUAL(vcmpgtfp, vcmpgtud, 3, 11, PPC_ALTIVEC, PPC_NONE) -GEN_VXRFORM_DUAL(vcmpbfp, vcmpgtsd, 3, 15, PPC_ALTIVEC, PPC_NONE) -GEN_VXRFORM_DUAL(vcmpequb, vcmpneb, 3, 0, PPC_ALTIVEC, PPC_NONE) -GEN_VXRFORM_DUAL(vcmpequh, vcmpneh, 3, 1, PPC_ALTIVEC, PPC_NONE) -GEN_VXRFORM_DUAL(vcmpequw, vcmpnew, 3, 2, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM(vcmpgtfp, 3, 11) +GEN_VXRFORM(vcmpbfp, 3, 15) #define GEN_VXFORM_DUAL_INV(name0, name1, opc2, opc3, inval0, inval1, type) \ GEN_OPCODE_DUAL(name0##_##name1, 0x04, opc2, opc3, inval0, inval1, type, \ @@ -230,18 +188,12 @@ GEN_VXFORM(vspltish, 6, 13), GEN_VXFORM(vspltisw, 6, 14), GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06), GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07), -GEN_VXFORM_300_EO(vextsb2w, 0x01, 0x18, 0x10), -GEN_VXFORM_300_EO(vextsh2w, 0x01, 0x18, 0x11), -GEN_VXFORM_300_EO(vextsb2d, 0x01, 0x18, 0x18), -GEN_VXFORM_300_EO(vextsh2d, 0x01, 0x18, 0x19), -GEN_VXFORM_300_EO(vextsw2d, 0x01, 0x18, 0x1A), GEN_VXFORM_300_EO(vctzb, 0x01, 0x18, 0x1C), GEN_VXFORM_300_EO(vctzh, 0x01, 0x18, 0x1D), GEN_VXFORM_300_EO(vctzw, 0x01, 0x18, 0x1E), GEN_VXFORM_300_EO(vctzd, 0x01, 0x18, 0x1F), GEN_VXFORM_300_EO(vclzlsbb, 0x01, 0x18, 0x0), GEN_VXFORM_300_EO(vctzlsbb, 0x01, 0x18, 0x1), -GEN_VXFORM_300(vpermr, 0x1D, 0xFF), #define GEN_VXFORM_NOA(name, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x001f0000, PPC_ALTIVEC) @@ -276,7 +228,6 @@ GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16), GEN_VAFORM_PAIRED(vmsumubm, vmsummbm, 18), GEN_VAFORM_PAIRED(vmsumuhm, vmsumuhs, 19), GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20), -GEN_VAFORM_PAIRED(vsel, vperm, 21), GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23), GEN_VXFORM_DUAL(vclzb, vpopcntb, 1, 28, PPC_NONE, PPC2_ALTIVEC_207), diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 128968b5e7..3692740736 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -288,30 +288,6 @@ VSX_VECTOR_LOAD_STORE_LENGTH(stxvl) VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) #endif -#define VSX_LOAD_SCALAR_DS(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 xth; \ - \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - xth = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_imm_index(ctx, EA, 0x03); \ - gen_qemu_##operation(ctx, xth, EA); \ - set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); \ - /* NOTE: cpu_vsrl is undefined */ \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(xth); \ -} - -VSX_LOAD_SCALAR_DS(lxsd, ld64_i64) -VSX_LOAD_SCALAR_DS(lxssp, ld32fs) - #define VSX_STORE_SCALAR(name, operation) \ static void gen_##name(DisasContext *ctx) \ { \ @@ -461,30 +437,6 @@ static void gen_stxvb16x(DisasContext *ctx) tcg_temp_free_i64(xsl); } -#define VSX_STORE_SCALAR_DS(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 xth; \ - \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - xth = tcg_temp_new_i64(); \ - get_cpu_vsr(xth, rD(ctx->opcode) + 32, true); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_imm_index(ctx, EA, 0x03); \ - gen_qemu_##operation(ctx, xth, EA); \ - /* NOTE: cpu_vsrl is undefined */ \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(xth); \ -} - -VSX_STORE_SCALAR_DS(stxsd, st64_i64) -VSX_STORE_SCALAR_DS(stxssp, st32fs) - static void gen_mfvsrwz(DisasContext *ctx) { if (xS(ctx->opcode) < 32) { @@ -665,45 +617,6 @@ static void gen_mtvsrws(DisasContext *ctx) #endif -static void gen_xxpermdi(DisasContext *ctx) -{ - TCGv_i64 xh, xl; - - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - - xh = tcg_temp_new_i64(); - xl = tcg_temp_new_i64(); - - if (unlikely((xT(ctx->opcode) == xA(ctx->opcode)) || - (xT(ctx->opcode) == xB(ctx->opcode)))) { - get_cpu_vsr(xh, xA(ctx->opcode), (DM(ctx->opcode) & 2) == 0); - get_cpu_vsr(xl, xB(ctx->opcode), (DM(ctx->opcode) & 1) == 0); - - set_cpu_vsr(xT(ctx->opcode), xh, true); - set_cpu_vsr(xT(ctx->opcode), xl, false); - } else { - if ((DM(ctx->opcode) & 2) == 0) { - get_cpu_vsr(xh, xA(ctx->opcode), true); - set_cpu_vsr(xT(ctx->opcode), xh, true); - } else { - get_cpu_vsr(xh, xA(ctx->opcode), false); - set_cpu_vsr(xT(ctx->opcode), xh, true); - } - if ((DM(ctx->opcode) & 1) == 0) { - get_cpu_vsr(xl, xB(ctx->opcode), true); - set_cpu_vsr(xT(ctx->opcode), xl, false); - } else { - get_cpu_vsr(xl, xB(ctx->opcode), false); - set_cpu_vsr(xT(ctx->opcode), xl, false); - } - } - tcg_temp_free_i64(xh); - tcg_temp_free_i64(xl); -} - #define OP_ABS 1 #define OP_NABS 2 #define OP_NEG 3 @@ -925,6 +838,28 @@ static bool trans_XSCVQPDP(DisasContext *ctx, arg_X_tb_rc *a) return true; } +static bool do_helper_env_X_tb(DisasContext *ctx, arg_X_tb *a, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + TCGv_ptr xt, xb; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + xt = gen_avr_ptr(a->rt); + xb = gen_avr_ptr(a->rb); + gen_helper(cpu_env, xt, xb); + tcg_temp_free_ptr(xt); + tcg_temp_free_ptr(xb); + + return true; +} + +TRANS(XSCVUQQP, do_helper_env_X_tb, gen_helper_XSCVUQQP) +TRANS(XSCVSQQP, do_helper_env_X_tb, gen_helper_XSCVSQQP) +TRANS(XSCVQPUQZ, do_helper_env_X_tb, gen_helper_XSCVQPUQZ) +TRANS(XSCVQPSQZ, do_helper_env_X_tb, gen_helper_XSCVQPSQZ) + #define GEN_VSX_HELPER_2(name, op1, op2, inval, type) \ static void gen_##name(DisasContext *ctx) \ { \ @@ -1091,10 +1026,6 @@ GEN_VSX_HELPER_X2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xsrsqrtedp, 0x14, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xstdivdp, 0x14, 0x07, 0, PPC2_VSX) GEN_VSX_HELPER_X1(xstsqrtdp, 0x14, 0x06, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xscmpeqdp, 0x0C, 0x00, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xscmpgtdp, 0x0C, 0x01, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xscmpgedp, 0x0C, 0x02, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xscmpnedp, 0x0C, 0x03, 0, PPC2_ISA300) GEN_VSX_HELPER_X2_AB(xscmpexpdp, 0x0C, 0x07, 0, PPC2_ISA300) GEN_VSX_HELPER_R2_AB(xscmpexpqp, 0x04, 0x05, 0, PPC2_ISA300) GEN_VSX_HELPER_X2_AB(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) @@ -1200,47 +1131,248 @@ GEN_VSX_HELPER_X2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvtstdcsp, 0x14, 0x1A, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvtstdcdp, 0x14, 0x1E, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xxperm, 0x08, 0x03, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xxpermr, 0x08, 0x07, 0, PPC2_ISA300) + +static bool trans_XXPERM(DisasContext *ctx, arg_XX3 *a) +{ + TCGv_ptr xt, xa, xb; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); + + xt = gen_vsr_ptr(a->xt); + xa = gen_vsr_ptr(a->xa); + xb = gen_vsr_ptr(a->xb); + + gen_helper_VPERM(xt, xa, xt, xb); + + tcg_temp_free_ptr(xt); + tcg_temp_free_ptr(xa); + tcg_temp_free_ptr(xb); + + return true; +} + +static bool trans_XXPERMR(DisasContext *ctx, arg_XX3 *a) +{ + TCGv_ptr xt, xa, xb; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); + + xt = gen_vsr_ptr(a->xt); + xa = gen_vsr_ptr(a->xa); + xb = gen_vsr_ptr(a->xb); + + gen_helper_VPERMR(xt, xa, xt, xb); + + tcg_temp_free_ptr(xt); + tcg_temp_free_ptr(xa); + tcg_temp_free_ptr(xb); + + return true; +} + +static bool trans_XXPERMDI(DisasContext *ctx, arg_XX3_dm *a) +{ + TCGv_i64 t0, t1; + + REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); + + t0 = tcg_temp_new_i64(); + + if (unlikely(a->xt == a->xa || a->xt == a->xb)) { + t1 = tcg_temp_new_i64(); + + get_cpu_vsr(t0, a->xa, (a->dm & 2) == 0); + get_cpu_vsr(t1, a->xb, (a->dm & 1) == 0); + + set_cpu_vsr(a->xt, t0, true); + set_cpu_vsr(a->xt, t1, false); + + tcg_temp_free_i64(t1); + } else { + get_cpu_vsr(t0, a->xa, (a->dm & 2) == 0); + set_cpu_vsr(a->xt, t0, true); + + get_cpu_vsr(t0, a->xb, (a->dm & 1) == 0); + set_cpu_vsr(a->xt, t0, false); + } + + tcg_temp_free_i64(t0); + + return true; +} + +static bool trans_XXPERMX(DisasContext *ctx, arg_8RR_XX4_uim3 *a) +{ + TCGv_ptr xt, xa, xb, xc; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + xt = gen_vsr_ptr(a->xt); + xa = gen_vsr_ptr(a->xa); + xb = gen_vsr_ptr(a->xb); + xc = gen_vsr_ptr(a->xc); + + gen_helper_XXPERMX(xt, xa, xb, xc, tcg_constant_tl(a->uim3)); + + tcg_temp_free_ptr(xt); + tcg_temp_free_ptr(xa); + tcg_temp_free_ptr(xb); + tcg_temp_free_ptr(xc); + + return true; +} + +typedef void (*xxgenpcv_genfn)(TCGv_ptr, TCGv_ptr); + +static bool do_xxgenpcv(DisasContext *ctx, arg_X_imm5 *a, + const xxgenpcv_genfn fn[4]) +{ + TCGv_ptr xt, vrb; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + if (a->imm & ~0x3) { + gen_invalid(ctx); + return true; + } + + xt = gen_vsr_ptr(a->xt); + vrb = gen_avr_ptr(a->vrb); + + fn[a->imm](xt, vrb); + + tcg_temp_free_ptr(xt); + tcg_temp_free_ptr(vrb); + + return true; +} + +#define XXGENPCV(NAME) \ + static bool trans_##NAME(DisasContext *ctx, arg_X_imm5 *a) \ + { \ + static const xxgenpcv_genfn fn[4] = { \ + gen_helper_##NAME##_be_exp, \ + gen_helper_##NAME##_be_comp, \ + gen_helper_##NAME##_le_exp, \ + gen_helper_##NAME##_le_comp, \ + }; \ + return do_xxgenpcv(ctx, a, fn); \ + } + +XXGENPCV(XXGENPCVBM) +XXGENPCV(XXGENPCVHM) +XXGENPCV(XXGENPCVWM) +XXGENPCV(XXGENPCVDM) +#undef XXGENPCV + +static bool do_xsmadd(DisasContext *ctx, int tgt, int src1, int src2, int src3, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + TCGv_ptr t, s1, s2, s3; + + t = gen_vsr_ptr(tgt); + s1 = gen_vsr_ptr(src1); + s2 = gen_vsr_ptr(src2); + s3 = gen_vsr_ptr(src3); + + gen_helper(cpu_env, t, s1, s2, s3); + + tcg_temp_free_ptr(t); + tcg_temp_free_ptr(s1); + tcg_temp_free_ptr(s2); + tcg_temp_free_ptr(s3); + + return true; +} + +static bool do_xsmadd_XX3(DisasContext *ctx, arg_XX3 *a, bool type_a, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + REQUIRE_VSX(ctx); + + if (type_a) { + return do_xsmadd(ctx, a->xt, a->xa, a->xt, a->xb, gen_helper); + } + return do_xsmadd(ctx, a->xt, a->xa, a->xb, a->xt, gen_helper); +} + +TRANS_FLAGS2(VSX, XSMADDADP, do_xsmadd_XX3, true, gen_helper_XSMADDDP) +TRANS_FLAGS2(VSX, XSMADDMDP, do_xsmadd_XX3, false, gen_helper_XSMADDDP) +TRANS_FLAGS2(VSX, XSMSUBADP, do_xsmadd_XX3, true, gen_helper_XSMSUBDP) +TRANS_FLAGS2(VSX, XSMSUBMDP, do_xsmadd_XX3, false, gen_helper_XSMSUBDP) +TRANS_FLAGS2(VSX, XSNMADDADP, do_xsmadd_XX3, true, gen_helper_XSNMADDDP) +TRANS_FLAGS2(VSX, XSNMADDMDP, do_xsmadd_XX3, false, gen_helper_XSNMADDDP) +TRANS_FLAGS2(VSX, XSNMSUBADP, do_xsmadd_XX3, true, gen_helper_XSNMSUBDP) +TRANS_FLAGS2(VSX, XSNMSUBMDP, do_xsmadd_XX3, false, gen_helper_XSNMSUBDP) +TRANS_FLAGS2(VSX207, XSMADDASP, do_xsmadd_XX3, true, gen_helper_XSMADDSP) +TRANS_FLAGS2(VSX207, XSMADDMSP, do_xsmadd_XX3, false, gen_helper_XSMADDSP) +TRANS_FLAGS2(VSX207, XSMSUBASP, do_xsmadd_XX3, true, gen_helper_XSMSUBSP) +TRANS_FLAGS2(VSX207, XSMSUBMSP, do_xsmadd_XX3, false, gen_helper_XSMSUBSP) +TRANS_FLAGS2(VSX207, XSNMADDASP, do_xsmadd_XX3, true, gen_helper_XSNMADDSP) +TRANS_FLAGS2(VSX207, XSNMADDMSP, do_xsmadd_XX3, false, gen_helper_XSNMADDSP) +TRANS_FLAGS2(VSX207, XSNMSUBASP, do_xsmadd_XX3, true, gen_helper_XSNMSUBSP) +TRANS_FLAGS2(VSX207, XSNMSUBMSP, do_xsmadd_XX3, false, gen_helper_XSNMSUBSP) + +static bool do_xsmadd_X(DisasContext *ctx, arg_X_rc *a, + void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr), + void (*gen_helper_ro)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + int vrt, vra, vrb; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); + + vrt = a->rt + 32; + vra = a->ra + 32; + vrb = a->rb + 32; + + if (a->rc) { + return do_xsmadd(ctx, vrt, vra, vrt, vrb, gen_helper_ro); + } + + return do_xsmadd(ctx, vrt, vra, vrt, vrb, gen_helper); +} + +TRANS(XSMADDQP, do_xsmadd_X, gen_helper_XSMADDQP, gen_helper_XSMADDQPO) +TRANS(XSMSUBQP, do_xsmadd_X, gen_helper_XSMSUBQP, gen_helper_XSMSUBQPO) +TRANS(XSNMADDQP, do_xsmadd_X, gen_helper_XSNMADDQP, gen_helper_XSNMADDQPO) +TRANS(XSNMSUBQP, do_xsmadd_X, gen_helper_XSNMSUBQP, gen_helper_XSNMSUBQPO) #define GEN_VSX_HELPER_VSX_MADD(name, op1, aop, mop, inval, type) \ static void gen_##name(DisasContext *ctx) \ { \ - TCGv_ptr xt, xa, b, c; \ + TCGv_ptr xt, s1, s2, s3; \ if (unlikely(!ctx->vsx_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ xt = gen_vsr_ptr(xT(ctx->opcode)); \ - xa = gen_vsr_ptr(xA(ctx->opcode)); \ + s1 = gen_vsr_ptr(xA(ctx->opcode)); \ if (ctx->opcode & PPC_BIT32(25)) { \ /* \ * AxT + B \ */ \ - b = gen_vsr_ptr(xT(ctx->opcode)); \ - c = gen_vsr_ptr(xB(ctx->opcode)); \ + s2 = gen_vsr_ptr(xB(ctx->opcode)); \ + s3 = gen_vsr_ptr(xT(ctx->opcode)); \ } else { \ /* \ * AxB + T \ */ \ - b = gen_vsr_ptr(xB(ctx->opcode)); \ - c = gen_vsr_ptr(xT(ctx->opcode)); \ + s2 = gen_vsr_ptr(xT(ctx->opcode)); \ + s3 = gen_vsr_ptr(xB(ctx->opcode)); \ } \ - gen_helper_##name(cpu_env, xt, xa, b, c); \ + gen_helper_##name(cpu_env, xt, s1, s2, s3); \ tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(xa); \ - tcg_temp_free_ptr(b); \ - tcg_temp_free_ptr(c); \ + tcg_temp_free_ptr(s1); \ + tcg_temp_free_ptr(s2); \ + tcg_temp_free_ptr(s3); \ } -GEN_VSX_HELPER_VSX_MADD(xsmadddp, 0x04, 0x04, 0x05, 0, PPC2_VSX) -GEN_VSX_HELPER_VSX_MADD(xsmsubdp, 0x04, 0x06, 0x07, 0, PPC2_VSX) -GEN_VSX_HELPER_VSX_MADD(xsnmadddp, 0x04, 0x14, 0x15, 0, PPC2_VSX) -GEN_VSX_HELPER_VSX_MADD(xsnmsubdp, 0x04, 0x16, 0x17, 0, PPC2_VSX) -GEN_VSX_HELPER_VSX_MADD(xsmaddsp, 0x04, 0x00, 0x01, 0, PPC2_VSX207) -GEN_VSX_HELPER_VSX_MADD(xsmsubsp, 0x04, 0x02, 0x03, 0, PPC2_VSX207) -GEN_VSX_HELPER_VSX_MADD(xsnmaddsp, 0x04, 0x10, 0x11, 0, PPC2_VSX207) -GEN_VSX_HELPER_VSX_MADD(xsnmsubsp, 0x04, 0x12, 0x13, 0, PPC2_VSX207) GEN_VSX_HELPER_VSX_MADD(xvmadddp, 0x04, 0x0C, 0x0D, 0, PPC2_VSX) GEN_VSX_HELPER_VSX_MADD(xvmsubdp, 0x04, 0x0E, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_VSX_MADD(xvnmadddp, 0x04, 0x1C, 0x1D, 0, PPC2_VSX) @@ -1422,22 +1554,18 @@ static void glue(gen_, name)(DisasContext *ctx) \ VSX_XXMRG(xxmrghw, 1) VSX_XXMRG(xxmrglw, 0) -static void gen_xxsel(DisasContext *ctx) +static bool trans_XXSEL(DisasContext *ctx, arg_XX4 *a) { - int rt = xT(ctx->opcode); - int ra = xA(ctx->opcode); - int rb = xB(ctx->opcode); - int rc = xC(ctx->opcode); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - tcg_gen_gvec_bitsel(MO_64, vsr_full_offset(rt), vsr_full_offset(rc), - vsr_full_offset(rb), vsr_full_offset(ra), 16, 16); + tcg_gen_gvec_bitsel(MO_64, vsr_full_offset(a->xt), vsr_full_offset(a->xc), + vsr_full_offset(a->xb), vsr_full_offset(a->xa), 16, 16); + + return true; } -static bool trans_XXSPLTW(DisasContext *ctx, arg_XX2 *a) +static bool trans_XXSPLTW(DisasContext *ctx, arg_XX2_uim2 *a) { int tofs, bofs; @@ -1446,7 +1574,7 @@ static bool trans_XXSPLTW(DisasContext *ctx, arg_XX2 *a) tofs = vsr_full_offset(a->xt); bofs = vsr_full_offset(a->xb); bofs += a->uim << MO_32; -#ifndef HOST_WORDS_BIG_ENDIAN +#if !HOST_BIG_ENDIAN bofs ^= 8 | 4; #endif @@ -1547,6 +1675,46 @@ static bool trans_LXVKQ(DisasContext *ctx, arg_X_uim5 *a) return true; } +static bool trans_XVTLSBB(DisasContext *ctx, arg_XX2_bf_xb *a) +{ + TCGv_i64 xb, t0, t1, all_true, all_false, mask, zero; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + xb = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + all_true = tcg_temp_new_i64(); + all_false = tcg_temp_new_i64(); + mask = tcg_constant_i64(dup_const(MO_8, 1)); + zero = tcg_constant_i64(0); + + get_cpu_vsr(xb, a->xb, true); + tcg_gen_and_i64(t0, mask, xb); + get_cpu_vsr(xb, a->xb, false); + tcg_gen_and_i64(t1, mask, xb); + + tcg_gen_or_i64(all_false, t0, t1); + tcg_gen_and_i64(all_true, t0, t1); + + tcg_gen_setcond_i64(TCG_COND_EQ, all_false, all_false, zero); + tcg_gen_shli_i64(all_false, all_false, 1); + tcg_gen_setcond_i64(TCG_COND_EQ, all_true, all_true, mask); + tcg_gen_shli_i64(all_true, all_true, 3); + + tcg_gen_or_i64(t0, all_false, all_true); + tcg_gen_extrl_i64_i32(cpu_crf[a->bf], t0); + + tcg_temp_free_i64(xb); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(all_true); + tcg_temp_free_i64(all_false); + + return true; +} + static void gen_xxsldwi(DisasContext *ctx) { TCGv_i64 xth, xtl; @@ -2072,12 +2240,6 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store, bool paired) { - if (paired) { - REQUIRE_INSNS_FLAGS2(ctx, ISA310); - } else { - REQUIRE_INSNS_FLAGS2(ctx, ISA300); - } - if (paired || a->rt >= 32) { REQUIRE_VSX(ctx); } else { @@ -2091,7 +2253,6 @@ static bool do_lstxv_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool store, bool paired) { arg_D d; - REQUIRE_INSNS_FLAGS2(ctx, ISA310); REQUIRE_VSX(ctx); if (!resolve_PLS_D(ctx, &d, a)) { @@ -2103,12 +2264,6 @@ static bool do_lstxv_PLS_D(DisasContext *ctx, arg_PLS_D *a, static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store, bool paired) { - if (paired) { - REQUIRE_INSNS_FLAGS2(ctx, ISA310); - } else { - REQUIRE_INSNS_FLAGS2(ctx, ISA300); - } - if (paired || a->rt >= 32) { REQUIRE_VSX(ctx); } else { @@ -2118,18 +2273,373 @@ static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store, bool paired) return do_lstxv(ctx, a->ra, cpu_gpr[a->rb], a->rt, store, paired); } -TRANS(STXV, do_lstxv_D, true, false) -TRANS(LXV, do_lstxv_D, false, false) -TRANS(STXVP, do_lstxv_D, true, true) -TRANS(LXVP, do_lstxv_D, false, true) -TRANS(STXVX, do_lstxv_X, true, false) -TRANS(LXVX, do_lstxv_X, false, false) -TRANS(STXVPX, do_lstxv_X, true, true) -TRANS(LXVPX, do_lstxv_X, false, true) -TRANS64(PSTXV, do_lstxv_PLS_D, true, false) -TRANS64(PLXV, do_lstxv_PLS_D, false, false) -TRANS64(PSTXVP, do_lstxv_PLS_D, true, true) -TRANS64(PLXVP, do_lstxv_PLS_D, false, true) +static bool do_lstxsd(DisasContext *ctx, int rt, int ra, TCGv displ, bool store) +{ + TCGv ea; + TCGv_i64 xt; + MemOp mop; + + if (store) { + REQUIRE_VECTOR(ctx); + } else { + REQUIRE_VSX(ctx); + } + + xt = tcg_temp_new_i64(); + mop = DEF_MEMOP(MO_UQ); + + gen_set_access_type(ctx, ACCESS_INT); + ea = do_ea_calc(ctx, ra, displ); + + if (store) { + get_cpu_vsr(xt, rt + 32, true); + tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + } else { + tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); + set_cpu_vsr(rt + 32, xt, true); + set_cpu_vsr(rt + 32, tcg_constant_i64(0), false); + } + + tcg_temp_free(ea); + tcg_temp_free_i64(xt); + + return true; +} + +static bool do_lstxsd_DS(DisasContext *ctx, arg_D *a, bool store) +{ + return do_lstxsd(ctx, a->rt, a->ra, tcg_constant_tl(a->si), store); +} + +static bool do_plstxsd_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool store) +{ + arg_D d; + + if (!resolve_PLS_D(ctx, &d, a)) { + return true; + } + + return do_lstxsd(ctx, d.rt, d.ra, tcg_constant_tl(d.si), store); +} + +static bool do_lstxssp(DisasContext *ctx, int rt, int ra, TCGv displ, bool store) +{ + TCGv ea; + TCGv_i64 xt; + + REQUIRE_VECTOR(ctx); + + xt = tcg_temp_new_i64(); + + gen_set_access_type(ctx, ACCESS_INT); + ea = do_ea_calc(ctx, ra, displ); + + if (store) { + get_cpu_vsr(xt, rt + 32, true); + gen_qemu_st32fs(ctx, xt, ea); + } else { + gen_qemu_ld32fs(ctx, xt, ea); + set_cpu_vsr(rt + 32, xt, true); + set_cpu_vsr(rt + 32, tcg_constant_i64(0), false); + } + + tcg_temp_free(ea); + tcg_temp_free_i64(xt); + + return true; +} + +static bool do_lstxssp_DS(DisasContext *ctx, arg_D *a, bool store) +{ + return do_lstxssp(ctx, a->rt, a->ra, tcg_constant_tl(a->si), store); +} + +static bool do_plstxssp_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool store) +{ + arg_D d; + + if (!resolve_PLS_D(ctx, &d, a)) { + return true; + } + + return do_lstxssp(ctx, d.rt, d.ra, tcg_constant_tl(d.si), store); +} + +TRANS_FLAGS2(ISA300, LXSD, do_lstxsd_DS, false) +TRANS_FLAGS2(ISA300, STXSD, do_lstxsd_DS, true) +TRANS_FLAGS2(ISA300, LXSSP, do_lstxssp_DS, false) +TRANS_FLAGS2(ISA300, STXSSP, do_lstxssp_DS, true) +TRANS_FLAGS2(ISA300, STXV, do_lstxv_D, true, false) +TRANS_FLAGS2(ISA300, LXV, do_lstxv_D, false, false) +TRANS_FLAGS2(ISA310, STXVP, do_lstxv_D, true, true) +TRANS_FLAGS2(ISA310, LXVP, do_lstxv_D, false, true) +TRANS_FLAGS2(ISA300, STXVX, do_lstxv_X, true, false) +TRANS_FLAGS2(ISA300, LXVX, do_lstxv_X, false, false) +TRANS_FLAGS2(ISA310, STXVPX, do_lstxv_X, true, true) +TRANS_FLAGS2(ISA310, LXVPX, do_lstxv_X, false, true) +TRANS64_FLAGS2(ISA310, PLXSD, do_plstxsd_PLS_D, false) +TRANS64_FLAGS2(ISA310, PSTXSD, do_plstxsd_PLS_D, true) +TRANS64_FLAGS2(ISA310, PLXSSP, do_plstxssp_PLS_D, false) +TRANS64_FLAGS2(ISA310, PSTXSSP, do_plstxssp_PLS_D, true) +TRANS64_FLAGS2(ISA310, PSTXV, do_lstxv_PLS_D, true, false) +TRANS64_FLAGS2(ISA310, PLXV, do_lstxv_PLS_D, false, false) +TRANS64_FLAGS2(ISA310, PSTXVP, do_lstxv_PLS_D, true, true) +TRANS64_FLAGS2(ISA310, PLXVP, do_lstxv_PLS_D, false, true) + +static bool do_lstrm(DisasContext *ctx, arg_X *a, MemOp mop, bool store) +{ + TCGv ea; + TCGv_i64 xt; + + REQUIRE_VSX(ctx); + + xt = tcg_temp_new_i64(); + + gen_set_access_type(ctx, ACCESS_INT); + ea = do_ea_calc(ctx, a->ra , cpu_gpr[a->rb]); + + if (store) { + get_cpu_vsr(xt, a->rt, false); + tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + } else { + tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); + set_cpu_vsr(a->rt, xt, false); + set_cpu_vsr(a->rt, tcg_constant_i64(0), true); + } + + tcg_temp_free(ea); + tcg_temp_free_i64(xt); + return true; +} + +TRANS_FLAGS2(ISA310, LXVRBX, do_lstrm, DEF_MEMOP(MO_UB), false) +TRANS_FLAGS2(ISA310, LXVRHX, do_lstrm, DEF_MEMOP(MO_UW), false) +TRANS_FLAGS2(ISA310, LXVRWX, do_lstrm, DEF_MEMOP(MO_UL), false) +TRANS_FLAGS2(ISA310, LXVRDX, do_lstrm, DEF_MEMOP(MO_UQ), false) +TRANS_FLAGS2(ISA310, STXVRBX, do_lstrm, DEF_MEMOP(MO_UB), true) +TRANS_FLAGS2(ISA310, STXVRHX, do_lstrm, DEF_MEMOP(MO_UW), true) +TRANS_FLAGS2(ISA310, STXVRWX, do_lstrm, DEF_MEMOP(MO_UL), true) +TRANS_FLAGS2(ISA310, STXVRDX, do_lstrm, DEF_MEMOP(MO_UQ), true) + +static void gen_xxeval_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, TCGv_i64 c, + int64_t imm) +{ + /* + * Instead of processing imm bit-by-bit, we'll skip the computation of + * conjunctions whose corresponding bit is unset. + */ + int bit; + TCGv_i64 conj, disj; + + conj = tcg_temp_new_i64(); + disj = tcg_const_i64(0); + + /* Iterate over set bits from the least to the most significant bit */ + while (imm) { + /* + * Get the next bit to be processed with ctz64. Invert the result of + * ctz64 to match the indexing used by PowerISA. + */ + bit = 7 - ctz64(imm); + if (bit & 0x4) { + tcg_gen_mov_i64(conj, a); + } else { + tcg_gen_not_i64(conj, a); + } + if (bit & 0x2) { + tcg_gen_and_i64(conj, conj, b); + } else { + tcg_gen_andc_i64(conj, conj, b); + } + if (bit & 0x1) { + tcg_gen_and_i64(conj, conj, c); + } else { + tcg_gen_andc_i64(conj, conj, c); + } + tcg_gen_or_i64(disj, disj, conj); + + /* Unset the least significant bit that is set */ + imm &= imm - 1; + } + + tcg_gen_mov_i64(t, disj); + + tcg_temp_free_i64(conj); + tcg_temp_free_i64(disj); +} + +static void gen_xxeval_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, + TCGv_vec c, int64_t imm) +{ + /* + * Instead of processing imm bit-by-bit, we'll skip the computation of + * conjunctions whose corresponding bit is unset. + */ + int bit; + TCGv_vec disj, conj; + + disj = tcg_const_zeros_vec_matching(t); + conj = tcg_temp_new_vec_matching(t); + + /* Iterate over set bits from the least to the most significant bit */ + while (imm) { + /* + * Get the next bit to be processed with ctz64. Invert the result of + * ctz64 to match the indexing used by PowerISA. + */ + bit = 7 - ctz64(imm); + if (bit & 0x4) { + tcg_gen_mov_vec(conj, a); + } else { + tcg_gen_not_vec(vece, conj, a); + } + if (bit & 0x2) { + tcg_gen_and_vec(vece, conj, conj, b); + } else { + tcg_gen_andc_vec(vece, conj, conj, b); + } + if (bit & 0x1) { + tcg_gen_and_vec(vece, conj, conj, c); + } else { + tcg_gen_andc_vec(vece, conj, conj, c); + } + tcg_gen_or_vec(vece, disj, disj, conj); + + /* Unset the least significant bit that is set */ + imm &= imm - 1; + } + + tcg_gen_mov_vec(t, disj); + + tcg_temp_free_vec(disj); + tcg_temp_free_vec(conj); +} + +static bool trans_XXEVAL(DisasContext *ctx, arg_8RR_XX4_imm *a) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_andc_vec, 0 + }; + static const GVecGen4i op = { + .fniv = gen_xxeval_vec, + .fno = gen_helper_XXEVAL, + .fni8 = gen_xxeval_i64, + .opt_opc = vecop_list, + .vece = MO_64 + }; + int xt = vsr_full_offset(a->xt), xa = vsr_full_offset(a->xa), + xb = vsr_full_offset(a->xb), xc = vsr_full_offset(a->xc); + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + /* Equivalent functions that can be implemented with a single gen_gvec */ + switch (a->imm) { + case 0b00000000: /* true */ + set_cpu_vsr(a->xt, tcg_constant_i64(0), true); + set_cpu_vsr(a->xt, tcg_constant_i64(0), false); + break; + case 0b00000011: /* and(B,A) */ + tcg_gen_gvec_and(MO_64, xt, xb, xa, 16, 16); + break; + case 0b00000101: /* and(C,A) */ + tcg_gen_gvec_and(MO_64, xt, xc, xa, 16, 16); + break; + case 0b00001111: /* A */ + tcg_gen_gvec_mov(MO_64, xt, xa, 16, 16); + break; + case 0b00010001: /* and(C,B) */ + tcg_gen_gvec_and(MO_64, xt, xc, xb, 16, 16); + break; + case 0b00011011: /* C?B:A */ + tcg_gen_gvec_bitsel(MO_64, xt, xc, xb, xa, 16, 16); + break; + case 0b00011101: /* B?C:A */ + tcg_gen_gvec_bitsel(MO_64, xt, xb, xc, xa, 16, 16); + break; + case 0b00100111: /* C?A:B */ + tcg_gen_gvec_bitsel(MO_64, xt, xc, xa, xb, 16, 16); + break; + case 0b00110011: /* B */ + tcg_gen_gvec_mov(MO_64, xt, xb, 16, 16); + break; + case 0b00110101: /* A?C:B */ + tcg_gen_gvec_bitsel(MO_64, xt, xa, xc, xb, 16, 16); + break; + case 0b00111100: /* xor(B,A) */ + tcg_gen_gvec_xor(MO_64, xt, xb, xa, 16, 16); + break; + case 0b00111111: /* or(B,A) */ + tcg_gen_gvec_or(MO_64, xt, xb, xa, 16, 16); + break; + case 0b01000111: /* B?A:C */ + tcg_gen_gvec_bitsel(MO_64, xt, xb, xa, xc, 16, 16); + break; + case 0b01010011: /* A?B:C */ + tcg_gen_gvec_bitsel(MO_64, xt, xa, xb, xc, 16, 16); + break; + case 0b01010101: /* C */ + tcg_gen_gvec_mov(MO_64, xt, xc, 16, 16); + break; + case 0b01011010: /* xor(C,A) */ + tcg_gen_gvec_xor(MO_64, xt, xc, xa, 16, 16); + break; + case 0b01011111: /* or(C,A) */ + tcg_gen_gvec_or(MO_64, xt, xc, xa, 16, 16); + break; + case 0b01100110: /* xor(C,B) */ + tcg_gen_gvec_xor(MO_64, xt, xc, xb, 16, 16); + break; + case 0b01110111: /* or(C,B) */ + tcg_gen_gvec_or(MO_64, xt, xc, xb, 16, 16); + break; + case 0b10001000: /* nor(C,B) */ + tcg_gen_gvec_nor(MO_64, xt, xc, xb, 16, 16); + break; + case 0b10011001: /* eqv(C,B) */ + tcg_gen_gvec_eqv(MO_64, xt, xc, xb, 16, 16); + break; + case 0b10100000: /* nor(C,A) */ + tcg_gen_gvec_nor(MO_64, xt, xc, xa, 16, 16); + break; + case 0b10100101: /* eqv(C,A) */ + tcg_gen_gvec_eqv(MO_64, xt, xc, xa, 16, 16); + break; + case 0b10101010: /* not(C) */ + tcg_gen_gvec_not(MO_64, xt, xc, 16, 16); + break; + case 0b11000000: /* nor(B,A) */ + tcg_gen_gvec_nor(MO_64, xt, xb, xa, 16, 16); + break; + case 0b11000011: /* eqv(B,A) */ + tcg_gen_gvec_eqv(MO_64, xt, xb, xa, 16, 16); + break; + case 0b11001100: /* not(B) */ + tcg_gen_gvec_not(MO_64, xt, xb, 16, 16); + break; + case 0b11101110: /* nand(C,B) */ + tcg_gen_gvec_nand(MO_64, xt, xc, xb, 16, 16); + break; + case 0b11110000: /* not(A) */ + tcg_gen_gvec_not(MO_64, xt, xa, 16, 16); + break; + case 0b11111010: /* nand(C,A) */ + tcg_gen_gvec_nand(MO_64, xt, xc, xa, 16, 16); + break; + case 0b11111100: /* nand(B,A) */ + tcg_gen_gvec_nand(MO_64, xt, xb, xa, 16, 16); + break; + case 0b11111111: /* true */ + set_cpu_vsr(a->xt, tcg_constant_i64(-1), true); + set_cpu_vsr(a->xt, tcg_constant_i64(-1), false); + break; + default: + /* Fallback to compute all conjunctions/disjunctions */ + tcg_gen_gvec_4i(xt, xa, xb, xc, 16, 16, a->imm, &op); + } + + return true; +} static void gen_xxblendv_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, TCGv_vec c) @@ -2140,7 +2650,7 @@ static void gen_xxblendv_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, tcg_temp_free_vec(tmp); } -static bool do_xxblendv(DisasContext *ctx, arg_XX4 *a, unsigned vece) +static bool do_xxblendv(DisasContext *ctx, arg_8RR_XX4 *a, unsigned vece) { static const TCGOpcode vecop_list[] = { INDEX_op_sari_vec, 0 @@ -2186,8 +2696,8 @@ TRANS(XXBLENDVH, do_xxblendv, MO_16) TRANS(XXBLENDVW, do_xxblendv, MO_32) TRANS(XXBLENDVD, do_xxblendv, MO_64) -static bool do_xsmaxmincjdp(DisasContext *ctx, arg_XX3 *a, - void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +static bool do_helper_XX3(DisasContext *ctx, arg_XX3 *a, + void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) { TCGv_ptr xt, xa, xb; @@ -2207,10 +2717,75 @@ static bool do_xsmaxmincjdp(DisasContext *ctx, arg_XX3 *a, return true; } -TRANS(XSMAXCDP, do_xsmaxmincjdp, gen_helper_xsmaxcdp) -TRANS(XSMINCDP, do_xsmaxmincjdp, gen_helper_xsmincdp) -TRANS(XSMAXJDP, do_xsmaxmincjdp, gen_helper_xsmaxjdp) -TRANS(XSMINJDP, do_xsmaxmincjdp, gen_helper_xsminjdp) +TRANS(XSCMPEQDP, do_helper_XX3, gen_helper_XSCMPEQDP) +TRANS(XSCMPGEDP, do_helper_XX3, gen_helper_XSCMPGEDP) +TRANS(XSCMPGTDP, do_helper_XX3, gen_helper_XSCMPGTDP) +TRANS(XSMAXCDP, do_helper_XX3, gen_helper_XSMAXCDP) +TRANS(XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) +TRANS(XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) +TRANS(XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) + +static bool do_helper_X(arg_X *a, + void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + TCGv_ptr rt, ra, rb; + + rt = gen_avr_ptr(a->rt); + ra = gen_avr_ptr(a->ra); + rb = gen_avr_ptr(a->rb); + + helper(cpu_env, rt, ra, rb); + + tcg_temp_free_ptr(rt); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rb); + + return true; +} + +static bool do_xscmpqp(DisasContext *ctx, arg_X *a, + void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + return do_helper_X(a, helper); +} + +TRANS(XSCMPEQQP, do_xscmpqp, gen_helper_XSCMPEQQP) +TRANS(XSCMPGEQP, do_xscmpqp, gen_helper_XSCMPGEQP) +TRANS(XSCMPGTQP, do_xscmpqp, gen_helper_XSCMPGTQP) +TRANS(XSMAXCQP, do_xscmpqp, gen_helper_XSMAXCQP) +TRANS(XSMINCQP, do_xscmpqp, gen_helper_XSMINCQP) + +static bool trans_XVCVSPBF16(DisasContext *ctx, arg_XX2 *a) +{ + TCGv_ptr xt, xb; + + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + xt = gen_vsr_ptr(a->xt); + xb = gen_vsr_ptr(a->xb); + + gen_helper_XVCVSPBF16(cpu_env, xt, xb); + + tcg_temp_free_ptr(xt); + tcg_temp_free_ptr(xb); + + return true; +} + +static bool trans_XVCVBF16SPN(DisasContext *ctx, arg_XX2 *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VSX(ctx); + + tcg_gen_gvec_shli(MO_32, vsr_full_offset(a->xt), vsr_full_offset(a->xb), + 16, 16, 16); + + return true; +} #undef GEN_XX2FORM #undef GEN_XX3FORM diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index c974324c4c..b8fd116728 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -186,18 +186,6 @@ GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), GEN_XX2FORM(xsrsqrtedp, 0x14, 0x04, PPC2_VSX), GEN_XX3FORM(xstdivdp, 0x14, 0x07, PPC2_VSX), GEN_XX2FORM(xstsqrtdp, 0x14, 0x06, PPC2_VSX), -GEN_XX3FORM_NAME(xsmadddp, "xsmaddadp", 0x04, 0x04, PPC2_VSX), -GEN_XX3FORM_NAME(xsmadddp, "xsmaddmdp", 0x04, 0x05, PPC2_VSX), -GEN_XX3FORM_NAME(xsmsubdp, "xsmsubadp", 0x04, 0x06, PPC2_VSX), -GEN_XX3FORM_NAME(xsmsubdp, "xsmsubmdp", 0x04, 0x07, PPC2_VSX), -GEN_XX3FORM_NAME(xsnmadddp, "xsnmaddadp", 0x04, 0x14, PPC2_VSX), -GEN_XX3FORM_NAME(xsnmadddp, "xsnmaddmdp", 0x04, 0x15, PPC2_VSX), -GEN_XX3FORM_NAME(xsnmsubdp, "xsnmsubadp", 0x04, 0x16, PPC2_VSX), -GEN_XX3FORM_NAME(xsnmsubdp, "xsnmsubmdp", 0x04, 0x17, PPC2_VSX), -GEN_XX3FORM(xscmpeqdp, 0x0C, 0x00, PPC2_ISA300), -GEN_XX3FORM(xscmpgtdp, 0x0C, 0x01, PPC2_ISA300), -GEN_XX3FORM(xscmpgedp, 0x0C, 0x02, PPC2_ISA300), -GEN_XX3FORM(xscmpnedp, 0x0C, 0x03, PPC2_ISA300), GEN_XX3FORM(xscmpexpdp, 0x0C, 0x07, PPC2_ISA300), GEN_VSX_XFORM_300(xscmpexpqp, 0x04, 0x05, 0x00600001), GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), @@ -235,14 +223,6 @@ GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), GEN_XX2FORM(xssqrtsp, 0x16, 0x00, PPC2_VSX207), GEN_XX2FORM(xsrsqrtesp, 0x14, 0x00, PPC2_VSX207), -GEN_XX3FORM_NAME(xsmaddsp, "xsmaddasp", 0x04, 0x00, PPC2_VSX207), -GEN_XX3FORM_NAME(xsmaddsp, "xsmaddmsp", 0x04, 0x01, PPC2_VSX207), -GEN_XX3FORM_NAME(xsmsubsp, "xsmsubasp", 0x04, 0x02, PPC2_VSX207), -GEN_XX3FORM_NAME(xsmsubsp, "xsmsubmsp", 0x04, 0x03, PPC2_VSX207), -GEN_XX3FORM_NAME(xsnmaddsp, "xsnmaddasp", 0x04, 0x10, PPC2_VSX207), -GEN_XX3FORM_NAME(xsnmaddsp, "xsnmaddmsp", 0x04, 0x11, PPC2_VSX207), -GEN_XX3FORM_NAME(xsnmsubsp, "xsnmsubasp", 0x04, 0x12, PPC2_VSX207), -GEN_XX3FORM_NAME(xsnmsubsp, "xsnmsubmsp", 0x04, 0x13, PPC2_VSX207), GEN_XX2FORM(xscvsxdsp, 0x10, 0x13, PPC2_VSX207), GEN_XX2FORM(xscvuxdsp, 0x10, 0x12, PPC2_VSX207), @@ -341,53 +321,6 @@ VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), -GEN_XX3FORM(xxperm, 0x08, 0x03, PPC2_ISA300), -GEN_XX3FORM(xxpermr, 0x08, 0x07, PPC2_ISA300), GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), GEN_XX2FORM_EXT(xxextractuw, 0x0A, 0x0A, PPC2_ISA300), GEN_XX2FORM_EXT(xxinsertw, 0x0A, 0x0B, PPC2_ISA300), - -#define GEN_XXSEL_ROW(opc3) \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x18, opc3, 0, PPC_NONE, PPC2_VSX), \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x19, opc3, 0, PPC_NONE, PPC2_VSX), \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1A, opc3, 0, PPC_NONE, PPC2_VSX), \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1B, opc3, 0, PPC_NONE, PPC2_VSX), \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1C, opc3, 0, PPC_NONE, PPC2_VSX), \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1D, opc3, 0, PPC_NONE, PPC2_VSX), \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1E, opc3, 0, PPC_NONE, PPC2_VSX), \ -GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1F, opc3, 0, PPC_NONE, PPC2_VSX), \ - -GEN_XXSEL_ROW(0x00) -GEN_XXSEL_ROW(0x01) -GEN_XXSEL_ROW(0x02) -GEN_XXSEL_ROW(0x03) -GEN_XXSEL_ROW(0x04) -GEN_XXSEL_ROW(0x05) -GEN_XXSEL_ROW(0x06) -GEN_XXSEL_ROW(0x07) -GEN_XXSEL_ROW(0x08) -GEN_XXSEL_ROW(0x09) -GEN_XXSEL_ROW(0x0A) -GEN_XXSEL_ROW(0x0B) -GEN_XXSEL_ROW(0x0C) -GEN_XXSEL_ROW(0x0D) -GEN_XXSEL_ROW(0x0E) -GEN_XXSEL_ROW(0x0F) -GEN_XXSEL_ROW(0x10) -GEN_XXSEL_ROW(0x11) -GEN_XXSEL_ROW(0x12) -GEN_XXSEL_ROW(0x13) -GEN_XXSEL_ROW(0x14) -GEN_XXSEL_ROW(0x15) -GEN_XXSEL_ROW(0x16) -GEN_XXSEL_ROW(0x17) -GEN_XXSEL_ROW(0x18) -GEN_XXSEL_ROW(0x19) -GEN_XXSEL_ROW(0x1A) -GEN_XXSEL_ROW(0x1B) -GEN_XXSEL_ROW(0x1C) -GEN_XXSEL_ROW(0x1D) -GEN_XXSEL_ROW(0x1E) -GEN_XXSEL_ROW(0x1F) - -GEN_XX3FORM_DM(xxpermdi, 0x08, 0x01), diff --git a/target/riscv/bitmanip_helper.c b/target/riscv/bitmanip_helper.c index f1b5e5549f..b99c4a39a1 100644 --- a/target/riscv/bitmanip_helper.c +++ b/target/riscv/bitmanip_helper.c @@ -49,3 +49,83 @@ target_ulong HELPER(clmulr)(target_ulong rs1, target_ulong rs2) return result; } + +static inline target_ulong do_swap(target_ulong x, uint64_t mask, int shift) +{ + return ((x & mask) << shift) | ((x & ~mask) >> shift); +} + +target_ulong HELPER(brev8)(target_ulong rs1) +{ + target_ulong x = rs1; + + x = do_swap(x, 0x5555555555555555ull, 1); + x = do_swap(x, 0x3333333333333333ull, 2); + x = do_swap(x, 0x0f0f0f0f0f0f0f0full, 4); + return x; +} + +static const uint64_t shuf_masks[] = { + dup_const(MO_8, 0x44), + dup_const(MO_8, 0x30), + dup_const(MO_16, 0x0f00), + dup_const(MO_32, 0xff0000) +}; + +static inline target_ulong do_shuf_stage(target_ulong src, uint64_t maskL, + uint64_t maskR, int shift) +{ + target_ulong x = src & ~(maskL | maskR); + + x |= ((src << shift) & maskL) | ((src >> shift) & maskR); + return x; +} + +target_ulong HELPER(unzip)(target_ulong rs1) +{ + target_ulong x = rs1; + + x = do_shuf_stage(x, shuf_masks[0], shuf_masks[0] >> 1, 1); + x = do_shuf_stage(x, shuf_masks[1], shuf_masks[1] >> 2, 2); + x = do_shuf_stage(x, shuf_masks[2], shuf_masks[2] >> 4, 4); + x = do_shuf_stage(x, shuf_masks[3], shuf_masks[3] >> 8, 8); + return x; +} + +target_ulong HELPER(zip)(target_ulong rs1) +{ + target_ulong x = rs1; + + x = do_shuf_stage(x, shuf_masks[3], shuf_masks[3] >> 8, 8); + x = do_shuf_stage(x, shuf_masks[2], shuf_masks[2] >> 4, 4); + x = do_shuf_stage(x, shuf_masks[1], shuf_masks[1] >> 2, 2); + x = do_shuf_stage(x, shuf_masks[0], shuf_masks[0] >> 1, 1); + return x; +} + +static inline target_ulong do_xperm(target_ulong rs1, target_ulong rs2, + uint32_t sz_log2) +{ + target_ulong r = 0; + target_ulong sz = 1LL << sz_log2; + target_ulong mask = (1LL << sz) - 1; + target_ulong pos; + + for (int i = 0; i < TARGET_LONG_BITS; i += sz) { + pos = ((rs2 >> i) & mask) << sz_log2; + if (pos < sizeof(target_ulong) * 8) { + r |= ((rs1 >> pos) & mask) << i; + } + } + return r; +} + +target_ulong HELPER(xperm4)(target_ulong rs1, target_ulong rs2) +{ + return do_xperm(rs1, rs2, 2); +} + +target_ulong HELPER(xperm8)(target_ulong rs1, target_ulong rs2) +{ + return do_xperm(rs1, rs2, 3); +} diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b0a40b83e7..ccacdee215 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -34,7 +34,17 @@ /* RISC-V CPU definitions */ -static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG"; +#define RISCV_CPU_MARCHID ((QEMU_VERSION_MAJOR << 16) | \ + (QEMU_VERSION_MINOR << 8) | \ + (QEMU_VERSION_MICRO)) +#define RISCV_CPU_MIPID RISCV_CPU_MARCHID + +static const char riscv_single_letter_exts[] = "IEMAFDQCPVH"; + +struct isa_ext_data { + const char *name; + bool enabled; +}; const char * const riscv_int_regnames[] = { "x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1", @@ -150,7 +160,7 @@ static void riscv_any_cpu_init(Object *obj) #elif defined(TARGET_RISCV64) set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU); #endif - set_priv_version(env, PRIV_VERSION_1_11_0); + set_priv_version(env, PRIV_VERSION_1_12_0); } #if defined(TARGET_RISCV64) @@ -461,6 +471,10 @@ static void riscv_cpu_reset(DeviceState *dev) set_default_nan_mode(1, &env->fp_status); #ifndef CONFIG_USER_ONLY + if (riscv_feature(env, RISCV_FEATURE_DEBUG)) { + riscv_trigger_init(env); + } + if (kvm_enabled()) { kvm_riscv_reset_vcpu(cpu); } @@ -503,7 +517,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } if (cpu->cfg.priv_spec) { - if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { + if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) { + priv_version = PRIV_VERSION_1_12_0; + } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { priv_version = PRIV_VERSION_1_11_0; } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) { priv_version = PRIV_VERSION_1_10_0; @@ -518,7 +534,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (priv_version) { set_priv_version(env, priv_version); } else if (!env->priv_ver) { - set_priv_version(env, PRIV_VERSION_1_11_0); + set_priv_version(env, PRIV_VERSION_1_12_0); } if (cpu->cfg.mmu) { @@ -541,6 +557,10 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) riscv_set_feature(env, RISCV_FEATURE_AIA); } + if (cpu->cfg.debug) { + riscv_set_feature(env, RISCV_FEATURE_DEBUG); + } + set_resetvec(env, cpu->cfg.resetvec); /* Validate that MISA_MXL is set properly. */ @@ -567,18 +587,18 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (cpu->cfg.ext_i && cpu->cfg.ext_e) { error_setg(errp, "I and E extensions are incompatible"); - return; - } + return; + } if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) { error_setg(errp, "Either I or E extension must be set"); - return; - } + return; + } - if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m & - cpu->cfg.ext_a & cpu->cfg.ext_f & - cpu->cfg.ext_d)) { + if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m & + cpu->cfg.ext_a & cpu->cfg.ext_f & + cpu->cfg.ext_d)) { warn_report("Setting G will also set IMAFD"); cpu->cfg.ext_i = true; cpu->cfg.ext_m = true; @@ -587,6 +607,34 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) cpu->cfg.ext_d = true; } + if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx || + cpu->cfg.ext_zhinxmin) { + cpu->cfg.ext_zfinx = true; + } + + if (cpu->cfg.ext_zk) { + cpu->cfg.ext_zkn = true; + cpu->cfg.ext_zkr = true; + cpu->cfg.ext_zkt = true; + } + + if (cpu->cfg.ext_zkn) { + cpu->cfg.ext_zbkb = true; + cpu->cfg.ext_zbkc = true; + cpu->cfg.ext_zbkx = true; + cpu->cfg.ext_zkne = true; + cpu->cfg.ext_zknd = true; + cpu->cfg.ext_zknh = true; + } + + if (cpu->cfg.ext_zks) { + cpu->cfg.ext_zbkb = true; + cpu->cfg.ext_zbkc = true; + cpu->cfg.ext_zbkx = true; + cpu->cfg.ext_zksed = true; + cpu->cfg.ext_zksh = true; + } + /* Set the ISA extensions, checks should have happened above */ if (cpu->cfg.ext_i) { ext |= RVI; @@ -665,6 +713,13 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (cpu->cfg.ext_j) { ext |= RVJ; } + if (cpu->cfg.ext_zfinx && ((ext & (RVF | RVD)) || cpu->cfg.ext_zfh || + cpu->cfg.ext_zfhmin)) { + error_setg(errp, + "'Zfinx' cannot be supported together with 'F', 'D', 'Zfh'," + " 'Zfhmin'"); + return; + } set_misa(env, env->misa_mxl, ext); } @@ -694,15 +749,23 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) case IRQ_VS_TIMER: case IRQ_M_TIMER: case IRQ_U_EXT: - case IRQ_S_EXT: case IRQ_VS_EXT: case IRQ_M_EXT: - if (kvm_enabled()) { + if (kvm_enabled()) { kvm_riscv_set_irq(cpu, irq, level); - } else { + } else { riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level)); - } + } break; + case IRQ_S_EXT: + if (kvm_enabled()) { + kvm_riscv_set_irq(cpu, irq, level); + } else { + env->external_seip = level; + riscv_cpu_update_mip(cpu, 1 << irq, + BOOL_TO_MASK(level | env->software_seip)); + } + break; default: g_assert_not_reached(); } @@ -768,12 +831,17 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), + DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), + DEFINE_PROP_UINT32("mvendorid", RISCVCPU, cfg.mvendorid, 0), + DEFINE_PROP_UINT64("marchid", RISCVCPU, cfg.marchid, RISCV_CPU_MARCHID), + DEFINE_PROP_UINT64("mipid", RISCVCPU, cfg.mipid, RISCV_CPU_MIPID), + DEFINE_PROP_BOOL("svinval", RISCVCPU, cfg.ext_svinval, false), DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false), DEFINE_PROP_BOOL("svpbmt", RISCVCPU, cfg.ext_svpbmt, false), @@ -781,7 +849,25 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("zba", RISCVCPU, cfg.ext_zba, true), DEFINE_PROP_BOOL("zbb", RISCVCPU, cfg.ext_zbb, true), DEFINE_PROP_BOOL("zbc", RISCVCPU, cfg.ext_zbc, true), + DEFINE_PROP_BOOL("zbkb", RISCVCPU, cfg.ext_zbkb, false), + DEFINE_PROP_BOOL("zbkc", RISCVCPU, cfg.ext_zbkc, false), + DEFINE_PROP_BOOL("zbkx", RISCVCPU, cfg.ext_zbkx, false), DEFINE_PROP_BOOL("zbs", RISCVCPU, cfg.ext_zbs, true), + DEFINE_PROP_BOOL("zk", RISCVCPU, cfg.ext_zk, false), + DEFINE_PROP_BOOL("zkn", RISCVCPU, cfg.ext_zkn, false), + DEFINE_PROP_BOOL("zknd", RISCVCPU, cfg.ext_zknd, false), + DEFINE_PROP_BOOL("zkne", RISCVCPU, cfg.ext_zkne, false), + DEFINE_PROP_BOOL("zknh", RISCVCPU, cfg.ext_zknh, false), + DEFINE_PROP_BOOL("zkr", RISCVCPU, cfg.ext_zkr, false), + DEFINE_PROP_BOOL("zks", RISCVCPU, cfg.ext_zks, false), + DEFINE_PROP_BOOL("zksed", RISCVCPU, cfg.ext_zksed, false), + DEFINE_PROP_BOOL("zksh", RISCVCPU, cfg.ext_zksh, false), + DEFINE_PROP_BOOL("zkt", RISCVCPU, cfg.ext_zkt, false), + + DEFINE_PROP_BOOL("zdinx", RISCVCPU, cfg.ext_zdinx, false), + DEFINE_PROP_BOOL("zfinx", RISCVCPU, cfg.ext_zfinx, false), + DEFINE_PROP_BOOL("zhinx", RISCVCPU, cfg.ext_zhinx, false), + DEFINE_PROP_BOOL("zhinxmin", RISCVCPU, cfg.ext_zhinxmin, false), /* Vendor-specific custom extensions */ DEFINE_PROP_BOOL("xventanacondops", RISCVCPU, cfg.ext_XVentanaCondOps, false), @@ -848,6 +934,9 @@ static const struct TCGCPUOps riscv_tcg_ops = { .do_interrupt = riscv_cpu_do_interrupt, .do_transaction_failed = riscv_cpu_do_transaction_failed, .do_unaligned_access = riscv_cpu_do_unaligned_access, + .debug_excp_handler = riscv_cpu_debug_excp_handler, + .debug_check_breakpoint = riscv_cpu_debug_check_breakpoint, + .debug_check_watchpoint = riscv_cpu_debug_check_watchpoint, #endif /* !CONFIG_USER_ONLY */ }; @@ -881,18 +970,86 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) device_class_set_props(dc, riscv_cpu_properties); } +#define ISA_EDATA_ENTRY(name, prop) {#name, cpu->cfg.prop} + +static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int max_str_len) +{ + char *old = *isa_str; + char *new = *isa_str; + int i; + + /** + * Here are the ordering rules of extension naming defined by RISC-V + * specification : + * 1. All extensions should be separated from other multi-letter extensions + * by an underscore. + * 2. The first letter following the 'Z' conventionally indicates the most + * closely related alphabetical extension category, IMAFDQLCBKJTPVH. + * If multiple 'Z' extensions are named, they should be ordered first + * by category, then alphabetically within a category. + * 3. Standard supervisor-level extensions (starts with 'S') should be + * listed after standard unprivileged extensions. If multiple + * supervisor-level extensions are listed, they should be ordered + * alphabetically. + * 4. Non-standard extensions (starts with 'X') must be listed after all + * standard extensions. They must be separated from other multi-letter + * extensions by an underscore. + */ + struct isa_ext_data isa_edata_arr[] = { + ISA_EDATA_ENTRY(zfh, ext_zfh), + ISA_EDATA_ENTRY(zfhmin, ext_zfhmin), + ISA_EDATA_ENTRY(zfinx, ext_zfinx), + ISA_EDATA_ENTRY(zhinx, ext_zhinx), + ISA_EDATA_ENTRY(zhinxmin, ext_zhinxmin), + ISA_EDATA_ENTRY(zdinx, ext_zdinx), + ISA_EDATA_ENTRY(zba, ext_zba), + ISA_EDATA_ENTRY(zbb, ext_zbb), + ISA_EDATA_ENTRY(zbc, ext_zbc), + ISA_EDATA_ENTRY(zbkb, ext_zbkb), + ISA_EDATA_ENTRY(zbkc, ext_zbkc), + ISA_EDATA_ENTRY(zbkx, ext_zbkx), + ISA_EDATA_ENTRY(zbs, ext_zbs), + ISA_EDATA_ENTRY(zk, ext_zk), + ISA_EDATA_ENTRY(zkn, ext_zkn), + ISA_EDATA_ENTRY(zknd, ext_zknd), + ISA_EDATA_ENTRY(zkne, ext_zkne), + ISA_EDATA_ENTRY(zknh, ext_zknh), + ISA_EDATA_ENTRY(zkr, ext_zkr), + ISA_EDATA_ENTRY(zks, ext_zks), + ISA_EDATA_ENTRY(zksed, ext_zksed), + ISA_EDATA_ENTRY(zksh, ext_zksh), + ISA_EDATA_ENTRY(zkt, ext_zkt), + ISA_EDATA_ENTRY(zve32f, ext_zve32f), + ISA_EDATA_ENTRY(zve64f, ext_zve64f), + ISA_EDATA_ENTRY(svinval, ext_svinval), + ISA_EDATA_ENTRY(svnapot, ext_svnapot), + ISA_EDATA_ENTRY(svpbmt, ext_svpbmt), + }; + + for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { + if (isa_edata_arr[i].enabled) { + new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL); + g_free(old); + old = new; + } + } + + *isa_str = new; +} + char *riscv_isa_string(RISCVCPU *cpu) { int i; - const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1; + const size_t maxlen = sizeof("rv128") + sizeof(riscv_single_letter_exts); char *isa_str = g_new(char, maxlen); char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS); - for (i = 0; i < sizeof(riscv_exts); i++) { - if (cpu->env.misa_ext & RV(riscv_exts[i])) { - *p++ = qemu_tolower(riscv_exts[i]); + for (i = 0; i < sizeof(riscv_single_letter_exts) - 1; i++) { + if (cpu->env.misa_ext & RV(riscv_single_letter_exts[i])) { + *p++ = qemu_tolower(riscv_single_letter_exts[i]); } } *p = '\0'; + riscv_isa_string_ext(cpu, &isa_str, maxlen); return isa_str; } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 8183fb86d5..fe6c9a2c92 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -23,7 +23,7 @@ #include "hw/core/cpu.h" #include "hw/registerfields.h" #include "exec/cpu-defs.h" -#include "fpu/softfloat-types.h" +#include "qemu/cpu-float.h" #include "qom/object.h" #include "qemu/int128.h" #include "cpu_bits.h" @@ -79,11 +79,16 @@ enum { RISCV_FEATURE_PMP, RISCV_FEATURE_EPMP, RISCV_FEATURE_MISA, - RISCV_FEATURE_AIA + RISCV_FEATURE_AIA, + RISCV_FEATURE_DEBUG }; -#define PRIV_VERSION_1_10_0 0x00011000 -#define PRIV_VERSION_1_11_0 0x00011100 +/* Privileged specification version */ +enum { + PRIV_VERSION_1_10_0 = 0, + PRIV_VERSION_1_11_0, + PRIV_VERSION_1_12_0, +}; #define VEXT_VERSION_1_00_0 0x00010000 @@ -98,10 +103,11 @@ enum { #define MAX_RISCV_PMPS (16) -typedef struct CPURISCVState CPURISCVState; +typedef struct CPUArchState CPURISCVState; #if !defined(CONFIG_USER_ONLY) #include "pmp.h" +#include "debug.h" #endif #define RV_VLEN_MAX 1024 @@ -113,7 +119,7 @@ FIELD(VTYPE, VMA, 7, 1) FIELD(VTYPE, VEDIV, 8, 2) FIELD(VTYPE, RESERVED, 10, sizeof(target_ulong) * 8 - 11) -struct CPURISCVState { +struct CPUArchState { target_ulong gpr[32]; target_ulong gprh[32]; /* 64 top bits of the 128-bit registers */ uint64_t fpr[32]; /* assume both F and D extensions */ @@ -173,6 +179,14 @@ struct CPURISCVState { uint64_t mstatus; uint64_t mip; + /* + * MIP contains the software writable version of SEIP ORed with the + * external interrupt value. The MIP register is always up-to-date. + * To keep track of the current source, we also save booleans of the values + * here. + */ + bool external_seip; + bool software_seip; uint64_t miclaim; @@ -267,9 +281,13 @@ struct CPURISCVState { pmp_table_t pmp_state; target_ulong mseccfg; + /* trigger module */ + target_ulong trigger_cur; + type2_trigger_t type2_trig[TRIGGER_TYPE2_NUM]; + /* machine specific rdtime callback */ - uint64_t (*rdtime_fn)(uint32_t); - uint32_t rdtime_fn_arg; + uint64_t (*rdtime_fn)(void *); + void *rdtime_fn_arg; /* machine specific AIA ireg read-modify-write callback */ #define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \ @@ -300,6 +318,11 @@ struct CPURISCVState { target_ulong spmbase; target_ulong upmmask; target_ulong upmbase; + + /* CSRs for execution enviornment configuration */ + uint64_t menvcfg; + target_ulong senvcfg; + uint64_t henvcfg; #endif target_ulong cur_pmmask; target_ulong cur_pmbase; @@ -320,8 +343,7 @@ struct CPURISCVState { uint64_t kvm_timer_frequency; }; -OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass, - RISCV_CPU) +OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) /** * RISCVCPUClass: @@ -355,18 +377,39 @@ struct RISCVCPUConfig { bool ext_zba; bool ext_zbb; bool ext_zbc; + bool ext_zbkb; + bool ext_zbkc; + bool ext_zbkx; bool ext_zbs; + bool ext_zk; + bool ext_zkn; + bool ext_zknd; + bool ext_zkne; + bool ext_zknh; + bool ext_zkr; + bool ext_zks; + bool ext_zksed; + bool ext_zksh; + bool ext_zkt; bool ext_counters; bool ext_ifencei; bool ext_icsr; bool ext_svinval; bool ext_svnapot; bool ext_svpbmt; + bool ext_zdinx; bool ext_zfh; bool ext_zfhmin; + bool ext_zfinx; + bool ext_zhinx; + bool ext_zhinxmin; bool ext_zve32f; bool ext_zve64f; + uint32_t mvendorid; + uint64_t marchid; + uint64_t mipid; + /* Vendor-specific custom extensions */ bool ext_XVentanaCondOps; @@ -380,6 +423,7 @@ struct RISCVCPUConfig { bool pmp; bool epmp; bool aia; + bool debug; uint64_t resetvec; }; @@ -391,7 +435,7 @@ typedef struct RISCVCPUConfig RISCVCPUConfig; * * A RISCV CPU. */ -struct RISCVCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -448,9 +492,9 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); bool riscv_cpu_two_stage_lookup(int mmu_idx); int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); @@ -471,8 +515,8 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ -void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), - uint32_t arg); +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), + void *arg); void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, int (*rmw_fn)(void *arg, target_ulong reg, @@ -484,8 +528,8 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); void riscv_translate_init(void); -void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, - uint32_t exception, uintptr_t pc); +G_NORETURN void riscv_raise_exception(CPURISCVState *env, + uint32_t exception, uintptr_t pc); target_ulong riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); @@ -495,8 +539,6 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); #define TB_FLAGS_MSTATUS_FS MSTATUS_FS #define TB_FLAGS_MSTATUS_VS MSTATUS_VS -typedef CPURISCVState CPUArchState; -typedef RISCVCPU ArchCPU; #include "exec/cpu-all.h" FIELD(TB_FLAGS, MEM_IDX, 0, 3) @@ -653,6 +695,8 @@ typedef struct { riscv_csr_op_fn op; riscv_csr_read128_fn read128; riscv_csr_write128_fn write128; + /* The default priv spec version should be PRIV_VERSION_1_10_0 (i.e 0) */ + uint32_t min_priv_ver; } riscv_csr_operations; /* CSR function table constants */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 0fe01d7da5..4d04b20d06 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -148,6 +148,7 @@ #define CSR_MARCHID 0xf12 #define CSR_MIMPID 0xf13 #define CSR_MHARTID 0xf14 +#define CSR_MCONFIGPTR 0xf15 /* Machine Trap Setup */ #define CSR_MSTATUS 0x300 @@ -201,6 +202,9 @@ #define CSR_STVEC 0x105 #define CSR_SCOUNTEREN 0x106 +/* Supervisor Configuration CSRs */ +#define CSR_SENVCFG 0x10A + /* Supervisor Trap Handling */ #define CSR_SSCRATCH 0x140 #define CSR_SEPC 0x141 @@ -246,6 +250,10 @@ #define CSR_HTIMEDELTA 0x605 #define CSR_HTIMEDELTAH 0x615 +/* Hypervisor Configuration CSRs */ +#define CSR_HENVCFG 0x60A +#define CSR_HENVCFGH 0x61A + /* Virtual CSRs */ #define CSR_VSSTATUS 0x200 #define CSR_VSIE 0x204 @@ -289,6 +297,10 @@ #define CSR_VSIEH 0x214 #define CSR_VSIPH 0x254 +/* Machine Configuration CSRs */ +#define CSR_MENVCFG 0x30A +#define CSR_MENVCFGH 0x31A + /* Enhanced Physical Memory Protection (ePMP) */ #define CSR_MSECCFG 0x747 #define CSR_MSECCFGH 0x757 @@ -446,6 +458,9 @@ #define CSR_VSPMMASK 0x2c1 #define CSR_VSPMBASE 0x2c2 +/* Crypto Extension */ +#define CSR_SEED 0x015 + /* mstatus CSR bits */ #define MSTATUS_UIE 0x00000001 #define MSTATUS_SIE 0x00000002 @@ -662,6 +677,34 @@ typedef enum RISCVException { #define PM_EXT_CLEAN 0x00000002ULL #define PM_EXT_DIRTY 0x00000003ULL +/* Execution enviornment configuration bits */ +#define MENVCFG_FIOM BIT(0) +#define MENVCFG_CBIE (3UL << 4) +#define MENVCFG_CBCFE BIT(6) +#define MENVCFG_CBZE BIT(7) +#define MENVCFG_PBMTE (1ULL << 62) +#define MENVCFG_STCE (1ULL << 63) + +/* For RV32 */ +#define MENVCFGH_PBMTE BIT(30) +#define MENVCFGH_STCE BIT(31) + +#define SENVCFG_FIOM MENVCFG_FIOM +#define SENVCFG_CBIE MENVCFG_CBIE +#define SENVCFG_CBCFE MENVCFG_CBCFE +#define SENVCFG_CBZE MENVCFG_CBZE + +#define HENVCFG_FIOM MENVCFG_FIOM +#define HENVCFG_CBIE MENVCFG_CBIE +#define HENVCFG_CBCFE MENVCFG_CBCFE +#define HENVCFG_CBZE MENVCFG_CBZE +#define HENVCFG_PBMTE MENVCFG_PBMTE +#define HENVCFG_STCE MENVCFG_STCE + +/* For RV32 */ +#define HENVCFGH_PBMTE MENVCFGH_PBMTE +#define HENVCFGH_STCE MENVCFGH_STCE + /* Offsets for every pair of control bits per each priv level */ #define XS_OFFSET 0ULL #define U_OFFSET 2ULL @@ -760,4 +803,10 @@ typedef enum RISCVException { #define HVICTL_VALID_MASK \ (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO) +/* seed CSR bits */ +#define SEED_OPST (0b11 << 30) +#define SEED_OPST_BIST (0b00 << 30) +#define SEED_OPST_WAIT (0b01 << 30) +#define SEED_OPST_ES16 (0b10 << 30) +#define SEED_OPST_DEAD (0b11 << 30) #endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 746335bfd6..e1aa4f2097 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -466,9 +466,13 @@ bool riscv_cpu_vector_enabled(CPURISCVState *env) void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) { - uint64_t mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS | + uint64_t mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE | MSTATUS64_UXL | MSTATUS_VS; + + if (riscv_has_ext(env, RVF)) { + mstatus_mask |= MSTATUS_FS; + } bool current_virt = riscv_cpu_virt_enabled(env); g_assert(riscv_has_ext(env, RVH)); @@ -628,8 +632,8 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value) return old; } -void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), - uint32_t arg) +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), + void *arg) { env->rdtime_fn = fn; env->rdtime_fn_arg = arg; @@ -1146,7 +1150,7 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, env->badaddr = addr; env->two_stage_lookup = riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx); - riscv_raise_exception(&cpu->env, cs->exception_index, retaddr); + cpu_loop_exit_restore(cs, retaddr); } void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, @@ -1171,7 +1175,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, env->badaddr = addr; env->two_stage_lookup = riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx); - riscv_raise_exception(env, cs->exception_index, retaddr); + cpu_loop_exit_restore(cs, retaddr); } bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -1307,7 +1311,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, first_stage_error, riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx)); - riscv_raise_exception(env, cs->exception_index, retaddr); + cpu_loop_exit_restore(cs, retaddr); } return true; diff --git a/target/riscv/crypto_helper.c b/target/riscv/crypto_helper.c new file mode 100644 index 0000000000..2ef30281b1 --- /dev/null +++ b/target/riscv/crypto_helper.c @@ -0,0 +1,302 @@ +/* + * RISC-V Crypto Emulation Helpers for QEMU. + * + * Copyright (c) 2021 Ruibo Lu, luruibo2000@163.com + * Copyright (c) 2021 Zewen Ye, lustrew@foxmail.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "crypto/aes.h" +#include "crypto/sm4.h" + +#define AES_XTIME(a) \ + ((a << 1) ^ ((a & 0x80) ? 0x1b : 0)) + +#define AES_GFMUL(a, b) (( \ + (((b) & 0x1) ? (a) : 0) ^ \ + (((b) & 0x2) ? AES_XTIME(a) : 0) ^ \ + (((b) & 0x4) ? AES_XTIME(AES_XTIME(a)) : 0) ^ \ + (((b) & 0x8) ? AES_XTIME(AES_XTIME(AES_XTIME(a))) : 0)) & 0xFF) + +static inline uint32_t aes_mixcolumn_byte(uint8_t x, bool fwd) +{ + uint32_t u; + + if (fwd) { + u = (AES_GFMUL(x, 3) << 24) | (x << 16) | (x << 8) | + (AES_GFMUL(x, 2) << 0); + } else { + u = (AES_GFMUL(x, 0xb) << 24) | (AES_GFMUL(x, 0xd) << 16) | + (AES_GFMUL(x, 0x9) << 8) | (AES_GFMUL(x, 0xe) << 0); + } + return u; +} + +#define sext32_xlen(x) (target_ulong)(int32_t)(x) + +static inline target_ulong aes32_operation(target_ulong shamt, + target_ulong rs1, target_ulong rs2, + bool enc, bool mix) +{ + uint8_t si = rs2 >> shamt; + uint8_t so; + uint32_t mixed; + target_ulong res; + + if (enc) { + so = AES_sbox[si]; + if (mix) { + mixed = aes_mixcolumn_byte(so, true); + } else { + mixed = so; + } + } else { + so = AES_isbox[si]; + if (mix) { + mixed = aes_mixcolumn_byte(so, false); + } else { + mixed = so; + } + } + mixed = rol32(mixed, shamt); + res = rs1 ^ mixed; + + return sext32_xlen(res); +} + +target_ulong HELPER(aes32esmi)(target_ulong rs1, target_ulong rs2, + target_ulong shamt) +{ + return aes32_operation(shamt, rs1, rs2, true, true); +} + +target_ulong HELPER(aes32esi)(target_ulong rs1, target_ulong rs2, + target_ulong shamt) +{ + return aes32_operation(shamt, rs1, rs2, true, false); +} + +target_ulong HELPER(aes32dsmi)(target_ulong rs1, target_ulong rs2, + target_ulong shamt) +{ + return aes32_operation(shamt, rs1, rs2, false, true); +} + +target_ulong HELPER(aes32dsi)(target_ulong rs1, target_ulong rs2, + target_ulong shamt) +{ + return aes32_operation(shamt, rs1, rs2, false, false); +} + +#define BY(X, I) ((X >> (8 * I)) & 0xFF) + +#define AES_SHIFROWS_LO(RS1, RS2) ( \ + (((RS1 >> 24) & 0xFF) << 56) | (((RS2 >> 48) & 0xFF) << 48) | \ + (((RS2 >> 8) & 0xFF) << 40) | (((RS1 >> 32) & 0xFF) << 32) | \ + (((RS2 >> 56) & 0xFF) << 24) | (((RS2 >> 16) & 0xFF) << 16) | \ + (((RS1 >> 40) & 0xFF) << 8) | (((RS1 >> 0) & 0xFF) << 0)) + +#define AES_INVSHIFROWS_LO(RS1, RS2) ( \ + (((RS2 >> 24) & 0xFF) << 56) | (((RS2 >> 48) & 0xFF) << 48) | \ + (((RS1 >> 8) & 0xFF) << 40) | (((RS1 >> 32) & 0xFF) << 32) | \ + (((RS1 >> 56) & 0xFF) << 24) | (((RS2 >> 16) & 0xFF) << 16) | \ + (((RS2 >> 40) & 0xFF) << 8) | (((RS1 >> 0) & 0xFF) << 0)) + +#define AES_MIXBYTE(COL, B0, B1, B2, B3) ( \ + BY(COL, B3) ^ BY(COL, B2) ^ AES_GFMUL(BY(COL, B1), 3) ^ \ + AES_GFMUL(BY(COL, B0), 2)) + +#define AES_MIXCOLUMN(COL) ( \ + AES_MIXBYTE(COL, 3, 0, 1, 2) << 24 | \ + AES_MIXBYTE(COL, 2, 3, 0, 1) << 16 | \ + AES_MIXBYTE(COL, 1, 2, 3, 0) << 8 | AES_MIXBYTE(COL, 0, 1, 2, 3) << 0) + +#define AES_INVMIXBYTE(COL, B0, B1, B2, B3) ( \ + AES_GFMUL(BY(COL, B3), 0x9) ^ AES_GFMUL(BY(COL, B2), 0xd) ^ \ + AES_GFMUL(BY(COL, B1), 0xb) ^ AES_GFMUL(BY(COL, B0), 0xe)) + +#define AES_INVMIXCOLUMN(COL) ( \ + AES_INVMIXBYTE(COL, 3, 0, 1, 2) << 24 | \ + AES_INVMIXBYTE(COL, 2, 3, 0, 1) << 16 | \ + AES_INVMIXBYTE(COL, 1, 2, 3, 0) << 8 | \ + AES_INVMIXBYTE(COL, 0, 1, 2, 3) << 0) + +static inline target_ulong aes64_operation(target_ulong rs1, target_ulong rs2, + bool enc, bool mix) +{ + uint64_t RS1 = rs1; + uint64_t RS2 = rs2; + uint64_t result; + uint64_t temp; + uint32_t col_0; + uint32_t col_1; + + if (enc) { + temp = AES_SHIFROWS_LO(RS1, RS2); + temp = (((uint64_t)AES_sbox[(temp >> 0) & 0xFF] << 0) | + ((uint64_t)AES_sbox[(temp >> 8) & 0xFF] << 8) | + ((uint64_t)AES_sbox[(temp >> 16) & 0xFF] << 16) | + ((uint64_t)AES_sbox[(temp >> 24) & 0xFF] << 24) | + ((uint64_t)AES_sbox[(temp >> 32) & 0xFF] << 32) | + ((uint64_t)AES_sbox[(temp >> 40) & 0xFF] << 40) | + ((uint64_t)AES_sbox[(temp >> 48) & 0xFF] << 48) | + ((uint64_t)AES_sbox[(temp >> 56) & 0xFF] << 56)); + if (mix) { + col_0 = temp & 0xFFFFFFFF; + col_1 = temp >> 32; + + col_0 = AES_MIXCOLUMN(col_0); + col_1 = AES_MIXCOLUMN(col_1); + + result = ((uint64_t)col_1 << 32) | col_0; + } else { + result = temp; + } + } else { + temp = AES_INVSHIFROWS_LO(RS1, RS2); + temp = (((uint64_t)AES_isbox[(temp >> 0) & 0xFF] << 0) | + ((uint64_t)AES_isbox[(temp >> 8) & 0xFF] << 8) | + ((uint64_t)AES_isbox[(temp >> 16) & 0xFF] << 16) | + ((uint64_t)AES_isbox[(temp >> 24) & 0xFF] << 24) | + ((uint64_t)AES_isbox[(temp >> 32) & 0xFF] << 32) | + ((uint64_t)AES_isbox[(temp >> 40) & 0xFF] << 40) | + ((uint64_t)AES_isbox[(temp >> 48) & 0xFF] << 48) | + ((uint64_t)AES_isbox[(temp >> 56) & 0xFF] << 56)); + if (mix) { + col_0 = temp & 0xFFFFFFFF; + col_1 = temp >> 32; + + col_0 = AES_INVMIXCOLUMN(col_0); + col_1 = AES_INVMIXCOLUMN(col_1); + + result = ((uint64_t)col_1 << 32) | col_0; + } else { + result = temp; + } + } + + return result; +} + +target_ulong HELPER(aes64esm)(target_ulong rs1, target_ulong rs2) +{ + return aes64_operation(rs1, rs2, true, true); +} + +target_ulong HELPER(aes64es)(target_ulong rs1, target_ulong rs2) +{ + return aes64_operation(rs1, rs2, true, false); +} + +target_ulong HELPER(aes64ds)(target_ulong rs1, target_ulong rs2) +{ + return aes64_operation(rs1, rs2, false, false); +} + +target_ulong HELPER(aes64dsm)(target_ulong rs1, target_ulong rs2) +{ + return aes64_operation(rs1, rs2, false, true); +} + +target_ulong HELPER(aes64ks2)(target_ulong rs1, target_ulong rs2) +{ + uint64_t RS1 = rs1; + uint64_t RS2 = rs2; + uint32_t rs1_hi = RS1 >> 32; + uint32_t rs2_lo = RS2; + uint32_t rs2_hi = RS2 >> 32; + + uint32_t r_lo = (rs1_hi ^ rs2_lo); + uint32_t r_hi = (rs1_hi ^ rs2_lo ^ rs2_hi); + target_ulong result = ((uint64_t)r_hi << 32) | r_lo; + + return result; +} + +target_ulong HELPER(aes64ks1i)(target_ulong rs1, target_ulong rnum) +{ + uint64_t RS1 = rs1; + static const uint8_t round_consts[10] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 + }; + + uint8_t enc_rnum = rnum; + uint32_t temp = (RS1 >> 32) & 0xFFFFFFFF; + uint8_t rcon_ = 0; + target_ulong result; + + if (enc_rnum != 0xA) { + temp = ror32(temp, 8); /* Rotate right by 8 */ + rcon_ = round_consts[enc_rnum]; + } + + temp = ((uint32_t)AES_sbox[(temp >> 24) & 0xFF] << 24) | + ((uint32_t)AES_sbox[(temp >> 16) & 0xFF] << 16) | + ((uint32_t)AES_sbox[(temp >> 8) & 0xFF] << 8) | + ((uint32_t)AES_sbox[(temp >> 0) & 0xFF] << 0); + + temp ^= rcon_; + + result = ((uint64_t)temp << 32) | temp; + + return result; +} + +target_ulong HELPER(aes64im)(target_ulong rs1) +{ + uint64_t RS1 = rs1; + uint32_t col_0 = RS1 & 0xFFFFFFFF; + uint32_t col_1 = RS1 >> 32; + target_ulong result; + + col_0 = AES_INVMIXCOLUMN(col_0); + col_1 = AES_INVMIXCOLUMN(col_1); + + result = ((uint64_t)col_1 << 32) | col_0; + + return result; +} + +target_ulong HELPER(sm4ed)(target_ulong rs1, target_ulong rs2, + target_ulong shamt) +{ + uint32_t sb_in = (uint8_t)(rs2 >> shamt); + uint32_t sb_out = (uint32_t)sm4_sbox[sb_in]; + + uint32_t x = sb_out ^ (sb_out << 8) ^ (sb_out << 2) ^ (sb_out << 18) ^ + ((sb_out & 0x3f) << 26) ^ ((sb_out & 0xC0) << 10); + + uint32_t rotl = rol32(x, shamt); + + return sext32_xlen(rotl ^ (uint32_t)rs1); +} + +target_ulong HELPER(sm4ks)(target_ulong rs1, target_ulong rs2, + target_ulong shamt) +{ + uint32_t sb_in = (uint8_t)(rs2 >> shamt); + uint32_t sb_out = sm4_sbox[sb_in]; + + uint32_t x = sb_out ^ ((sb_out & 0x07) << 29) ^ ((sb_out & 0xFE) << 7) ^ + ((sb_out & 0x01) << 23) ^ ((sb_out & 0xF8) << 13); + + uint32_t rotl = rol32(x, shamt); + + return sext32_xlen(rotl ^ (uint32_t)rs1); +} +#undef sext32_xlen diff --git a/target/riscv/csr.c b/target/riscv/csr.c index a938760a3f..3500e07f92 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -23,6 +23,9 @@ #include "cpu.h" #include "qemu/main-loop.h" #include "exec/exec-all.h" +#include "sysemu/cpu-timers.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) @@ -39,7 +42,8 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) static RISCVException fs(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) - if (!env->debugger && !riscv_cpu_fp_enabled(env)) { + if (!env->debugger && !riscv_cpu_fp_enabled(env) && + !RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { return RISCV_EXCP_ILLEGAL_INST; } #endif @@ -288,8 +292,57 @@ static RISCVException epmp(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } + +static RISCVException debug(CPURISCVState *env, int csrno) +{ + if (riscv_feature(env, RISCV_FEATURE_DEBUG)) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} #endif +static RISCVException seed(CPURISCVState *env, int csrno) +{ + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_zkr) { + return RISCV_EXCP_ILLEGAL_INST; + } + +#if !defined(CONFIG_USER_ONLY) + /* + * With a CSR read-write instruction: + * 1) The seed CSR is always available in machine mode as normal. + * 2) Attempted access to seed from virtual modes VS and VU always raises + * an exception(virtual instruction exception only if mseccfg.sseed=1). + * 3) Without the corresponding access control bit set to 1, any attempted + * access to seed from U, S or HS modes will raise an illegal instruction + * exception. + */ + if (env->priv == PRV_M) { + return RISCV_EXCP_NONE; + } else if (riscv_cpu_virt_enabled(env)) { + if (env->mseccfg & MSECCFG_SSEED) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } else { + return RISCV_EXCP_ILLEGAL_INST; + } + } else { + if (env->priv == PRV_S && (env->mseccfg & MSECCFG_SSEED)) { + return RISCV_EXCP_NONE; + } else if (env->priv == PRV_U && (env->mseccfg & MSECCFG_USEED)) { + return RISCV_EXCP_NONE; + } else { + return RISCV_EXCP_ILLEGAL_INST; + } + } +#else + return RISCV_EXCP_NONE; +#endif +} + /* User Floating-Point CSRs */ static RISCVException read_fflags(CPURISCVState *env, int csrno, target_ulong *val) @@ -302,7 +355,9 @@ static RISCVException write_fflags(CPURISCVState *env, int csrno, target_ulong val) { #if !defined(CONFIG_USER_ONLY) - env->mstatus |= MSTATUS_FS; + if (riscv_has_ext(env, RVF)) { + env->mstatus |= MSTATUS_FS; + } #endif riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); return RISCV_EXCP_NONE; @@ -319,7 +374,9 @@ static RISCVException write_frm(CPURISCVState *env, int csrno, target_ulong val) { #if !defined(CONFIG_USER_ONLY) - env->mstatus |= MSTATUS_FS; + if (riscv_has_ext(env, RVF)) { + env->mstatus |= MSTATUS_FS; + } #endif env->frm = val & (FSR_RD >> FSR_RD_SHIFT); return RISCV_EXCP_NONE; @@ -337,7 +394,9 @@ static RISCVException write_fcsr(CPURISCVState *env, int csrno, target_ulong val) { #if !defined(CONFIG_USER_ONLY) - env->mstatus |= MSTATUS_FS; + if (riscv_has_ext(env, RVF)) { + env->mstatus |= MSTATUS_FS; + } #endif env->frm = (val & FSR_RD) >> FSR_RD_SHIFT; riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); @@ -595,6 +654,36 @@ static RISCVException write_ignore(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_mvendorid(CPURISCVState *env, int csrno, + target_ulong *val) +{ + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + + *val = cpu->cfg.mvendorid; + return RISCV_EXCP_NONE; +} + +static RISCVException read_marchid(CPURISCVState *env, int csrno, + target_ulong *val) +{ + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + + *val = cpu->cfg.marchid; + return RISCV_EXCP_NONE; +} + +static RISCVException read_mipid(CPURISCVState *env, int csrno, + target_ulong *val) +{ + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + + *val = cpu->cfg.mipid; + return RISCV_EXCP_NONE; +} + static RISCVException read_mhartid(CPURISCVState *env, int csrno, target_ulong *val) { @@ -653,10 +742,14 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, tlb_flush(env_cpu(env)); } mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | - MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | + MSTATUS_SPP | MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR | MSTATUS_TW | MSTATUS_VS; + if (riscv_has_ext(env, RVF)) { + mask |= MSTATUS_FS; + } + if (xl != MXL_RV32 || env->debugger) { /* * RV32: MPV and GVA are not in mstatus. The current plan is to @@ -788,6 +881,10 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } + if (!(val & RVF)) { + env->mstatus &= ~MSTATUS_FS; + } + /* flush translation cache */ tb_flush(env_cpu(env)); env->misa_ext = val; @@ -1382,15 +1479,114 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +/* Execution environment configuration setup */ +static RISCVException read_menvcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->menvcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_menvcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE; + + if (riscv_cpu_mxl(env) == MXL_RV64) { + mask |= MENVCFG_PBMTE | MENVCFG_STCE; + } + env->menvcfg = (env->menvcfg & ~mask) | (val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->menvcfg >> 32; + return RISCV_EXCP_NONE; +} + +static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE; + uint64_t valh = (uint64_t)val << 32; + + env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_senvcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->senvcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_senvcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE; + + env->senvcfg = (env->senvcfg & ~mask) | (val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_henvcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->henvcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_henvcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; + + if (riscv_cpu_mxl(env) == MXL_RV64) { + mask |= HENVCFG_PBMTE | HENVCFG_STCE; + } + + env->henvcfg = (env->henvcfg & ~mask) | (val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->henvcfg >> 32; + return RISCV_EXCP_NONE; +} + +static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE; + uint64_t valh = (uint64_t)val << 32; + + env->henvcfg = (env->henvcfg & ~mask) | (valh & mask); + + return RISCV_EXCP_NONE; +} + static RISCVException rmw_mip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { RISCVCPU *cpu = env_archcpu(env); - /* Allow software control of delegable interrupts not claimed by hardware */ - uint64_t old_mip, mask = wr_mask & delegable_ints & ~env->miclaim; + uint64_t old_mip, mask = wr_mask & delegable_ints; uint32_t gin; + if (mask & MIP_SEIP) { + env->software_seip = new_val & MIP_SEIP; + new_val |= env->external_seip * MIP_SEIP; + } + if (mask) { old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask)); } else { @@ -1828,7 +2024,7 @@ static RISCVException read_satp(CPURISCVState *env, int csrno, static RISCVException write_satp(CPURISCVState *env, int csrno, target_ulong val) { - target_ulong vm, mask, asid; + target_ulong vm, mask; if (!riscv_feature(env, RISCV_FEATURE_MMU)) { return RISCV_EXCP_NONE; @@ -1837,20 +2033,22 @@ static RISCVException write_satp(CPURISCVState *env, int csrno, if (riscv_cpu_mxl(env) == MXL_RV32) { vm = validate_vm(env, get_field(val, SATP32_MODE)); mask = (val ^ env->satp) & (SATP32_MODE | SATP32_ASID | SATP32_PPN); - asid = (val ^ env->satp) & SATP32_ASID; } else { vm = validate_vm(env, get_field(val, SATP64_MODE)); mask = (val ^ env->satp) & (SATP64_MODE | SATP64_ASID | SATP64_PPN); - asid = (val ^ env->satp) & SATP64_ASID; } if (vm && mask) { if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { return RISCV_EXCP_ILLEGAL_INST; } else { - if (asid) { - tlb_flush(env_cpu(env)); - } + /* + * The ISA defines SATP.MODE=Bare as "no translation", but we still + * pass these through QEMU's TLB emulation as it improves + * performance. Flushing the TLB on SATP writes with paging + * enabled avoids leaking those invalid cached mappings. + */ + tlb_flush(env_cpu(env)); env->satp = val; } } @@ -2560,6 +2758,48 @@ static RISCVException write_pmpaddr(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_tselect(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = tselect_csr_read(env); + return RISCV_EXCP_NONE; +} + +static RISCVException write_tselect(CPURISCVState *env, int csrno, + target_ulong val) +{ + tselect_csr_write(env, val); + return RISCV_EXCP_NONE; +} + +static RISCVException read_tdata(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* return 0 in tdata1 to end the trigger enumeration */ + if (env->trigger_cur >= TRIGGER_NUM && csrno == CSR_TDATA1) { + *val = 0; + return RISCV_EXCP_NONE; + } + + if (!tdata_available(env, csrno - CSR_TDATA1)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + *val = tdata_csr_read(env, csrno - CSR_TDATA1); + return RISCV_EXCP_NONE; +} + +static RISCVException write_tdata(CPURISCVState *env, int csrno, + target_ulong val) +{ + if (!tdata_available(env, csrno - CSR_TDATA1)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + tdata_csr_write(env, csrno - CSR_TDATA1, val); + return RISCV_EXCP_NONE; +} + /* * Functions to access Pointer Masking feature registers * We have to check if current priv lvl could modify @@ -2846,6 +3086,41 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno, #endif +/* Crypto Extension */ +static RISCVException rmw_seed(CPURISCVState *env, int csrno, + target_ulong *ret_value, + target_ulong new_value, + target_ulong write_mask) +{ + uint16_t random_v; + Error *random_e = NULL; + int random_r; + target_ulong rval; + + random_r = qemu_guest_getrandom(&random_v, 2, &random_e); + if (unlikely(random_r < 0)) { + /* + * Failed, for unknown reasons in the crypto subsystem. + * The best we can do is log the reason and return a + * failure indication to the guest. There is no reason + * we know to expect the failure to be transitory, so + * indicate DEAD to avoid having the guest spin on WAIT. + */ + qemu_log_mask(LOG_UNIMP, "%s: Crypto failure: %s", + __func__, error_get_pretty(random_e)); + error_free(random_e); + rval = SEED_OPST_DEAD; + } else { + rval = random_v | SEED_OPST_ES16; + } + + if (ret_value) { + *ret_value = rval; + } + + return RISCV_EXCP_NONE; +} + /* * riscv_csrrw - read and/or update control and status register * @@ -2862,6 +3137,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, { /* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */ int read_only = get_field(csrno, 0xC00) == 3; + int csr_min_priv = csr_ops[csrno].min_priv_ver; #if !defined(CONFIG_USER_ONLY) int effective_priv = env->priv; @@ -2894,6 +3170,10 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, return RISCV_EXCP_ILLEGAL_INST; } + if (env->priv_ver < csr_min_priv) { + return RISCV_EXCP_ILLEGAL_INST; + } + return csr_ops[csrno].predicate(env, csrno); } @@ -3052,13 +3332,20 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_FRM] = { "frm", fs, read_frm, write_frm }, [CSR_FCSR] = { "fcsr", fs, read_fcsr, write_fcsr }, /* Vector CSRs */ - [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart }, - [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat }, - [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm }, - [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr }, - [CSR_VL] = { "vl", vs, read_vl }, - [CSR_VTYPE] = { "vtype", vs, read_vtype }, - [CSR_VLENB] = { "vlenb", vs, read_vlenb }, + [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VL] = { "vl", vs, read_vl, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VTYPE] = { "vtype", vs, read_vtype, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VLENB] = { "vlenb", vs, read_vlenb, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* User Timers and Counters */ [CSR_CYCLE] = { "cycle", ctr, read_instret }, [CSR_INSTRET] = { "instret", ctr, read_instret }, @@ -3072,6 +3359,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_TIME] = { "time", ctr, read_time }, [CSR_TIMEH] = { "timeh", ctr32, read_timeh }, + /* Crypto Extension */ + [CSR_SEED] = { "seed", seed, NULL, NULL, rmw_seed }, + #if !defined(CONFIG_USER_ONLY) /* Machine Timers and Counters */ [CSR_MCYCLE] = { "mcycle", any, read_instret }, @@ -3080,11 +3370,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MINSTRETH] = { "minstreth", any32, read_instreth }, /* Machine Information Registers */ - [CSR_MVENDORID] = { "mvendorid", any, read_zero }, - [CSR_MARCHID] = { "marchid", any, read_zero }, - [CSR_MIMPID] = { "mimpid", any, read_zero }, - [CSR_MHARTID] = { "mhartid", any, read_mhartid }, + [CSR_MVENDORID] = { "mvendorid", any, read_mvendorid }, + [CSR_MARCHID] = { "marchid", any, read_marchid }, + [CSR_MIMPID] = { "mimpid", any, read_mipid }, + [CSR_MHARTID] = { "mhartid", any, read_mhartid }, + [CSR_MCONFIGPTR] = { "mconfigptr", any, read_zero, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Machine Trap Setup */ [CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, NULL, read_mstatus_i128 }, @@ -3131,6 +3423,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore }, [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, + /* Execution environment configuration */ + [CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HENVCFG] = { "henvcfg", hmode, read_henvcfg, write_henvcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Supervisor Trap Setup */ [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, read_sstatus_i128 }, @@ -3167,33 +3471,58 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, [CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph }, - [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus }, - [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg }, - [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg }, - [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip }, - [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip }, - [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie }, - [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren }, - [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie }, - [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval }, - [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst }, - [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, NULL }, - [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp }, - [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta }, - [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah }, + [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah, + .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus }, - [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip }, - [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie }, - [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec }, - [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch }, - [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc }, - [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause }, - [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval }, - [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp }, + [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie , + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 }, - [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst }, + [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, @@ -3227,7 +3556,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, /* Physical Memory Protection */ - [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg }, + [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg }, @@ -3249,6 +3579,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, + /* Debug CSRs */ + [CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect }, + [CSR_TDATA1] = { "tdata1", debug, read_tdata, write_tdata }, + [CSR_TDATA2] = { "tdata2", debug, read_tdata, write_tdata }, + [CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata }, + /* User Pointer Masking */ [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask }, diff --git a/target/riscv/debug.c b/target/riscv/debug.c new file mode 100644 index 0000000000..2f2a51c732 --- /dev/null +++ b/target/riscv/debug.c @@ -0,0 +1,441 @@ +/* + * QEMU RISC-V Native Debug Support + * + * Copyright (c) 2022 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * This provides the native debug support via the Trigger Module, as defined + * in the RISC-V Debug Specification: + * https://github.com/riscv/riscv-debug-spec/raw/master/riscv-debug-stable.pdf + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "cpu.h" +#include "trace.h" +#include "exec/exec-all.h" + +/* + * The following M-mode trigger CSRs are implemented: + * + * - tselect + * - tdata1 + * - tdata2 + * - tdata3 + * + * We don't support writable 'type' field in the tdata1 register, so there is + * no need to implement the "tinfo" CSR. + * + * The following triggers are implemented: + * + * Index | Type | tdata mapping | Description + * ------+------+------------------------+------------ + * 0 | 2 | tdata1, tdata2 | Address / Data Match + * 1 | 2 | tdata1, tdata2 | Address / Data Match + */ + +/* tdata availability of a trigger */ +typedef bool tdata_avail[TDATA_NUM]; + +static tdata_avail tdata_mapping[TRIGGER_NUM] = { + [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false }, +}; + +/* only breakpoint size 1/2/4/8 supported */ +static int access_size[SIZE_NUM] = { + [SIZE_ANY] = 0, + [SIZE_1B] = 1, + [SIZE_2B] = 2, + [SIZE_4B] = 4, + [SIZE_6B] = -1, + [SIZE_8B] = 8, + [6 ... 15] = -1, +}; + +static inline target_ulong trigger_type(CPURISCVState *env, + trigger_type_t type) +{ + target_ulong tdata1; + + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + tdata1 = RV32_TYPE(type); + break; + case MXL_RV64: + tdata1 = RV64_TYPE(type); + break; + default: + g_assert_not_reached(); + } + + return tdata1; +} + +bool tdata_available(CPURISCVState *env, int tdata_index) +{ + if (unlikely(tdata_index >= TDATA_NUM)) { + return false; + } + + if (unlikely(env->trigger_cur >= TRIGGER_NUM)) { + return false; + } + + return tdata_mapping[env->trigger_cur][tdata_index]; +} + +target_ulong tselect_csr_read(CPURISCVState *env) +{ + return env->trigger_cur; +} + +void tselect_csr_write(CPURISCVState *env, target_ulong val) +{ + /* all target_ulong bits of tselect are implemented */ + env->trigger_cur = val; +} + +static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, + trigger_type_t t) +{ + uint32_t type, dmode; + target_ulong tdata1; + + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + type = extract32(val, 28, 4); + dmode = extract32(val, 27, 1); + tdata1 = RV32_TYPE(t); + break; + case MXL_RV64: + type = extract64(val, 60, 4); + dmode = extract64(val, 59, 1); + tdata1 = RV64_TYPE(t); + break; + default: + g_assert_not_reached(); + } + + if (type != t) { + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring type write to tdata1 register\n"); + } + if (dmode != 0) { + qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n"); + } + + return tdata1; +} + +static inline void warn_always_zero_bit(target_ulong val, target_ulong mask, + const char *msg) +{ + if (val & mask) { + qemu_log_mask(LOG_UNIMP, "%s bit is always zero\n", msg); + } +} + +static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl) +{ + uint32_t size, sizelo, sizehi = 0; + + if (riscv_cpu_mxl(env) == MXL_RV64) { + sizehi = extract32(ctrl, 21, 2); + } + sizelo = extract32(ctrl, 16, 2); + size = (sizehi << 2) | sizelo; + + return size; +} + +static inline bool type2_breakpoint_enabled(target_ulong ctrl) +{ + bool mode = !!(ctrl & (TYPE2_U | TYPE2_S | TYPE2_M)); + bool rwx = !!(ctrl & (TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC)); + + return mode && rwx; +} + +static target_ulong type2_mcontrol_validate(CPURISCVState *env, + target_ulong ctrl) +{ + target_ulong val; + uint32_t size; + + /* validate the generic part first */ + val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH); + + /* validate unimplemented (always zero) bits */ + warn_always_zero_bit(ctrl, TYPE2_MATCH, "match"); + warn_always_zero_bit(ctrl, TYPE2_CHAIN, "chain"); + warn_always_zero_bit(ctrl, TYPE2_ACTION, "action"); + warn_always_zero_bit(ctrl, TYPE2_TIMING, "timing"); + warn_always_zero_bit(ctrl, TYPE2_SELECT, "select"); + warn_always_zero_bit(ctrl, TYPE2_HIT, "hit"); + + /* validate size encoding */ + size = type2_breakpoint_size(env, ctrl); + if (access_size[size] == -1) { + qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n", + size); + } else { + val |= (ctrl & TYPE2_SIZELO); + if (riscv_cpu_mxl(env) == MXL_RV64) { + val |= (ctrl & TYPE2_SIZEHI); + } + } + + /* keep the mode and attribute bits */ + val |= (ctrl & (TYPE2_U | TYPE2_S | TYPE2_M | + TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC)); + + return val; +} + +static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) +{ + target_ulong ctrl = env->type2_trig[index].mcontrol; + target_ulong addr = env->type2_trig[index].maddress; + bool enabled = type2_breakpoint_enabled(ctrl); + CPUState *cs = env_cpu(env); + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + uint32_t size; + + if (!enabled) { + return; + } + + if (ctrl & TYPE2_EXEC) { + cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp); + } + + if (ctrl & TYPE2_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE2_STORE) { + flags |= BP_MEM_WRITE; + } + + if (flags & BP_MEM_ACCESS) { + size = type2_breakpoint_size(env, ctrl); + if (size != 0) { + cpu_watchpoint_insert(cs, addr, size, flags, + &env->type2_trig[index].wp); + } else { + cpu_watchpoint_insert(cs, addr, 8, flags, + &env->type2_trig[index].wp); + } + } +} + +static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index) +{ + CPUState *cs = env_cpu(env); + + if (env->type2_trig[index].bp) { + cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp); + env->type2_trig[index].bp = NULL; + } + + if (env->type2_trig[index].wp) { + cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp); + env->type2_trig[index].wp = NULL; + } +} + +static target_ulong type2_reg_read(CPURISCVState *env, + target_ulong trigger_index, int tdata_index) +{ + uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; + target_ulong tdata; + + switch (tdata_index) { + case TDATA1: + tdata = env->type2_trig[index].mcontrol; + break; + case TDATA2: + tdata = env->type2_trig[index].maddress; + break; + default: + g_assert_not_reached(); + } + + return tdata; +} + +static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index, + int tdata_index, target_ulong val) +{ + uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; + target_ulong new_val; + + switch (tdata_index) { + case TDATA1: + new_val = type2_mcontrol_validate(env, val); + if (new_val != env->type2_trig[index].mcontrol) { + env->type2_trig[index].mcontrol = new_val; + type2_breakpoint_remove(env, index); + type2_breakpoint_insert(env, index); + } + break; + case TDATA2: + if (val != env->type2_trig[index].maddress) { + env->type2_trig[index].maddress = val; + type2_breakpoint_remove(env, index); + type2_breakpoint_insert(env, index); + } + break; + default: + g_assert_not_reached(); + } + + return; +} + +typedef target_ulong (*tdata_read_func)(CPURISCVState *env, + target_ulong trigger_index, + int tdata_index); + +static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = { + [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read, +}; + +typedef void (*tdata_write_func)(CPURISCVState *env, + target_ulong trigger_index, + int tdata_index, + target_ulong val); + +static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = { + [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write, +}; + +target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index) +{ + tdata_read_func read_func = trigger_read_funcs[env->trigger_cur]; + + return read_func(env, env->trigger_cur, tdata_index); +} + +void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val) +{ + tdata_write_func write_func = trigger_write_funcs[env->trigger_cur]; + + return write_func(env, env->trigger_cur, tdata_index, val); +} + +void riscv_cpu_debug_excp_handler(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + if (cs->watchpoint_hit) { + if (cs->watchpoint_hit->flags & BP_CPU) { + cs->watchpoint_hit = NULL; + riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + } + } else { + if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) { + riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + } + } +} + +bool riscv_cpu_debug_check_breakpoint(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + CPUBreakpoint *bp; + target_ulong ctrl; + target_ulong pc; + int i; + + QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { + for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + ctrl = env->type2_trig[i].mcontrol; + pc = env->type2_trig[i].maddress; + + if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + } + } + + return false; +} + +bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + target_ulong ctrl; + target_ulong addr; + int flags; + int i; + + for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + ctrl = env->type2_trig[i].mcontrol; + addr = env->type2_trig[i].maddress; + flags = 0; + + if (ctrl & TYPE2_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE2_STORE) { + flags |= BP_MEM_WRITE; + } + + if ((wp->flags & flags) && (wp->vaddr == addr)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + } + + return false; +} + +void riscv_trigger_init(CPURISCVState *env) +{ + target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH); + int i; + + /* type 2 triggers */ + for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + /* + * type = TRIGGER_TYPE_AD_MATCH + * dmode = 0 (both debug and M-mode can write tdata) + * maskmax = 0 (unimplemented, always 0) + * sizehi = 0 (match against any size, RV64 only) + * hit = 0 (unimplemented, always 0) + * select = 0 (always 0, perform match on address) + * timing = 0 (always 0, trigger before instruction) + * sizelo = 0 (match against any size) + * action = 0 (always 0, raise a breakpoint exception) + * chain = 0 (unimplemented, always 0) + * match = 0 (always 0, when any compare value equals tdata2) + */ + env->type2_trig[i].mcontrol = type2; + env->type2_trig[i].maddress = 0; + env->type2_trig[i].bp = NULL; + env->type2_trig[i].wp = NULL; + } +} diff --git a/target/riscv/debug.h b/target/riscv/debug.h new file mode 100644 index 0000000000..27b9cac6b4 --- /dev/null +++ b/target/riscv/debug.h @@ -0,0 +1,114 @@ +/* + * QEMU RISC-V Native Debug Support + * + * Copyright (c) 2022 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef RISCV_DEBUG_H +#define RISCV_DEBUG_H + +/* trigger indexes implemented */ +enum { + TRIGGER_TYPE2_IDX_0 = 0, + TRIGGER_TYPE2_IDX_1, + TRIGGER_TYPE2_NUM, + TRIGGER_NUM = TRIGGER_TYPE2_NUM +}; + +/* register index of tdata CSRs */ +enum { + TDATA1 = 0, + TDATA2, + TDATA3, + TDATA_NUM +}; + +typedef enum { + TRIGGER_TYPE_NO_EXIST = 0, /* trigger does not exist */ + TRIGGER_TYPE_AD_MATCH = 2, /* address/data match trigger */ + TRIGGER_TYPE_INST_CNT = 3, /* instruction count trigger */ + TRIGGER_TYPE_INT = 4, /* interrupt trigger */ + TRIGGER_TYPE_EXCP = 5, /* exception trigger */ + TRIGGER_TYPE_AD_MATCH6 = 6, /* new address/data match trigger */ + TRIGGER_TYPE_EXT_SRC = 7, /* external source trigger */ + TRIGGER_TYPE_UNAVAIL = 15 /* trigger exists, but unavailable */ +} trigger_type_t; + +typedef struct { + target_ulong mcontrol; + target_ulong maddress; + struct CPUBreakpoint *bp; + struct CPUWatchpoint *wp; +} type2_trigger_t; + +/* tdata field masks */ + +#define RV32_TYPE(t) ((uint32_t)(t) << 28) +#define RV32_TYPE_MASK (0xf << 28) +#define RV32_DMODE BIT(27) +#define RV64_TYPE(t) ((uint64_t)(t) << 60) +#define RV64_TYPE_MASK (0xfULL << 60) +#define RV64_DMODE BIT_ULL(59) + +/* mcontrol field masks */ + +#define TYPE2_LOAD BIT(0) +#define TYPE2_STORE BIT(1) +#define TYPE2_EXEC BIT(2) +#define TYPE2_U BIT(3) +#define TYPE2_S BIT(4) +#define TYPE2_M BIT(6) +#define TYPE2_MATCH (0xf << 7) +#define TYPE2_CHAIN BIT(11) +#define TYPE2_ACTION (0xf << 12) +#define TYPE2_SIZELO (0x3 << 16) +#define TYPE2_TIMING BIT(18) +#define TYPE2_SELECT BIT(19) +#define TYPE2_HIT BIT(20) +#define TYPE2_SIZEHI (0x3 << 21) /* RV64 only */ + +/* access size */ +enum { + SIZE_ANY = 0, + SIZE_1B, + SIZE_2B, + SIZE_4B, + SIZE_6B, + SIZE_8B, + SIZE_10B, + SIZE_12B, + SIZE_14B, + SIZE_16B, + SIZE_NUM = 16 +}; + +bool tdata_available(CPURISCVState *env, int tdata_index); + +target_ulong tselect_csr_read(CPURISCVState *env); +void tselect_csr_write(CPURISCVState *env, target_ulong val); + +target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index); +void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val); + +void riscv_cpu_debug_excp_handler(CPUState *cs); +bool riscv_cpu_debug_check_breakpoint(CPUState *cs); +bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); + +void riscv_trigger_init(CPURISCVState *env); + +#endif /* RISCV_DEBUG_H */ diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 4a5982d594..5699c9517f 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -89,19 +89,21 @@ void helper_set_rod_rounding_mode(CPURISCVState *env) static uint64_t do_fmadd_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2, uint64_t rs3, int flags) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); - float16 frs3 = check_nanbox_h(rs3); - return nanbox_h(float16_muladd(frs1, frs2, frs3, flags, &env->fp_status)); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + float16 frs3 = check_nanbox_h(env, rs3); + return nanbox_h(env, float16_muladd(frs1, frs2, frs3, flags, + &env->fp_status)); } static uint64_t do_fmadd_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2, uint64_t rs3, int flags) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); - float32 frs3 = check_nanbox_s(rs3); - return nanbox_s(float32_muladd(frs1, frs2, frs3, flags, &env->fp_status)); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + float32 frs3 = check_nanbox_s(env, rs3); + return nanbox_s(env, float32_muladd(frs1, frs2, frs3, flags, + &env->fp_status)); } uint64_t helper_fmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, @@ -183,124 +185,124 @@ uint64_t helper_fnmadd_h(CPURISCVState *env, uint64_t frs1, uint64_t frs2, uint64_t helper_fadd_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(float32_add(frs1, frs2, &env->fp_status)); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return nanbox_s(env, float32_add(frs1, frs2, &env->fp_status)); } uint64_t helper_fsub_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(float32_sub(frs1, frs2, &env->fp_status)); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return nanbox_s(env, float32_sub(frs1, frs2, &env->fp_status)); } uint64_t helper_fmul_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(float32_mul(frs1, frs2, &env->fp_status)); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return nanbox_s(env, float32_mul(frs1, frs2, &env->fp_status)); } uint64_t helper_fdiv_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(float32_div(frs1, frs2, &env->fp_status)); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return nanbox_s(env, float32_div(frs1, frs2, &env->fp_status)); } uint64_t helper_fmin_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(env->priv_ver < PRIV_VERSION_1_11_0 ? + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return nanbox_s(env, env->priv_ver < PRIV_VERSION_1_11_0 ? float32_minnum(frs1, frs2, &env->fp_status) : float32_minimum_number(frs1, frs2, &env->fp_status)); } uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); - return nanbox_s(env->priv_ver < PRIV_VERSION_1_11_0 ? + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return nanbox_s(env, env->priv_ver < PRIV_VERSION_1_11_0 ? float32_maxnum(frs1, frs2, &env->fp_status) : float32_maximum_number(frs1, frs2, &env->fp_status)); } uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); - return nanbox_s(float32_sqrt(frs1, &env->fp_status)); + float32 frs1 = check_nanbox_s(env, rs1); + return nanbox_s(env, float32_sqrt(frs1, &env->fp_status)); } target_ulong helper_fle_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); return float32_le(frs1, frs2, &env->fp_status); } target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); return float32_lt(frs1, frs2, &env->fp_status); } target_ulong helper_feq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float32 frs1 = check_nanbox_s(rs1); - float32 frs2 = check_nanbox_s(rs2); + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); return float32_eq_quiet(frs1, frs2, &env->fp_status); } target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); + float32 frs1 = check_nanbox_s(env, rs1); return float32_to_int32(frs1, &env->fp_status); } target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); + float32 frs1 = check_nanbox_s(env, rs1); return (int32_t)float32_to_uint32(frs1, &env->fp_status); } target_ulong helper_fcvt_l_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); + float32 frs1 = check_nanbox_s(env, rs1); return float32_to_int64(frs1, &env->fp_status); } target_ulong helper_fcvt_lu_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); + float32 frs1 = check_nanbox_s(env, rs1); return float32_to_uint64(frs1, &env->fp_status); } uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1) { - return nanbox_s(int32_to_float32((int32_t)rs1, &env->fp_status)); + return nanbox_s(env, int32_to_float32((int32_t)rs1, &env->fp_status)); } uint64_t helper_fcvt_s_wu(CPURISCVState *env, target_ulong rs1) { - return nanbox_s(uint32_to_float32((uint32_t)rs1, &env->fp_status)); + return nanbox_s(env, uint32_to_float32((uint32_t)rs1, &env->fp_status)); } uint64_t helper_fcvt_s_l(CPURISCVState *env, target_ulong rs1) { - return nanbox_s(int64_to_float32(rs1, &env->fp_status)); + return nanbox_s(env, int64_to_float32(rs1, &env->fp_status)); } uint64_t helper_fcvt_s_lu(CPURISCVState *env, target_ulong rs1) { - return nanbox_s(uint64_to_float32(rs1, &env->fp_status)); + return nanbox_s(env, uint64_to_float32(rs1, &env->fp_status)); } -target_ulong helper_fclass_s(uint64_t rs1) +target_ulong helper_fclass_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); + float32 frs1 = check_nanbox_s(env, rs1); return fclass_s(frs1); } @@ -340,12 +342,12 @@ uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) { - return nanbox_s(float64_to_float32(rs1, &env->fp_status)); + return nanbox_s(env, float64_to_float32(rs1, &env->fp_status)); } uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); + float32 frs1 = check_nanbox_s(env, rs1); return float32_to_float64(frs1, &env->fp_status); } @@ -416,146 +418,146 @@ target_ulong helper_fclass_d(uint64_t frs1) uint64_t helper_fadd_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); - return nanbox_h(float16_add(frs1, frs2, &env->fp_status)); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return nanbox_h(env, float16_add(frs1, frs2, &env->fp_status)); } uint64_t helper_fsub_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); - return nanbox_h(float16_sub(frs1, frs2, &env->fp_status)); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return nanbox_h(env, float16_sub(frs1, frs2, &env->fp_status)); } uint64_t helper_fmul_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); - return nanbox_h(float16_mul(frs1, frs2, &env->fp_status)); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return nanbox_h(env, float16_mul(frs1, frs2, &env->fp_status)); } uint64_t helper_fdiv_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); - return nanbox_h(float16_div(frs1, frs2, &env->fp_status)); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return nanbox_h(env, float16_div(frs1, frs2, &env->fp_status)); } uint64_t helper_fmin_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); - return nanbox_h(env->priv_ver < PRIV_VERSION_1_11_0 ? + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return nanbox_h(env, env->priv_ver < PRIV_VERSION_1_11_0 ? float16_minnum(frs1, frs2, &env->fp_status) : float16_minimum_number(frs1, frs2, &env->fp_status)); } uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); - return nanbox_h(env->priv_ver < PRIV_VERSION_1_11_0 ? + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return nanbox_h(env, env->priv_ver < PRIV_VERSION_1_11_0 ? float16_maxnum(frs1, frs2, &env->fp_status) : float16_maximum_number(frs1, frs2, &env->fp_status)); } uint64_t helper_fsqrt_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); - return nanbox_h(float16_sqrt(frs1, &env->fp_status)); + float16 frs1 = check_nanbox_h(env, rs1); + return nanbox_h(env, float16_sqrt(frs1, &env->fp_status)); } target_ulong helper_fle_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); return float16_le(frs1, frs2, &env->fp_status); } target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); return float16_lt(frs1, frs2, &env->fp_status); } target_ulong helper_feq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { - float16 frs1 = check_nanbox_h(rs1); - float16 frs2 = check_nanbox_h(rs2); + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); return float16_eq_quiet(frs1, frs2, &env->fp_status); } -target_ulong helper_fclass_h(uint64_t rs1) +target_ulong helper_fclass_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); + float16 frs1 = check_nanbox_h(env, rs1); return fclass_h(frs1); } target_ulong helper_fcvt_w_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); + float16 frs1 = check_nanbox_h(env, rs1); return float16_to_int32(frs1, &env->fp_status); } target_ulong helper_fcvt_wu_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); + float16 frs1 = check_nanbox_h(env, rs1); return (int32_t)float16_to_uint32(frs1, &env->fp_status); } target_ulong helper_fcvt_l_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); + float16 frs1 = check_nanbox_h(env, rs1); return float16_to_int64(frs1, &env->fp_status); } target_ulong helper_fcvt_lu_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); + float16 frs1 = check_nanbox_h(env, rs1); return float16_to_uint64(frs1, &env->fp_status); } uint64_t helper_fcvt_h_w(CPURISCVState *env, target_ulong rs1) { - return nanbox_h(int32_to_float16((int32_t)rs1, &env->fp_status)); + return nanbox_h(env, int32_to_float16((int32_t)rs1, &env->fp_status)); } uint64_t helper_fcvt_h_wu(CPURISCVState *env, target_ulong rs1) { - return nanbox_h(uint32_to_float16((uint32_t)rs1, &env->fp_status)); + return nanbox_h(env, uint32_to_float16((uint32_t)rs1, &env->fp_status)); } uint64_t helper_fcvt_h_l(CPURISCVState *env, target_ulong rs1) { - return nanbox_h(int64_to_float16(rs1, &env->fp_status)); + return nanbox_h(env, int64_to_float16(rs1, &env->fp_status)); } uint64_t helper_fcvt_h_lu(CPURISCVState *env, target_ulong rs1) { - return nanbox_h(uint64_to_float16(rs1, &env->fp_status)); + return nanbox_h(env, uint64_to_float16(rs1, &env->fp_status)); } uint64_t helper_fcvt_h_s(CPURISCVState *env, uint64_t rs1) { - float32 frs1 = check_nanbox_s(rs1); - return nanbox_h(float32_to_float16(frs1, true, &env->fp_status)); + float32 frs1 = check_nanbox_s(env, rs1); + return nanbox_h(env, float32_to_float16(frs1, true, &env->fp_status)); } uint64_t helper_fcvt_s_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); - return nanbox_s(float16_to_float32(frs1, true, &env->fp_status)); + float16 frs1 = check_nanbox_h(env, rs1); + return nanbox_s(env, float16_to_float32(frs1, true, &env->fp_status)); } uint64_t helper_fcvt_h_d(CPURISCVState *env, uint64_t rs1) { - return nanbox_h(float64_to_float16(rs1, true, &env->fp_status)); + return nanbox_h(env, float64_to_float16(rs1, true, &env->fp_status)); } uint64_t helper_fcvt_d_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(rs1); + float16 frs1 = check_nanbox_h(env, rs1); return float16_to_float64(frs1, true, &env->fp_status); } diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 72cc2582f4..4ef3b2251d 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -38,7 +38,7 @@ DEF_HELPER_FLAGS_2(fcvt_s_w, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl) -DEF_HELPER_FLAGS_1(fclass_s, TCG_CALL_NO_RWG_SE, tl, i64) +DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, tl, env, i64) /* Floating Point - Double Precision */ DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64) @@ -66,6 +66,11 @@ DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64) /* Bitmanip */ DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(clmulr, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_1(brev8, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(unzip, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(zip, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(xperm4, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(xperm8, TCG_CALL_NO_RWG_SE, tl, tl, tl) /* Floating Point - Half Precision */ DEF_HELPER_FLAGS_3(fadd_h, TCG_CALL_NO_RWG, i64, env, i64, i64) @@ -90,7 +95,7 @@ DEF_HELPER_FLAGS_2(fcvt_h_w, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl) -DEF_HELPER_FLAGS_1(fclass_h, TCG_CALL_NO_RWG_SE, tl, i64) +DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64) /* Special functions */ DEF_HELPER_2(csrr, tl, env, int) @@ -1086,10 +1091,7 @@ DEF_HELPER_6(vcompress_vm_h, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vcompress_vm_w, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vcompress_vm_d, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_4(vmv1r_v, void, ptr, ptr, env, i32) -DEF_HELPER_4(vmv2r_v, void, ptr, ptr, env, i32) -DEF_HELPER_4(vmv4r_v, void, ptr, ptr, env, i32) -DEF_HELPER_4(vmv8r_v, void, ptr, ptr, env, i32) +DEF_HELPER_4(vmvr_v, void, ptr, ptr, env, i32) DEF_HELPER_5(vzext_vf2_h, void, ptr, ptr, ptr, env, i32) DEF_HELPER_5(vzext_vf2_w, void, ptr, ptr, ptr, env, i32) @@ -1110,3 +1112,20 @@ DEF_HELPER_5(divu_i128, tl, env, tl, tl, tl, tl) DEF_HELPER_5(divs_i128, tl, env, tl, tl, tl, tl) DEF_HELPER_5(remu_i128, tl, env, tl, tl, tl, tl) DEF_HELPER_5(rems_i128, tl, env, tl, tl, tl, tl) + +/* Crypto functions */ +DEF_HELPER_FLAGS_3(aes32esmi, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_3(aes32esi, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_3(aes32dsmi, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_3(aes32dsi, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) + +DEF_HELPER_FLAGS_2(aes64esm, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(aes64es, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(aes64ds, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(aes64dsm, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(aes64ks2, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(aes64ks1i, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_1(aes64im, TCG_CALL_NO_RWG_SE, tl, tl) + +DEF_HELPER_FLAGS_3(sm4ed, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_3(sm4ks, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 1d3ff1efe1..4033565393 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -35,6 +35,8 @@ %imm_b 31:s1 7:1 25:6 8:4 !function=ex_shift_1 %imm_j 31:s1 12:8 20:1 21:10 !function=ex_shift_1 %imm_u 12:s20 !function=ex_shift_12 +%imm_bs 30:2 !function=ex_shift_3 +%imm_rnum 20:4 # Argument sets: &empty @@ -52,6 +54,7 @@ &rmr vm rd rs2 &r2nfvm vm rd rs1 nf &rnfvm vm rd rs1 rs2 nf +&k_aes shamt rs2 rs1 rd # Formats 32: @r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd @@ -89,6 +92,9 @@ @sfence_vma ....... ..... ..... ... ..... ....... %rs2 %rs1 @sfence_vm ....... ..... ..... ... ..... ....... %rs1 +@k_aes .. ..... ..... ..... ... ..... ....... &k_aes shamt=%imm_bs %rs2 %rs1 %rd +@i_aes .. ..... ..... ..... ... ..... ....... &i imm=%imm_rnum %rs1 %rd + # Formats 64: @sh5 ....... ..... ..... ... ..... ....... &shift shamt=%sh5 %rs1 %rd @@ -717,8 +723,22 @@ sh2add_uw 0010000 .......... 100 ..... 0111011 @r sh3add_uw 0010000 .......... 110 ..... 0111011 @r slli_uw 00001 ............ 001 ..... 0011011 @sh -# *** RV32 Zbb Standard Extension *** +# *** RV32 Zbb/Zbkb Standard Extension *** andn 0100000 .......... 111 ..... 0110011 @r +rol 0110000 .......... 001 ..... 0110011 @r +ror 0110000 .......... 101 ..... 0110011 @r +rori 01100 ............ 101 ..... 0010011 @sh +# The encoding for rev8 differs between RV32 and RV64. +# rev8_32 denotes the RV32 variant. +rev8_32 011010 011000 ..... 101 ..... 0010011 @r2 +# The encoding for zext.h differs between RV32 and RV64. +# zext_h_32 denotes the RV32 variant. +{ + zext_h_32 0000100 00000 ..... 100 ..... 0110011 @r2 + pack 0000100 ..... ..... 100 ..... 0110011 @r +} +xnor 0100000 .......... 100 ..... 0110011 @r +# *** RV32 extra Zbb Standard Extension *** clz 011000 000000 ..... 001 ..... 0010011 @r2 cpop 011000 000010 ..... 001 ..... 0010011 @r2 ctz 011000 000001 ..... 001 ..... 0010011 @r2 @@ -728,23 +748,15 @@ min 0000101 .......... 100 ..... 0110011 @r minu 0000101 .......... 101 ..... 0110011 @r orc_b 001010 000111 ..... 101 ..... 0010011 @r2 orn 0100000 .......... 110 ..... 0110011 @r -# The encoding for rev8 differs between RV32 and RV64. -# rev8_32 denotes the RV32 variant. -rev8_32 011010 011000 ..... 101 ..... 0010011 @r2 -rol 0110000 .......... 001 ..... 0110011 @r -ror 0110000 .......... 101 ..... 0110011 @r -rori 01100 ............ 101 ..... 0010011 @sh sext_b 011000 000100 ..... 001 ..... 0010011 @r2 sext_h 011000 000101 ..... 001 ..... 0010011 @r2 -xnor 0100000 .......... 100 ..... 0110011 @r -# The encoding for zext.h differs between RV32 and RV64. -# zext_h_32 denotes the RV32 variant. -zext_h_32 0000100 00000 ..... 100 ..... 0110011 @r2 +# *** RV32 extra Zbkb Standard Extension *** +brev8 0110100 00111 ..... 101 ..... 0010011 @r2 #grevi +packh 0000100 .......... 111 ..... 0110011 @r +unzip 0000100 01111 ..... 101 ..... 0010011 @r2 #unshfl +zip 0000100 01111 ..... 001 ..... 0010011 @r2 #shfl -# *** RV64 Zbb Standard Extension (in addition to RV32 Zbb) *** -clzw 0110000 00000 ..... 001 ..... 0011011 @r2 -ctzw 0110000 00001 ..... 001 ..... 0011011 @r2 -cpopw 0110000 00010 ..... 001 ..... 0011011 @r2 +# *** RV64 Zbb/Zbkb Standard Extension (in addition to RV32 Zbb/Zbkb) *** # The encoding for rev8 differs between RV32 and RV64. # When executing on RV64, the encoding used in RV32 is an illegal # instruction, so we use different handler functions to differentiate. @@ -755,13 +767,25 @@ rorw 0110000 .......... 101 ..... 0111011 @r # The encoding for zext.h differs between RV32 and RV64. # When executing on RV64, the encoding used in RV32 is an illegal # instruction, so we use different handler functions to differentiate. -zext_h_64 0000100 00000 ..... 100 ..... 0111011 @r2 +{ + zext_h_64 0000100 00000 ..... 100 ..... 0111011 @r2 + packw 0000100 ..... ..... 100 ..... 0111011 @r +} +# *** RV64 extra Zbb Standard Extension (in addition to RV32 Zbb) *** +clzw 0110000 00000 ..... 001 ..... 0011011 @r2 +ctzw 0110000 00001 ..... 001 ..... 0011011 @r2 +cpopw 0110000 00010 ..... 001 ..... 0011011 @r2 -# *** RV32 Zbc Standard Extension *** +# *** RV32 Zbc/Zbkc Standard Extension *** clmul 0000101 .......... 001 ..... 0110011 @r clmulh 0000101 .......... 011 ..... 0110011 @r +# *** RV32 extra Zbc Standard Extension *** clmulr 0000101 .......... 010 ..... 0110011 @r +# *** RV32 Zbkx Standard Extension *** +xperm4 0010100 .......... 010 ..... 0110011 @r +xperm8 0010100 .......... 100 ..... 0110011 @r + # *** RV32 Zbs Standard Extension *** bclr 0100100 .......... 001 ..... 0110011 @r bclri 01001. ........... 001 ..... 0010011 @sh @@ -816,3 +840,42 @@ sfence_w_inval 0001100 00000 00000 000 00000 1110011 sfence_inval_ir 0001100 00001 00000 000 00000 1110011 hinval_vvma 0010011 ..... ..... 000 00000 1110011 @hfence_vvma hinval_gvma 0110011 ..... ..... 000 00000 1110011 @hfence_gvma + +# *** RV32 Zknd Standard Extension *** +aes32dsmi .. 10111 ..... ..... 000 ..... 0110011 @k_aes +aes32dsi .. 10101 ..... ..... 000 ..... 0110011 @k_aes +# *** RV64 Zknd Standard Extension *** +aes64dsm 00 11111 ..... ..... 000 ..... 0110011 @r +aes64ds 00 11101 ..... ..... 000 ..... 0110011 @r +aes64im 00 11000 00000 ..... 001 ..... 0010011 @r2 +# *** RV32 Zkne Standard Extension *** +aes32esmi .. 10011 ..... ..... 000 ..... 0110011 @k_aes +aes32esi .. 10001 ..... ..... 000 ..... 0110011 @k_aes +# *** RV64 Zkne Standard Extension *** +aes64es 00 11001 ..... ..... 000 ..... 0110011 @r +aes64esm 00 11011 ..... ..... 000 ..... 0110011 @r +# *** RV64 Zkne/zknd Standard Extension *** +aes64ks2 01 11111 ..... ..... 000 ..... 0110011 @r +aes64ks1i 00 11000 1.... ..... 001 ..... 0010011 @i_aes +# *** RV32 Zknh Standard Extension *** +sha256sig0 00 01000 00010 ..... 001 ..... 0010011 @r2 +sha256sig1 00 01000 00011 ..... 001 ..... 0010011 @r2 +sha256sum0 00 01000 00000 ..... 001 ..... 0010011 @r2 +sha256sum1 00 01000 00001 ..... 001 ..... 0010011 @r2 +sha512sum0r 01 01000 ..... ..... 000 ..... 0110011 @r +sha512sum1r 01 01001 ..... ..... 000 ..... 0110011 @r +sha512sig0l 01 01010 ..... ..... 000 ..... 0110011 @r +sha512sig0h 01 01110 ..... ..... 000 ..... 0110011 @r +sha512sig1l 01 01011 ..... ..... 000 ..... 0110011 @r +sha512sig1h 01 01111 ..... ..... 000 ..... 0110011 @r +# *** RV64 Zknh Standard Extension *** +sha512sig0 00 01000 00110 ..... 001 ..... 0010011 @r2 +sha512sig1 00 01000 00111 ..... 001 ..... 0010011 @r2 +sha512sum0 00 01000 00100 ..... 001 ..... 0010011 @r2 +sha512sum1 00 01000 00101 ..... 001 ..... 0010011 @r2 +# *** RV32 Zksh Standard Extension *** +sm3p0 00 01000 01000 ..... 001 ..... 0010011 @r2 +sm3p1 00 01000 01001 ..... 001 ..... 0010011 @r2 +# *** RV32 Zksed Standard Extension *** +sm4ed .. 11000 ..... ..... 000 ..... 0110011 @k_aes +sm4ks .. 11010 ..... ..... 000 ..... 0110011 @k_aes diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index f9bd3b7ec4..e2b8329f1e 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -1,5 +1,5 @@ /* - * RISC-V translation routines for the Zb[abcs] Standard Extension. + * RISC-V translation routines for the Zb[abcs] and Zbk[bcx] Standard Extension. * * Copyright (c) 2020 Kito Cheng, kito.cheng@sifive.com * Copyright (c) 2020 Frank Chang, frank.chang@sifive.com @@ -19,25 +19,37 @@ */ #define REQUIRE_ZBA(ctx) do { \ - if (ctx->cfg_ptr->ext_zba) { \ + if (!ctx->cfg_ptr->ext_zba) { \ return false; \ } \ } while (0) #define REQUIRE_ZBB(ctx) do { \ - if (ctx->cfg_ptr->ext_zbb) { \ + if (!ctx->cfg_ptr->ext_zbb) { \ return false; \ } \ } while (0) #define REQUIRE_ZBC(ctx) do { \ - if (ctx->cfg_ptr->ext_zbc) { \ + if (!ctx->cfg_ptr->ext_zbc) { \ return false; \ } \ } while (0) #define REQUIRE_ZBS(ctx) do { \ - if (ctx->cfg_ptr->ext_zbs) { \ + if (!ctx->cfg_ptr->ext_zbs) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZBKB(ctx) do { \ + if (!ctx->cfg_ptr->ext_zbkb) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZBKX(ctx) do { \ + if (!ctx->cfg_ptr->ext_zbkx) { \ return false; \ } \ } while (0) @@ -85,19 +97,19 @@ static bool trans_cpop(DisasContext *ctx, arg_cpop *a) static bool trans_andn(DisasContext *ctx, arg_andn *a) { - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_logic(ctx, a, tcg_gen_andc_tl); } static bool trans_orn(DisasContext *ctx, arg_orn *a) { - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_logic(ctx, a, tcg_gen_orc_tl); } static bool trans_xnor(DisasContext *ctx, arg_xnor *a) { - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_logic(ctx, a, tcg_gen_eqv_tl); } @@ -247,7 +259,7 @@ static void gen_rorw(TCGv ret, TCGv arg1, TCGv arg2) static bool trans_ror(DisasContext *ctx, arg_ror *a) { - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_shift_per_ol(ctx, a, EXT_NONE, tcg_gen_rotr_tl, gen_rorw, NULL); } @@ -264,7 +276,7 @@ static void gen_roriw(TCGv ret, TCGv arg1, target_long shamt) static bool trans_rori(DisasContext *ctx, arg_rori *a) { - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_shift_imm_fn_per_ol(ctx, a, EXT_NONE, tcg_gen_rotri_tl, gen_roriw, NULL); } @@ -289,7 +301,7 @@ static void gen_rolw(TCGv ret, TCGv arg1, TCGv arg2) static bool trans_rol(DisasContext *ctx, arg_rol *a) { - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_shift_per_ol(ctx, a, EXT_NONE, tcg_gen_rotl_tl, gen_rolw, NULL); } @@ -301,14 +313,14 @@ static void gen_rev8_32(TCGv ret, TCGv src1) static bool trans_rev8_32(DisasContext *ctx, arg_rev8_32 *a) { REQUIRE_32BIT(ctx); - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_unary(ctx, a, EXT_NONE, gen_rev8_32); } static bool trans_rev8_64(DisasContext *ctx, arg_rev8_64 *a) { REQUIRE_64BIT(ctx); - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); return gen_unary(ctx, a, EXT_NONE, tcg_gen_bswap_tl); } @@ -403,7 +415,7 @@ static bool trans_cpopw(DisasContext *ctx, arg_cpopw *a) static bool trans_rorw(DisasContext *ctx, arg_rorw *a) { REQUIRE_64BIT(ctx); - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); ctx->ol = MXL_RV32; return gen_shift(ctx, a, EXT_NONE, gen_rorw, NULL); } @@ -411,7 +423,7 @@ static bool trans_rorw(DisasContext *ctx, arg_rorw *a) static bool trans_roriw(DisasContext *ctx, arg_roriw *a) { REQUIRE_64BIT(ctx); - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); ctx->ol = MXL_RV32; return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_roriw, NULL); } @@ -419,7 +431,7 @@ static bool trans_roriw(DisasContext *ctx, arg_roriw *a) static bool trans_rolw(DisasContext *ctx, arg_rolw *a) { REQUIRE_64BIT(ctx); - REQUIRE_ZBB(ctx); + REQUIRE_EITHER_EXT(ctx, zbb, zbkb); ctx->ol = MXL_RV32; return gen_shift(ctx, a, EXT_NONE, gen_rolw, NULL); } @@ -483,7 +495,7 @@ static bool trans_slli_uw(DisasContext *ctx, arg_slli_uw *a) static bool trans_clmul(DisasContext *ctx, arg_clmul *a) { - REQUIRE_ZBC(ctx); + REQUIRE_EITHER_EXT(ctx, zbc, zbkc); return gen_arith(ctx, a, EXT_NONE, gen_helper_clmul, NULL); } @@ -495,7 +507,7 @@ static void gen_clmulh(TCGv dst, TCGv src1, TCGv src2) static bool trans_clmulh(DisasContext *ctx, arg_clmulr *a) { - REQUIRE_ZBC(ctx); + REQUIRE_EITHER_EXT(ctx, zbc, zbkc); return gen_arith(ctx, a, EXT_NONE, gen_clmulh, NULL); } @@ -504,3 +516,79 @@ static bool trans_clmulr(DisasContext *ctx, arg_clmulh *a) REQUIRE_ZBC(ctx); return gen_arith(ctx, a, EXT_NONE, gen_helper_clmulr, NULL); } + +static void gen_pack(TCGv ret, TCGv src1, TCGv src2) +{ + tcg_gen_deposit_tl(ret, src1, src2, + TARGET_LONG_BITS / 2, + TARGET_LONG_BITS / 2); +} + +static void gen_packh(TCGv ret, TCGv src1, TCGv src2) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ext8u_tl(t, src2); + tcg_gen_deposit_tl(ret, src1, t, 8, TARGET_LONG_BITS - 8); + tcg_temp_free(t); +} + +static void gen_packw(TCGv ret, TCGv src1, TCGv src2) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ext16s_tl(t, src2); + tcg_gen_deposit_tl(ret, src1, t, 16, TARGET_LONG_BITS - 16); + tcg_temp_free(t); +} + +static bool trans_brev8(DisasContext *ctx, arg_brev8 *a) +{ + REQUIRE_ZBKB(ctx); + return gen_unary(ctx, a, EXT_NONE, gen_helper_brev8); +} + +static bool trans_pack(DisasContext *ctx, arg_pack *a) +{ + REQUIRE_ZBKB(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_pack, NULL); +} + +static bool trans_packh(DisasContext *ctx, arg_packh *a) +{ + REQUIRE_ZBKB(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_packh, NULL); +} + +static bool trans_packw(DisasContext *ctx, arg_packw *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZBKB(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_packw, NULL); +} + +static bool trans_unzip(DisasContext *ctx, arg_unzip *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZBKB(ctx); + return gen_unary(ctx, a, EXT_NONE, gen_helper_unzip); +} + +static bool trans_zip(DisasContext *ctx, arg_zip *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZBKB(ctx); + return gen_unary(ctx, a, EXT_NONE, gen_helper_zip); +} + +static bool trans_xperm4(DisasContext *ctx, arg_xperm4 *a) +{ + REQUIRE_ZBKX(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_xperm4, NULL); +} + +static bool trans_xperm8(DisasContext *ctx, arg_xperm8 *a) +{ + REQUIRE_ZBKX(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_xperm8, NULL); +} diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 091ed3a8ad..1397c1ce1c 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -18,6 +18,19 @@ * this program. If not, see . */ +#define REQUIRE_ZDINX_OR_D(ctx) do { \ + if (!ctx->cfg_ptr->ext_zdinx) { \ + REQUIRE_EXT(ctx, RVD); \ + } \ +} while (0) + +#define REQUIRE_EVEN(ctx, reg) do { \ + if (ctx->cfg_ptr->ext_zdinx && (get_xl(ctx) == MXL_RV32) && \ + ((reg) & 0x1)) { \ + return false; \ + } \ +} while (0) + static bool trans_fld(DisasContext *ctx, arg_fld *a) { TCGv addr; @@ -47,10 +60,17 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fmadd_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fmadd_d(dest, cpu_env, src1, src2, src3); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -58,10 +78,17 @@ static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a) static bool trans_fmsub_d(DisasContext *ctx, arg_fmsub_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fmsub_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fmsub_d(dest, cpu_env, src1, src2, src3); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -69,10 +96,17 @@ static bool trans_fmsub_d(DisasContext *ctx, arg_fmsub_d *a) static bool trans_fnmsub_d(DisasContext *ctx, arg_fnmsub_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fnmsub_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fnmsub_d(dest, cpu_env, src1, src2, src3); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -80,10 +114,17 @@ static bool trans_fnmsub_d(DisasContext *ctx, arg_fnmsub_d *a) static bool trans_fnmadd_d(DisasContext *ctx, arg_fnmadd_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fnmadd_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fnmadd_d(dest, cpu_env, src1, src2, src3); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -91,12 +132,16 @@ static bool trans_fnmadd_d(DisasContext *ctx, arg_fnmadd_d *a) static bool trans_fadd_d(DisasContext *ctx, arg_fadd_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fadd_d(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); - + gen_helper_fadd_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -104,12 +149,16 @@ static bool trans_fadd_d(DisasContext *ctx, arg_fadd_d *a) static bool trans_fsub_d(DisasContext *ctx, arg_fsub_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fsub_d(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); - + gen_helper_fsub_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -117,12 +166,16 @@ static bool trans_fsub_d(DisasContext *ctx, arg_fsub_d *a) static bool trans_fmul_d(DisasContext *ctx, arg_fmul_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fmul_d(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); - + gen_helper_fmul_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -130,12 +183,16 @@ static bool trans_fmul_d(DisasContext *ctx, arg_fmul_d *a) static bool trans_fdiv_d(DisasContext *ctx, arg_fdiv_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fdiv_d(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); - + gen_helper_fdiv_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -143,23 +200,34 @@ static bool trans_fdiv_d(DisasContext *ctx, arg_fdiv_d *a) static bool trans_fsqrt_d(DisasContext *ctx, arg_fsqrt_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fsqrt_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); - + gen_helper_fsqrt_d(dest, cpu_env, src1); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } static bool trans_fsgnj_d(DisasContext *ctx, arg_fsgnj_d *a) { + REQUIRE_FPU; + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); if (a->rs1 == a->rs2) { /* FMOV */ - tcg_gen_mov_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1]); + dest = get_fpr_d(ctx, a->rs1); } else { - tcg_gen_deposit_i64(cpu_fpr[a->rd], cpu_fpr[a->rs2], - cpu_fpr[a->rs1], 0, 63); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + tcg_gen_deposit_i64(dest, src2, src1, 0, 63); } + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -167,15 +235,22 @@ static bool trans_fsgnj_d(DisasContext *ctx, arg_fsgnj_d *a) static bool trans_fsgnjn_d(DisasContext *ctx, arg_fsgnjn_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + if (a->rs1 == a->rs2) { /* FNEG */ - tcg_gen_xori_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], INT64_MIN); + tcg_gen_xori_i64(dest, src1, INT64_MIN); } else { + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_not_i64(t0, cpu_fpr[a->rs2]); - tcg_gen_deposit_i64(cpu_fpr[a->rd], t0, cpu_fpr[a->rs1], 0, 63); + tcg_gen_not_i64(t0, src2); + tcg_gen_deposit_i64(dest, t0, src1, 0, 63); tcg_temp_free_i64(t0); } + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -183,15 +258,22 @@ static bool trans_fsgnjn_d(DisasContext *ctx, arg_fsgnjn_d *a) static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + if (a->rs1 == a->rs2) { /* FABS */ - tcg_gen_andi_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], ~INT64_MIN); + tcg_gen_andi_i64(dest, src1, ~INT64_MIN); } else { + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_andi_i64(t0, cpu_fpr[a->rs2], INT64_MIN); - tcg_gen_xor_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], t0); + tcg_gen_andi_i64(t0, src2, INT64_MIN); + tcg_gen_xor_i64(dest, src1, t0); tcg_temp_free_i64(t0); } + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -199,11 +281,15 @@ static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a) static bool trans_fmin_d(DisasContext *ctx, arg_fmin_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); - gen_helper_fmin_d(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + gen_helper_fmin_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -211,11 +297,15 @@ static bool trans_fmin_d(DisasContext *ctx, arg_fmin_d *a) static bool trans_fmax_d(DisasContext *ctx, arg_fmax_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2); - gen_helper_fmax_d(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + gen_helper_fmax_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -223,11 +313,15 @@ static bool trans_fmax_d(DisasContext *ctx, arg_fmax_d *a) static bool trans_fcvt_s_d(DisasContext *ctx, arg_fcvt_s_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); - + gen_helper_fcvt_s_d(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -235,11 +329,15 @@ static bool trans_fcvt_s_d(DisasContext *ctx, arg_fcvt_s_d *a) static bool trans_fcvt_d_s(DisasContext *ctx, arg_fcvt_d_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); - + gen_helper_fcvt_d_s(dest, cpu_env, src1); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -247,11 +345,14 @@ static bool trans_fcvt_d_s(DisasContext *ctx, arg_fcvt_d_s *a) static bool trans_feq_d(DisasContext *ctx, arg_feq_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1 | a->rs2); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_feq_d(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_feq_d(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -259,11 +360,14 @@ static bool trans_feq_d(DisasContext *ctx, arg_feq_d *a) static bool trans_flt_d(DisasContext *ctx, arg_flt_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1 | a->rs2); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_flt_d(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_flt_d(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -271,11 +375,14 @@ static bool trans_flt_d(DisasContext *ctx, arg_flt_d *a) static bool trans_fle_d(DisasContext *ctx, arg_fle_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1 | a->rs2); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_fle_d(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fle_d(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -283,11 +390,13 @@ static bool trans_fle_d(DisasContext *ctx, arg_fle_d *a) static bool trans_fclass_d(DisasContext *ctx, arg_fclass_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); - gen_helper_fclass_d(dest, cpu_fpr[a->rs1]); + gen_helper_fclass_d(dest, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -295,12 +404,14 @@ static bool trans_fclass_d(DisasContext *ctx, arg_fclass_d *a) static bool trans_fcvt_w_d(DisasContext *ctx, arg_fcvt_w_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_w_d(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_w_d(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -308,12 +419,14 @@ static bool trans_fcvt_w_d(DisasContext *ctx, arg_fcvt_w_d *a) static bool trans_fcvt_wu_d(DisasContext *ctx, arg_fcvt_wu_d *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_wu_d(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_wu_d(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -321,12 +434,15 @@ static bool trans_fcvt_wu_d(DisasContext *ctx, arg_fcvt_wu_d *a) static bool trans_fcvt_d_w(DisasContext *ctx, arg_fcvt_d_w *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_w(cpu_fpr[a->rd], cpu_env, src); + gen_helper_fcvt_d_w(dest, cpu_env, src); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -335,12 +451,15 @@ static bool trans_fcvt_d_w(DisasContext *ctx, arg_fcvt_d_w *a) static bool trans_fcvt_d_wu(DisasContext *ctx, arg_fcvt_d_wu *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_wu(cpu_fpr[a->rd], cpu_env, src); + gen_helper_fcvt_d_wu(dest, cpu_env, src); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -350,12 +469,14 @@ static bool trans_fcvt_l_d(DisasContext *ctx, arg_fcvt_l_d *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_l_d(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_l_d(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -364,12 +485,14 @@ static bool trans_fcvt_lu_d(DisasContext *ctx, arg_fcvt_lu_d *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_lu_d(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_lu_d(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -392,12 +515,15 @@ static bool trans_fcvt_d_l(DisasContext *ctx, arg_fcvt_d_l *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_l(cpu_fpr[a->rd], cpu_env, src); + gen_helper_fcvt_d_l(dest, cpu_env, src); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -407,12 +533,15 @@ static bool trans_fcvt_d_lu(DisasContext *ctx, arg_fcvt_d_lu *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_lu(cpu_fpr[a->rd], cpu_env, src); + gen_helper_fcvt_d_lu(dest, cpu_env, src); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index 0aac87f7db..a1d3eb52ad 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -20,7 +20,14 @@ #define REQUIRE_FPU do {\ if (ctx->mstatus_fs == 0) \ - return false; \ + if (!ctx->cfg_ptr->ext_zfinx) \ + return false; \ +} while (0) + +#define REQUIRE_ZFINX_OR_F(ctx) do {\ + if (!ctx->cfg_ptr->ext_zfinx) { \ + REQUIRE_EXT(ctx, RVF); \ + } \ } while (0) static bool trans_flw(DisasContext *ctx, arg_flw *a) @@ -55,10 +62,16 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fmadd_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fmadd_s(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -66,10 +79,16 @@ static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a) static bool trans_fmsub_s(DisasContext *ctx, arg_fmsub_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fmsub_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fmsub_s(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -77,10 +96,16 @@ static bool trans_fmsub_s(DisasContext *ctx, arg_fmsub_s *a) static bool trans_fnmsub_s(DisasContext *ctx, arg_fnmsub_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fnmsub_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fnmsub_s(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -88,10 +113,16 @@ static bool trans_fnmsub_s(DisasContext *ctx, arg_fnmsub_s *a) static bool trans_fnmadd_s(DisasContext *ctx, arg_fnmadd_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); + gen_set_rm(ctx, a->rm); - gen_helper_fnmadd_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fnmadd_s(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -99,11 +130,15 @@ static bool trans_fnmadd_s(DisasContext *ctx, arg_fnmadd_s *a) static bool trans_fadd_s(DisasContext *ctx, arg_fadd_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fadd_s(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fadd_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -111,11 +146,15 @@ static bool trans_fadd_s(DisasContext *ctx, arg_fadd_s *a) static bool trans_fsub_s(DisasContext *ctx, arg_fsub_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fsub_s(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fsub_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -123,11 +162,15 @@ static bool trans_fsub_s(DisasContext *ctx, arg_fsub_s *a) static bool trans_fmul_s(DisasContext *ctx, arg_fmul_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fmul_s(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fmul_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -135,11 +178,15 @@ static bool trans_fmul_s(DisasContext *ctx, arg_fmul_s *a) static bool trans_fdiv_s(DisasContext *ctx, arg_fdiv_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fdiv_s(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fdiv_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -147,10 +194,14 @@ static bool trans_fdiv_s(DisasContext *ctx, arg_fdiv_s *a) static bool trans_fsqrt_s(DisasContext *ctx, arg_fsqrt_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fsqrt_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + gen_helper_fsqrt_s(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -158,22 +209,37 @@ static bool trans_fsqrt_s(DisasContext *ctx, arg_fsqrt_s *a) static bool trans_fsgnj_s(DisasContext *ctx, arg_fsgnj_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); if (a->rs1 == a->rs2) { /* FMOV */ - gen_check_nanbox_s(cpu_fpr[a->rd], cpu_fpr[a->rs1]); + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_s(dest, src1); + } else { + tcg_gen_ext32s_i64(dest, src1); + } } else { /* FSGNJ */ - TCGv_i64 rs1 = tcg_temp_new_i64(); - TCGv_i64 rs2 = tcg_temp_new_i64(); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]); - gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]); + if (!ctx->cfg_ptr->ext_zfinx) { + TCGv_i64 rs1 = tcg_temp_new_i64(); + TCGv_i64 rs2 = tcg_temp_new_i64(); + gen_check_nanbox_s(rs1, src1); + gen_check_nanbox_s(rs2, src2); - /* This formulation retains the nanboxing of rs2. */ - tcg_gen_deposit_i64(cpu_fpr[a->rd], rs2, rs1, 0, 31); - tcg_temp_free_i64(rs1); - tcg_temp_free_i64(rs2); + /* This formulation retains the nanboxing of rs2 in normal 'F'. */ + tcg_gen_deposit_i64(dest, rs2, rs1, 0, 31); + + tcg_temp_free_i64(rs1); + tcg_temp_free_i64(rs2); + } else { + tcg_gen_deposit_i64(dest, src2, src1, 0, 31); + tcg_gen_ext32s_i64(dest, dest); + } } + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -183,16 +249,27 @@ static bool trans_fsgnjn_s(DisasContext *ctx, arg_fsgnjn_s *a) TCGv_i64 rs1, rs2, mask; REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); rs1 = tcg_temp_new_i64(); - gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]); - - if (a->rs1 == a->rs2) { /* FNEG */ - tcg_gen_xori_i64(cpu_fpr[a->rd], rs1, MAKE_64BIT_MASK(31, 1)); + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_s(rs1, src1); } else { + tcg_gen_mov_i64(rs1, src1); + } + if (a->rs1 == a->rs2) { /* FNEG */ + tcg_gen_xori_i64(dest, rs1, MAKE_64BIT_MASK(31, 1)); + } else { + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); rs2 = tcg_temp_new_i64(); - gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]); + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_s(rs2, src2); + } else { + tcg_gen_mov_i64(rs2, src2); + } /* * Replace bit 31 in rs1 with inverse in rs2. @@ -200,13 +277,17 @@ static bool trans_fsgnjn_s(DisasContext *ctx, arg_fsgnjn_s *a) */ mask = tcg_constant_i64(~MAKE_64BIT_MASK(31, 1)); tcg_gen_nor_i64(rs2, rs2, mask); - tcg_gen_and_i64(rs1, mask, rs1); - tcg_gen_or_i64(cpu_fpr[a->rd], rs1, rs2); + tcg_gen_and_i64(dest, mask, rs1); + tcg_gen_or_i64(dest, dest, rs2); tcg_temp_free_i64(rs2); } + /* signed-extended intead of nanboxing for result if enable zfinx */ + if (ctx->cfg_ptr->ext_zfinx) { + tcg_gen_ext32s_i64(dest, dest); + } + gen_set_fpr_hs(ctx, a->rd, dest); tcg_temp_free_i64(rs1); - mark_fs_dirty(ctx); return true; } @@ -216,28 +297,45 @@ static bool trans_fsgnjx_s(DisasContext *ctx, arg_fsgnjx_s *a) TCGv_i64 rs1, rs2; REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); rs1 = tcg_temp_new_i64(); - gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]); + + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_s(rs1, src1); + } else { + tcg_gen_mov_i64(rs1, src1); + } if (a->rs1 == a->rs2) { /* FABS */ - tcg_gen_andi_i64(cpu_fpr[a->rd], rs1, ~MAKE_64BIT_MASK(31, 1)); + tcg_gen_andi_i64(dest, rs1, ~MAKE_64BIT_MASK(31, 1)); } else { + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); rs2 = tcg_temp_new_i64(); - gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]); + + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_s(rs2, src2); + } else { + tcg_gen_mov_i64(rs2, src2); + } /* * Xor bit 31 in rs1 with that in rs2. * This formulation retains the nanboxing of rs1. */ - tcg_gen_andi_i64(rs2, rs2, MAKE_64BIT_MASK(31, 1)); - tcg_gen_xor_i64(cpu_fpr[a->rd], rs1, rs2); + tcg_gen_andi_i64(dest, rs2, MAKE_64BIT_MASK(31, 1)); + tcg_gen_xor_i64(dest, rs1, dest); tcg_temp_free_i64(rs2); } + /* signed-extended intead of nanboxing for result if enable zfinx */ + if (ctx->cfg_ptr->ext_zfinx) { + tcg_gen_ext32s_i64(dest, dest); + } tcg_temp_free_i64(rs1); - + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -245,10 +343,14 @@ static bool trans_fsgnjx_s(DisasContext *ctx, arg_fsgnjx_s *a) static bool trans_fmin_s(DisasContext *ctx, arg_fmin_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); - gen_helper_fmin_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2]); + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmin_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -256,10 +358,14 @@ static bool trans_fmin_s(DisasContext *ctx, arg_fmin_s *a) static bool trans_fmax_s(DisasContext *ctx, arg_fmax_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); - gen_helper_fmax_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2]); + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmax_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -267,12 +373,13 @@ static bool trans_fmax_s(DisasContext *ctx, arg_fmax_s *a) static bool trans_fcvt_w_s(DisasContext *ctx, arg_fcvt_w_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_w_s(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_w_s(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -280,12 +387,13 @@ static bool trans_fcvt_w_s(DisasContext *ctx, arg_fcvt_w_s *a) static bool trans_fcvt_wu_s(DisasContext *ctx, arg_fcvt_wu_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_wu_s(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_wu_s(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -294,14 +402,14 @@ static bool trans_fmv_x_w(DisasContext *ctx, arg_fmv_x_w *a) { /* NOTE: This was FMV.X.S in an earlier version of the ISA spec! */ REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); - + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); #if defined(TARGET_RISCV64) - tcg_gen_ext32s_tl(dest, cpu_fpr[a->rs1]); + tcg_gen_ext32s_tl(dest, src1); #else - tcg_gen_extrl_i64_i32(dest, cpu_fpr[a->rs1]); + tcg_gen_extrl_i64_i32(dest, src1); #endif gen_set_gpr(ctx, a->rd, dest); @@ -311,11 +419,13 @@ static bool trans_fmv_x_w(DisasContext *ctx, arg_fmv_x_w *a) static bool trans_feq_s(DisasContext *ctx, arg_feq_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_feq_s(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_feq_s(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -323,11 +433,13 @@ static bool trans_feq_s(DisasContext *ctx, arg_feq_s *a) static bool trans_flt_s(DisasContext *ctx, arg_flt_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_flt_s(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_flt_s(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -335,11 +447,13 @@ static bool trans_flt_s(DisasContext *ctx, arg_flt_s *a) static bool trans_fle_s(DisasContext *ctx, arg_fle_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fle_s(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fle_s(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -347,11 +461,12 @@ static bool trans_fle_s(DisasContext *ctx, arg_fle_s *a) static bool trans_fclass_s(DisasContext *ctx, arg_fclass_s *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); - gen_helper_fclass_s(dest, cpu_fpr[a->rs1]); + gen_helper_fclass_s(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -359,13 +474,14 @@ static bool trans_fclass_s(DisasContext *ctx, arg_fclass_s *a) static bool trans_fcvt_s_w(DisasContext *ctx, arg_fcvt_s_w *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_w(cpu_fpr[a->rd], cpu_env, src); - + gen_helper_fcvt_s_w(dest, cpu_env, src); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -373,13 +489,14 @@ static bool trans_fcvt_s_w(DisasContext *ctx, arg_fcvt_s_w *a) static bool trans_fcvt_s_wu(DisasContext *ctx, arg_fcvt_s_wu *a) { REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_wu(cpu_fpr[a->rd], cpu_env, src); - + gen_helper_fcvt_s_wu(dest, cpu_env, src); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -388,13 +505,14 @@ static bool trans_fmv_w_x(DisasContext *ctx, arg_fmv_w_x *a) { /* NOTE: This was FMV.S.X in an earlier version of the ISA spec! */ REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); - tcg_gen_extu_tl_i64(cpu_fpr[a->rd], src); - gen_nanbox_s(cpu_fpr[a->rd], cpu_fpr[a->rd]); - + tcg_gen_extu_tl_i64(dest, src); + gen_nanbox_s(dest, dest); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -403,12 +521,13 @@ static bool trans_fcvt_l_s(DisasContext *ctx, arg_fcvt_l_s *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_l_s(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_l_s(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -417,12 +536,13 @@ static bool trans_fcvt_lu_s(DisasContext *ctx, arg_fcvt_lu_s *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_lu_s(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_lu_s(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -431,13 +551,14 @@ static bool trans_fcvt_s_l(DisasContext *ctx, arg_fcvt_s_l *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_l(cpu_fpr[a->rd], cpu_env, src); - + gen_helper_fcvt_s_l(dest, cpu_env, src); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -446,13 +567,14 @@ static bool trans_fcvt_s_lu(DisasContext *ctx, arg_fcvt_s_lu *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_EXT(ctx, RVF); + REQUIRE_ZFINX_OR_F(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_lu(cpu_fpr[a->rd], cpu_env, src); - + gen_helper_fcvt_s_lu(dest, cpu_env, src); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } diff --git a/target/riscv/insn_trans/trans_rvk.c.inc b/target/riscv/insn_trans/trans_rvk.c.inc new file mode 100644 index 0000000000..90f4eeff60 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvk.c.inc @@ -0,0 +1,391 @@ +/* + * RISC-V translation routines for the Zk[nd,ne,nh,sed,sh] Standard Extension. + * + * Copyright (c) 2021 Ruibo Lu, luruibo2000@163.com + * Copyright (c) 2021 Zewen Ye, lustrew@foxmail.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZKND(ctx) do { \ + if (!ctx->cfg_ptr->ext_zknd) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZKNE(ctx) do { \ + if (!ctx->cfg_ptr->ext_zkne) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZKNH(ctx) do { \ + if (!ctx->cfg_ptr->ext_zknh) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZKSED(ctx) do { \ + if (!ctx->cfg_ptr->ext_zksed) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZKSH(ctx) do { \ + if (!ctx->cfg_ptr->ext_zksh) { \ + return false; \ + } \ +} while (0) + +static bool gen_aes32_sm4(DisasContext *ctx, arg_k_aes *a, + void (*func)(TCGv, TCGv, TCGv, TCGv)) +{ + TCGv shamt = tcg_constant_tl(a->shamt); + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + func(dest, src1, src2, shamt); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +static bool trans_aes32esmi(DisasContext *ctx, arg_aes32esmi *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNE(ctx); + return gen_aes32_sm4(ctx, a, gen_helper_aes32esmi); +} + +static bool trans_aes32esi(DisasContext *ctx, arg_aes32esi *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNE(ctx); + return gen_aes32_sm4(ctx, a, gen_helper_aes32esi); +} + +static bool trans_aes32dsmi(DisasContext *ctx, arg_aes32dsmi *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKND(ctx); + return gen_aes32_sm4(ctx, a, gen_helper_aes32dsmi); +} + +static bool trans_aes32dsi(DisasContext *ctx, arg_aes32dsi *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKND(ctx); + return gen_aes32_sm4(ctx, a, gen_helper_aes32dsi); +} + +static bool trans_aes64es(DisasContext *ctx, arg_aes64es *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKNE(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_aes64es, NULL); +} + +static bool trans_aes64esm(DisasContext *ctx, arg_aes64esm *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKNE(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_aes64esm, NULL); +} + +static bool trans_aes64ds(DisasContext *ctx, arg_aes64ds *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKND(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_aes64ds, NULL); +} + +static bool trans_aes64dsm(DisasContext *ctx, arg_aes64dsm *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKND(ctx); + return gen_arith(ctx, a, EXT_NONE, gen_helper_aes64dsm, NULL); +} + +static bool trans_aes64ks2(DisasContext *ctx, arg_aes64ks2 *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_EITHER_EXT(ctx, zknd, zkne); + return gen_arith(ctx, a, EXT_NONE, gen_helper_aes64ks2, NULL); +} + +static bool trans_aes64ks1i(DisasContext *ctx, arg_aes64ks1i *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_EITHER_EXT(ctx, zknd, zkne); + + if (a->imm > 0xA) { + return false; + } + + return gen_arith_imm_tl(ctx, a, EXT_NONE, gen_helper_aes64ks1i, NULL); +} + +static bool trans_aes64im(DisasContext *ctx, arg_aes64im *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKND(ctx); + return gen_unary(ctx, a, EXT_NONE, gen_helper_aes64im); +} + +static bool gen_sha256(DisasContext *ctx, arg_r2 *a, DisasExtend ext, + void (*func)(TCGv_i32, TCGv_i32, int32_t), + int32_t num1, int32_t num2, int32_t num3) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, src1); + tcg_gen_rotri_i32(t1, t0, num1); + tcg_gen_rotri_i32(t2, t0, num2); + tcg_gen_xor_i32(t1, t1, t2); + func(t2, t0, num3); + tcg_gen_xor_i32(t1, t1, t2); + tcg_gen_ext_i32_tl(dest, t1); + + gen_set_gpr(ctx, a->rd, dest); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + return true; +} + +static bool trans_sha256sig0(DisasContext *ctx, arg_sha256sig0 *a) +{ + REQUIRE_ZKNH(ctx); + return gen_sha256(ctx, a, EXT_NONE, tcg_gen_shri_i32, 7, 18, 3); +} + +static bool trans_sha256sig1(DisasContext *ctx, arg_sha256sig1 *a) +{ + REQUIRE_ZKNH(ctx); + return gen_sha256(ctx, a, EXT_NONE, tcg_gen_shri_i32, 17, 19, 10); +} + +static bool trans_sha256sum0(DisasContext *ctx, arg_sha256sum0 *a) +{ + REQUIRE_ZKNH(ctx); + return gen_sha256(ctx, a, EXT_NONE, tcg_gen_rotri_i32, 2, 13, 22); +} + +static bool trans_sha256sum1(DisasContext *ctx, arg_sha256sum1 *a) +{ + REQUIRE_ZKNH(ctx); + return gen_sha256(ctx, a, EXT_NONE, tcg_gen_rotri_i32, 6, 11, 25); +} + +static bool gen_sha512_rv32(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*func1)(TCGv_i64, TCGv_i64, int64_t), + void (*func2)(TCGv_i64, TCGv_i64, int64_t), + int64_t num1, int64_t num2, int64_t num3) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + TCGv src2 = get_gpr(ctx, a->rs2, ext); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(t0, src1, src2); + func1(t1, t0, num1); + func2(t2, t0, num2); + tcg_gen_xor_i64(t1, t1, t2); + tcg_gen_rotri_i64(t2, t0, num3); + tcg_gen_xor_i64(t1, t1, t2); + tcg_gen_trunc_i64_tl(dest, t1); + + gen_set_gpr(ctx, a->rd, dest); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + return true; +} + +static bool trans_sha512sum0r(DisasContext *ctx, arg_sha512sum0r *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv32(ctx, a, EXT_NONE, tcg_gen_rotli_i64, + tcg_gen_rotli_i64, 25, 30, 28); +} + +static bool trans_sha512sum1r(DisasContext *ctx, arg_sha512sum1r *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv32(ctx, a, EXT_NONE, tcg_gen_rotli_i64, + tcg_gen_rotri_i64, 23, 14, 18); +} + +static bool trans_sha512sig0l(DisasContext *ctx, arg_sha512sig0l *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv32(ctx, a, EXT_NONE, tcg_gen_rotri_i64, + tcg_gen_rotri_i64, 1, 7, 8); +} + +static bool trans_sha512sig1l(DisasContext *ctx, arg_sha512sig1l *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv32(ctx, a, EXT_NONE, tcg_gen_rotli_i64, + tcg_gen_rotri_i64, 3, 6, 19); +} + +static bool gen_sha512h_rv32(DisasContext *ctx, arg_r *a, DisasExtend ext, + void (*func)(TCGv_i64, TCGv_i64, int64_t), + int64_t num1, int64_t num2, int64_t num3) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + TCGv src2 = get_gpr(ctx, a->rs2, ext); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(t0, src1, src2); + func(t1, t0, num1); + tcg_gen_ext32u_i64(t2, t0); + tcg_gen_shri_i64(t2, t2, num2); + tcg_gen_xor_i64(t1, t1, t2); + tcg_gen_rotri_i64(t2, t0, num3); + tcg_gen_xor_i64(t1, t1, t2); + tcg_gen_trunc_i64_tl(dest, t1); + + gen_set_gpr(ctx, a->rd, dest); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + return true; +} + +static bool trans_sha512sig0h(DisasContext *ctx, arg_sha512sig0h *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512h_rv32(ctx, a, EXT_NONE, tcg_gen_rotri_i64, 1, 7, 8); +} + +static bool trans_sha512sig1h(DisasContext *ctx, arg_sha512sig1h *a) +{ + REQUIRE_32BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512h_rv32(ctx, a, EXT_NONE, tcg_gen_rotli_i64, 3, 6, 19); +} + +static bool gen_sha512_rv64(DisasContext *ctx, arg_r2 *a, DisasExtend ext, + void (*func)(TCGv_i64, TCGv_i64, int64_t), + int64_t num1, int64_t num2, int64_t num3) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_extu_tl_i64(t0, src1); + tcg_gen_rotri_i64(t1, t0, num1); + tcg_gen_rotri_i64(t2, t0, num2); + tcg_gen_xor_i64(t1, t1, t2); + func(t2, t0, num3); + tcg_gen_xor_i64(t1, t1, t2); + tcg_gen_trunc_i64_tl(dest, t1); + + gen_set_gpr(ctx, a->rd, dest); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + return true; +} + +static bool trans_sha512sig0(DisasContext *ctx, arg_sha512sig0 *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv64(ctx, a, EXT_NONE, tcg_gen_shri_i64, 1, 8, 7); +} + +static bool trans_sha512sig1(DisasContext *ctx, arg_sha512sig1 *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv64(ctx, a, EXT_NONE, tcg_gen_shri_i64, 19, 61, 6); +} + +static bool trans_sha512sum0(DisasContext *ctx, arg_sha512sum0 *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv64(ctx, a, EXT_NONE, tcg_gen_rotri_i64, 28, 34, 39); +} + +static bool trans_sha512sum1(DisasContext *ctx, arg_sha512sum1 *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZKNH(ctx); + return gen_sha512_rv64(ctx, a, EXT_NONE, tcg_gen_rotri_i64, 14, 18, 41); +} + +/* SM3 */ +static bool gen_sm3(DisasContext *ctx, arg_r2 *a, int32_t b, int32_t c) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, src1); + tcg_gen_rotli_i32(t1, t0, b); + tcg_gen_xor_i32(t1, t0, t1); + tcg_gen_rotli_i32(t0, t0, c); + tcg_gen_xor_i32(t1, t1, t0); + tcg_gen_ext_i32_tl(dest, t1); + gen_set_gpr(ctx, a->rd, dest); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + return true; +} + +static bool trans_sm3p0(DisasContext *ctx, arg_sm3p0 *a) +{ + REQUIRE_ZKSH(ctx); + return gen_sm3(ctx, a, 9, 17); +} + +static bool trans_sm3p1(DisasContext *ctx, arg_sm3p1 *a) +{ + REQUIRE_ZKSH(ctx); + return gen_sm3(ctx, a, 15, 23); +} + +/* SM4 */ +static bool trans_sm4ed(DisasContext *ctx, arg_sm4ed *a) +{ + REQUIRE_ZKSED(ctx); + return gen_aes32_sm4(ctx, a, gen_helper_sm4ed); +} + +static bool trans_sm4ks(DisasContext *ctx, arg_sm4ks *a) +{ + REQUIRE_ZKSED(ctx); + return gen_aes32_sm4(ctx, a, gen_helper_sm4ks); +} diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 275fded6e4..90327509f7 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1121,6 +1121,10 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, gen_helper_ldst_whole *fn, DisasContext *s, bool is_store) { + uint32_t evl = (s->cfg_ptr->vlen / 8) * nf / (1 << s->sew); + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, evl, over); + TCGv_ptr dest; TCGv base; TCGv_i32 desc; @@ -1140,6 +1144,7 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, if (!is_store) { mark_vs_dirty(s); } + gen_set_label(over); return true; } @@ -1193,7 +1198,7 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, 8, true) static inline uint32_t MAXSZ(DisasContext *s) { int scale = s->lmul - 3; - return scale < 0 ? s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; + return s->cfg_ptr->vlen >> -scale; } static bool opivv_check(DisasContext *s, arg_rmrr *a) @@ -3288,7 +3293,7 @@ static void load_element(TCGv_i64 dest, TCGv_ptr base, /* offset of the idx element with base regsiter r */ static uint32_t endian_ofs(DisasContext *s, int r, int idx) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN return vreg_ofs(s, r) + ((idx ^ (7 >> s->sew)) << s->sew); #else return vreg_ofs(s, r) + (idx << s->sew); @@ -3298,7 +3303,7 @@ static uint32_t endian_ofs(DisasContext *s, int r, int idx) /* adjust the index according to the endian */ static void endian_adjust(TCGv_i32 ofs, int sew) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN tcg_gen_xori_i32(ofs, ofs, 7 >> sew); #endif } @@ -3592,8 +3597,7 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a) if (a->vm && s->vl_eq_vlmax) { int scale = s->lmul - (s->sew + 3); - int vlmax = scale < 0 ? - s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; + int vlmax = s->cfg_ptr->vlen >> -scale; TCGv_i64 dest = tcg_temp_new_i64(); if (a->rs1 == 0) { @@ -3625,8 +3629,7 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a) if (a->vm && s->vl_eq_vlmax) { int scale = s->lmul - (s->sew + 3); - int vlmax = scale < 0 ? - s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; + int vlmax = s->cfg_ptr->vlen >> -scale; if (a->rs1 >= vlmax) { tcg_gen_gvec_dup_imm(MO_64, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), 0); @@ -3692,7 +3695,7 @@ static bool trans_vcompress_vm(DisasContext *s, arg_r *a) * Whole Vector Register Move Instructions ignore vtype and vl setting. * Thus, we don't need to check vill bit. (Section 16.6) */ -#define GEN_VMV_WHOLE_TRANS(NAME, LEN, SEQ) \ +#define GEN_VMV_WHOLE_TRANS(NAME, LEN) \ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ { \ if (require_rvv(s) && \ @@ -3707,13 +3710,8 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ } else { \ TCGLabel *over = gen_new_label(); \ tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, maxsz, over); \ - \ - static gen_helper_gvec_2_ptr * const fns[4] = { \ - gen_helper_vmv1r_v, gen_helper_vmv2r_v, \ - gen_helper_vmv4r_v, gen_helper_vmv8r_v, \ - }; \ tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), \ - cpu_env, maxsz, maxsz, 0, fns[SEQ]); \ + cpu_env, maxsz, maxsz, 0, gen_helper_vmvr_v); \ mark_vs_dirty(s); \ gen_set_label(over); \ } \ @@ -3722,10 +3720,10 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ return false; \ } -GEN_VMV_WHOLE_TRANS(vmv1r_v, 1, 0) -GEN_VMV_WHOLE_TRANS(vmv2r_v, 2, 1) -GEN_VMV_WHOLE_TRANS(vmv4r_v, 4, 2) -GEN_VMV_WHOLE_TRANS(vmv8r_v, 8, 3) +GEN_VMV_WHOLE_TRANS(vmv1r_v, 1) +GEN_VMV_WHOLE_TRANS(vmv2r_v, 2) +GEN_VMV_WHOLE_TRANS(vmv4r_v, 4) +GEN_VMV_WHOLE_TRANS(vmv8r_v, 8) static bool int_ext_check(DisasContext *s, arg_rmr *a, uint8_t div) { diff --git a/target/riscv/insn_trans/trans_rvzfh.c.inc b/target/riscv/insn_trans/trans_rvzfh.c.inc index 608c51da2c..5d07150cd0 100644 --- a/target/riscv/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/insn_trans/trans_rvzfh.c.inc @@ -22,12 +22,25 @@ } \ } while (0) +#define REQUIRE_ZHINX_OR_ZFH(ctx) do { \ + if (!ctx->cfg_ptr->ext_zhinx && !ctx->cfg_ptr->ext_zfh) { \ + return false; \ + } \ +} while (0) + #define REQUIRE_ZFH_OR_ZFHMIN(ctx) do { \ if (!(ctx->cfg_ptr->ext_zfh || ctx->cfg_ptr->ext_zfhmin)) { \ return false; \ } \ } while (0) +#define REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx) do { \ + if (!(ctx->cfg_ptr->ext_zfh || ctx->cfg_ptr->ext_zfhmin || \ + ctx->cfg_ptr->ext_zhinx || ctx->cfg_ptr->ext_zhinxmin)) { \ + return false; \ + } \ +} while (0) + static bool trans_flh(DisasContext *ctx, arg_flh *a) { TCGv_i64 dest; @@ -73,11 +86,16 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a) static bool trans_fmadd_h(DisasContext *ctx, arg_fmadd_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmadd_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fmadd_h(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -85,11 +103,16 @@ static bool trans_fmadd_h(DisasContext *ctx, arg_fmadd_h *a) static bool trans_fmsub_h(DisasContext *ctx, arg_fmsub_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmsub_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fmsub_h(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -97,11 +120,16 @@ static bool trans_fmsub_h(DisasContext *ctx, arg_fmsub_h *a) static bool trans_fnmsub_h(DisasContext *ctx, arg_fnmsub_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmsub_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fnmsub_h(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -109,11 +137,16 @@ static bool trans_fnmsub_h(DisasContext *ctx, arg_fnmsub_h *a) static bool trans_fnmadd_h(DisasContext *ctx, arg_fnmadd_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmadd_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + gen_helper_fnmadd_h(dest, cpu_env, src1, src2, src3); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -121,11 +154,15 @@ static bool trans_fnmadd_h(DisasContext *ctx, arg_fnmadd_h *a) static bool trans_fadd_h(DisasContext *ctx, arg_fadd_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fadd_h(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fadd_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -133,11 +170,15 @@ static bool trans_fadd_h(DisasContext *ctx, arg_fadd_h *a) static bool trans_fsub_h(DisasContext *ctx, arg_fsub_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fsub_h(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fsub_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -145,11 +186,15 @@ static bool trans_fsub_h(DisasContext *ctx, arg_fsub_h *a) static bool trans_fmul_h(DisasContext *ctx, arg_fmul_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fmul_h(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fmul_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -157,11 +202,15 @@ static bool trans_fmul_h(DisasContext *ctx, arg_fmul_h *a) static bool trans_fdiv_h(DisasContext *ctx, arg_fdiv_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fdiv_h(cpu_fpr[a->rd], cpu_env, - cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fdiv_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -169,10 +218,14 @@ static bool trans_fdiv_h(DisasContext *ctx, arg_fdiv_h *a) static bool trans_fsqrt_h(DisasContext *ctx, arg_fsqrt_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fsqrt_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + gen_helper_fsqrt_h(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -180,23 +233,37 @@ static bool trans_fsqrt_h(DisasContext *ctx, arg_fsqrt_h *a) static bool trans_fsgnj_h(DisasContext *ctx, arg_fsgnj_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); if (a->rs1 == a->rs2) { /* FMOV */ - gen_check_nanbox_h(cpu_fpr[a->rd], cpu_fpr[a->rs1]); + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_h(dest, src1); + } else { + tcg_gen_ext16s_i64(dest, src1); + } } else { - TCGv_i64 rs1 = tcg_temp_new_i64(); - TCGv_i64 rs2 = tcg_temp_new_i64(); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_check_nanbox_h(rs1, cpu_fpr[a->rs1]); - gen_check_nanbox_h(rs2, cpu_fpr[a->rs2]); + if (!ctx->cfg_ptr->ext_zfinx) { + TCGv_i64 rs1 = tcg_temp_new_i64(); + TCGv_i64 rs2 = tcg_temp_new_i64(); + gen_check_nanbox_h(rs1, src1); + gen_check_nanbox_h(rs2, src2); - /* This formulation retains the nanboxing of rs2. */ - tcg_gen_deposit_i64(cpu_fpr[a->rd], rs2, rs1, 0, 15); - tcg_temp_free_i64(rs1); - tcg_temp_free_i64(rs2); + /* This formulation retains the nanboxing of rs2 in normal 'Zfh'. */ + tcg_gen_deposit_i64(dest, rs2, rs1, 0, 15); + + tcg_temp_free_i64(rs1); + tcg_temp_free_i64(rs2); + } else { + tcg_gen_deposit_i64(dest, src2, src1, 0, 15); + tcg_gen_ext16s_i64(dest, dest); + } } - + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -206,16 +273,29 @@ static bool trans_fsgnjn_h(DisasContext *ctx, arg_fsgnjn_h *a) TCGv_i64 rs1, rs2, mask; REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); rs1 = tcg_temp_new_i64(); - gen_check_nanbox_h(rs1, cpu_fpr[a->rs1]); + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_h(rs1, src1); + } else { + tcg_gen_mov_i64(rs1, src1); + } if (a->rs1 == a->rs2) { /* FNEG */ - tcg_gen_xori_i64(cpu_fpr[a->rd], rs1, MAKE_64BIT_MASK(15, 1)); + tcg_gen_xori_i64(dest, rs1, MAKE_64BIT_MASK(15, 1)); } else { + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); rs2 = tcg_temp_new_i64(); - gen_check_nanbox_h(rs2, cpu_fpr[a->rs2]); + + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_h(rs2, src2); + } else { + tcg_gen_mov_i64(rs2, src2); + } /* * Replace bit 15 in rs1 with inverse in rs2. @@ -224,12 +304,17 @@ static bool trans_fsgnjn_h(DisasContext *ctx, arg_fsgnjn_h *a) mask = tcg_const_i64(~MAKE_64BIT_MASK(15, 1)); tcg_gen_not_i64(rs2, rs2); tcg_gen_andc_i64(rs2, rs2, mask); - tcg_gen_and_i64(rs1, mask, rs1); - tcg_gen_or_i64(cpu_fpr[a->rd], rs1, rs2); + tcg_gen_and_i64(dest, mask, rs1); + tcg_gen_or_i64(dest, dest, rs2); tcg_temp_free_i64(mask); tcg_temp_free_i64(rs2); } + /* signed-extended intead of nanboxing for result if enable zfinx */ + if (ctx->cfg_ptr->ext_zfinx) { + tcg_gen_ext16s_i64(dest, dest); + } + tcg_temp_free_i64(rs1); mark_fs_dirty(ctx); return true; } @@ -239,27 +324,44 @@ static bool trans_fsgnjx_h(DisasContext *ctx, arg_fsgnjx_h *a) TCGv_i64 rs1, rs2; REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); rs1 = tcg_temp_new_i64(); - gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]); + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_h(rs1, src1); + } else { + tcg_gen_mov_i64(rs1, src1); + } if (a->rs1 == a->rs2) { /* FABS */ - tcg_gen_andi_i64(cpu_fpr[a->rd], rs1, ~MAKE_64BIT_MASK(15, 1)); + tcg_gen_andi_i64(dest, rs1, ~MAKE_64BIT_MASK(15, 1)); } else { + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); rs2 = tcg_temp_new_i64(); - gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]); + + if (!ctx->cfg_ptr->ext_zfinx) { + gen_check_nanbox_h(rs2, src2); + } else { + tcg_gen_mov_i64(rs2, src2); + } /* * Xor bit 15 in rs1 with that in rs2. * This formulation retains the nanboxing of rs1. */ - tcg_gen_andi_i64(rs2, rs2, MAKE_64BIT_MASK(15, 1)); - tcg_gen_xor_i64(cpu_fpr[a->rd], rs1, rs2); + tcg_gen_andi_i64(dest, rs2, MAKE_64BIT_MASK(15, 1)); + tcg_gen_xor_i64(dest, rs1, dest); tcg_temp_free_i64(rs2); } - + /* signed-extended intead of nanboxing for result if enable zfinx */ + if (ctx->cfg_ptr->ext_zfinx) { + tcg_gen_ext16s_i64(dest, dest); + } + tcg_temp_free_i64(rs1); mark_fs_dirty(ctx); return true; } @@ -267,10 +369,14 @@ static bool trans_fsgnjx_h(DisasContext *ctx, arg_fsgnjx_h *a) static bool trans_fmin_h(DisasContext *ctx, arg_fmin_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); - gen_helper_fmin_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2]); + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmin_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -278,10 +384,14 @@ static bool trans_fmin_h(DisasContext *ctx, arg_fmin_h *a) static bool trans_fmax_h(DisasContext *ctx, arg_fmax_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); - gen_helper_fmax_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], - cpu_fpr[a->rs2]); + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmax_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; } @@ -289,10 +399,14 @@ static bool trans_fmax_h(DisasContext *ctx, arg_fmax_h *a) static bool trans_fcvt_s_h(DisasContext *ctx, arg_fcvt_s_h *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); + REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_s_h(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -302,26 +416,32 @@ static bool trans_fcvt_s_h(DisasContext *ctx, arg_fcvt_s_h *a) static bool trans_fcvt_d_h(DisasContext *ctx, arg_fcvt_d_h *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + REQUIRE_ZDINX_OR_D(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_d_h(dest, cpu_env, src1); + gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); - return true; } static bool trans_fcvt_h_s(DisasContext *ctx, arg_fcvt_h_s *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); + REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); - + gen_helper_fcvt_h_s(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -330,12 +450,15 @@ static bool trans_fcvt_h_s(DisasContext *ctx, arg_fcvt_h_s *a) static bool trans_fcvt_h_d(DisasContext *ctx, arg_fcvt_h_d *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); - REQUIRE_EXT(ctx, RVD); + REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + REQUIRE_ZDINX_OR_D(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); - + gen_helper_fcvt_h_d(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -344,11 +467,13 @@ static bool trans_fcvt_h_d(DisasContext *ctx, arg_fcvt_h_d *a) static bool trans_feq_h(DisasContext *ctx, arg_feq_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_feq_h(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_feq_h(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -356,11 +481,13 @@ static bool trans_feq_h(DisasContext *ctx, arg_feq_h *a) static bool trans_flt_h(DisasContext *ctx, arg_flt_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_flt_h(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_flt_h(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; @@ -369,11 +496,13 @@ static bool trans_flt_h(DisasContext *ctx, arg_flt_h *a) static bool trans_fle_h(DisasContext *ctx, arg_fle_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fle_h(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_helper_fle_h(dest, cpu_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -381,11 +510,12 @@ static bool trans_fle_h(DisasContext *ctx, arg_fle_h *a) static bool trans_fclass_h(DisasContext *ctx, arg_fclass_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); - gen_helper_fclass_h(dest, cpu_fpr[a->rs1]); + gen_helper_fclass_h(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -393,12 +523,13 @@ static bool trans_fclass_h(DisasContext *ctx, arg_fclass_h *a) static bool trans_fcvt_w_h(DisasContext *ctx, arg_fcvt_w_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_w_h(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_w_h(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -406,12 +537,13 @@ static bool trans_fcvt_w_h(DisasContext *ctx, arg_fcvt_w_h *a) static bool trans_fcvt_wu_h(DisasContext *ctx, arg_fcvt_wu_h *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_wu_h(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_wu_h(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -419,12 +551,14 @@ static bool trans_fcvt_wu_h(DisasContext *ctx, arg_fcvt_wu_h *a) static bool trans_fcvt_h_w(DisasContext *ctx, arg_fcvt_h_w *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_w(cpu_fpr[a->rd], cpu_env, t0); + gen_helper_fcvt_h_w(dest, cpu_env, t0); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -433,12 +567,14 @@ static bool trans_fcvt_h_w(DisasContext *ctx, arg_fcvt_h_w *a) static bool trans_fcvt_h_wu(DisasContext *ctx, arg_fcvt_h_wu *a) { REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_wu(cpu_fpr[a->rd], cpu_env, t0); + gen_helper_fcvt_h_wu(dest, cpu_env, t0); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -482,12 +618,13 @@ static bool trans_fcvt_l_h(DisasContext *ctx, arg_fcvt_l_h *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_l_h(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_l_h(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -496,12 +633,13 @@ static bool trans_fcvt_lu_h(DisasContext *ctx, arg_fcvt_lu_h *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_lu_h(dest, cpu_env, cpu_fpr[a->rs1]); + gen_helper_fcvt_lu_h(dest, cpu_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -510,12 +648,14 @@ static bool trans_fcvt_h_l(DisasContext *ctx, arg_fcvt_h_l *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_l(cpu_fpr[a->rd], cpu_env, t0); + gen_helper_fcvt_h_l(dest, cpu_env, t0); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -525,12 +665,14 @@ static bool trans_fcvt_h_lu(DisasContext *ctx, arg_fcvt_h_lu *a) { REQUIRE_64BIT(ctx); REQUIRE_FPU; - REQUIRE_ZFH(ctx); + REQUIRE_ZHINX_OR_ZFH(ctx); + TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_lu(cpu_fpr[a->rd], cpu_env, t0); + gen_helper_fcvt_h_lu(dest, cpu_env, t0); + gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 065e8162a2..dbb322bfa7 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -46,13 +46,23 @@ enum { RISCV_FRM_ROD = 8, /* Round to Odd */ }; -static inline uint64_t nanbox_s(float32 f) +static inline uint64_t nanbox_s(CPURISCVState *env, float32 f) { - return f | MAKE_64BIT_MASK(32, 32); + /* the value is sign-extended instead of NaN-boxing for zfinx */ + if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + return (int32_t)f; + } else { + return f | MAKE_64BIT_MASK(32, 32); + } } -static inline float32 check_nanbox_s(uint64_t f) +static inline float32 check_nanbox_s(CPURISCVState *env, uint64_t f) { + /* Disable NaN-boxing check when enable zfinx */ + if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + return (uint32_t)f; + } + uint64_t mask = MAKE_64BIT_MASK(32, 32); if (likely((f & mask) == mask)) { @@ -62,13 +72,23 @@ static inline float32 check_nanbox_s(uint64_t f) } } -static inline uint64_t nanbox_h(float16 f) +static inline uint64_t nanbox_h(CPURISCVState *env, float16 f) { - return f | MAKE_64BIT_MASK(16, 48); + /* the value is sign-extended instead of NaN-boxing for zfinx */ + if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + return (int16_t)f; + } else { + return f | MAKE_64BIT_MASK(16, 48); + } } -static inline float16 check_nanbox_h(uint64_t f) +static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) { + /* Disable nanbox check when enable zfinx */ + if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + return (uint16_t)f; + } + uint64_t mask = MAKE_64BIT_MASK(16, 48); if (likely((f & mask) == mask)) { diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index e6b7cb6d4d..70b4cff06f 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -21,7 +21,6 @@ #include -#include "qemu-common.h" #include "qemu/timer.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 5178b3fec9..2a437b29a1 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -216,7 +216,38 @@ static const VMStateDescription vmstate_kvmtimer = { VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; +static bool debug_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return riscv_feature(env, RISCV_FEATURE_DEBUG); +} + +static const VMStateDescription vmstate_debug_type2 = { + .name = "cpu/debug/type2", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(mcontrol, type2_trigger_t), + VMSTATE_UINTTL(maddress, type2_trigger_t), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_debug = { + .name = "cpu/debug", + .version_id = 1, + .minimum_version_id = 1, + .needed = debug_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(env.trigger_cur, RISCVCPU), + VMSTATE_STRUCT_ARRAY(env.type2_trig, RISCVCPU, TRIGGER_TYPE2_NUM, + 0, vmstate_debug_type2, type2_trigger_t), VMSTATE_END_OF_LIST() } }; @@ -231,6 +262,28 @@ static int riscv_cpu_post_load(void *opaque, int version_id) return 0; } +static bool envcfg_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return (env->priv_ver >= PRIV_VERSION_1_12_0 ? 1 : 0); +} + +static const VMStateDescription vmstate_envcfg = { + .name = "cpu/envcfg", + .version_id = 1, + .minimum_version_id = 1, + .needed = envcfg_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.menvcfg, RISCVCPU), + VMSTATE_UINTTL(env.senvcfg, RISCVCPU), + VMSTATE_UINT64(env.henvcfg, RISCVCPU), + + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", .version_id = 3, @@ -292,6 +345,8 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_pointermasking, &vmstate_rv128, &vmstate_kvmtimer, + &vmstate_envcfg, + &vmstate_debug, NULL } }; diff --git a/target/riscv/meson.build b/target/riscv/meson.build index 91f0ac32ff..096249f3a3 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -19,7 +19,8 @@ riscv_ss.add(files( 'vector_helper.c', 'bitmanip_helper.c', 'translate.c', - 'm128_helper.c' + 'm128_helper.c', + 'crypto_helper.c' )) riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) @@ -27,6 +28,7 @@ riscv_softmmu_ss = ss.source_set() riscv_softmmu_ss.add(files( 'arch_dump.c', 'pmp.c', + 'debug.c', 'monitor.c', 'machine.c' )) diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c index 7efb4b62c1..17e63fab00 100644 --- a/target/riscv/monitor.c +++ b/target/riscv/monitor.c @@ -84,6 +84,7 @@ static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, { hwaddr pte_addr; hwaddr paddr; + target_ulong last_start = -1; target_ulong pgsize; target_ulong pte; int ptshift; @@ -111,12 +112,13 @@ static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, * A leaf PTE has been found * * If current PTE's permission bits differ from the last one, - * or current PTE's ppn does not make a contiguous physical - * address block together with the last one, print out the last - * contiguous mapped block details. + * or the current PTE breaks up a contiguous virtual or + * physical mapping, address block together with the last one, + * print out the last contiguous mapped block details. */ if ((*last_attr != attr) || - (*last_paddr + *last_size != paddr)) { + (*last_paddr + *last_size != paddr) || + (last_start + *last_size != start)) { print_pte(mon, va_bits, *vbase, *pbase, *last_paddr + *last_size - *pbase, *last_attr); @@ -125,6 +127,7 @@ static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, *last_attr = attr; } + last_start = start; *last_paddr = paddr; *last_size = pgsize; } else { diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 1a75ba11e6..09f1f5185d 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -24,8 +24,8 @@ #include "exec/helper-proto.h" /* Exceptions processing helpers */ -void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, - uint32_t exception, uintptr_t pc) +G_NORETURN void riscv_raise_exception(CPURISCVState *env, + uint32_t exception, uintptr_t pc) { CPUState *cs = env_cpu(env); cs->exception_index = exception; @@ -39,6 +39,15 @@ void helper_raise_exception(CPURISCVState *env, uint32_t exception) target_ulong helper_csrr(CPURISCVState *env, int csr) { + /* + * The seed CSR must be accessed with a read-write instruction. A + * read-only instruction such as CSRRS/CSRRC with rs1=x0 or CSRRSI/ + * CSRRCI with uimm=0 will raise an illegal instruction exception. + */ + if (csr == CSR_SEED) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + target_ulong val = 0; RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0); diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 81b61bb65c..151da3fa08 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -141,17 +141,9 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) 0111...1111 2^(XLEN+2)-byte NAPOT range 1111...1111 Reserved */ - if (a == -1) { - *sa = 0u; - *ea = -1; - return; - } else { - target_ulong t1 = ctz64(~a); - target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2; - target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; - *sa = base; - *ea = base + range; - } + a = (a << 2) | 0x3; + *sa = a & (a + 1); + *ea = a | (a + 1); } void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index a9a0b363a7..a8dd797476 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -22,6 +22,8 @@ #ifndef RISCV_PMP_H #define RISCV_PMP_H +#include "cpu.h" + typedef enum { PMP_READ = 1 << 0, PMP_WRITE = 1 << 1, @@ -37,9 +39,11 @@ typedef enum { } pmp_am_t; typedef enum { - MSECCFG_MML = 1 << 0, - MSECCFG_MMWP = 1 << 1, - MSECCFG_RLB = 1 << 2 + MSECCFG_MML = 1 << 0, + MSECCFG_MMWP = 1 << 1, + MSECCFG_RLB = 1 << 2, + MSECCFG_USEED = 1 << 8, + MSECCFG_SSEED = 1 << 9 } mseccfg_field_t; typedef struct { diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 84dbfa6340..0cd1d9ee94 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -101,6 +101,9 @@ typedef struct DisasContext { TCGv zero; /* Space for 3 operands plus 1 extra for address computation. */ TCGv temp[4]; + /* Space for 4 operands(1 dest and <=3 src) for float point computation */ + TCGv_i64 ftemp[4]; + uint8_t nftemp; /* PointerMasking extension */ bool pm_mask_enabled; bool pm_base_enabled; @@ -380,6 +383,138 @@ static void gen_set_gpr128(DisasContext *ctx, int reg_num, TCGv rl, TCGv rh) } } +static TCGv_i64 ftemp_new(DisasContext *ctx) +{ + assert(ctx->nftemp < ARRAY_SIZE(ctx->ftemp)); + return ctx->ftemp[ctx->nftemp++] = tcg_temp_new_i64(); +} + +static TCGv_i64 get_fpr_hs(DisasContext *ctx, int reg_num) +{ + if (!ctx->cfg_ptr->ext_zfinx) { + return cpu_fpr[reg_num]; + } + + if (reg_num == 0) { + return tcg_constant_i64(0); + } + switch (get_xl(ctx)) { + case MXL_RV32: +#ifdef TARGET_RISCV32 + { + TCGv_i64 t = ftemp_new(ctx); + tcg_gen_ext_i32_i64(t, cpu_gpr[reg_num]); + return t; + } +#else + /* fall through */ + case MXL_RV64: + return cpu_gpr[reg_num]; +#endif + default: + g_assert_not_reached(); + } +} + +static TCGv_i64 get_fpr_d(DisasContext *ctx, int reg_num) +{ + if (!ctx->cfg_ptr->ext_zfinx) { + return cpu_fpr[reg_num]; + } + + if (reg_num == 0) { + return tcg_constant_i64(0); + } + switch (get_xl(ctx)) { + case MXL_RV32: + { + TCGv_i64 t = ftemp_new(ctx); + tcg_gen_concat_tl_i64(t, cpu_gpr[reg_num], cpu_gpr[reg_num + 1]); + return t; + } +#ifdef TARGET_RISCV64 + case MXL_RV64: + return cpu_gpr[reg_num]; +#endif + default: + g_assert_not_reached(); + } +} + +static TCGv_i64 dest_fpr(DisasContext *ctx, int reg_num) +{ + if (!ctx->cfg_ptr->ext_zfinx) { + return cpu_fpr[reg_num]; + } + + if (reg_num == 0) { + return ftemp_new(ctx); + } + + switch (get_xl(ctx)) { + case MXL_RV32: + return ftemp_new(ctx); +#ifdef TARGET_RISCV64 + case MXL_RV64: + return cpu_gpr[reg_num]; +#endif + default: + g_assert_not_reached(); + } +} + +/* assume t is nanboxing (for normal) or sign-extended (for zfinx) */ +static void gen_set_fpr_hs(DisasContext *ctx, int reg_num, TCGv_i64 t) +{ + if (!ctx->cfg_ptr->ext_zfinx) { + tcg_gen_mov_i64(cpu_fpr[reg_num], t); + return; + } + if (reg_num != 0) { + switch (get_xl(ctx)) { + case MXL_RV32: +#ifdef TARGET_RISCV32 + tcg_gen_extrl_i64_i32(cpu_gpr[reg_num], t); + break; +#else + /* fall through */ + case MXL_RV64: + tcg_gen_mov_i64(cpu_gpr[reg_num], t); + break; +#endif + default: + g_assert_not_reached(); + } + } +} + +static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) +{ + if (!ctx->cfg_ptr->ext_zfinx) { + tcg_gen_mov_i64(cpu_fpr[reg_num], t); + return; + } + + if (reg_num != 0) { + switch (get_xl(ctx)) { + case MXL_RV32: +#ifdef TARGET_RISCV32 + tcg_gen_extr_i64_i32(cpu_gpr[reg_num], cpu_gpr[reg_num + 1], t); + break; +#else + tcg_gen_ext32s_i64(cpu_gpr[reg_num], t); + tcg_gen_sari_i64(cpu_gpr[reg_num + 1], t, 32); + break; + case MXL_RV64: + tcg_gen_mov_i64(cpu_gpr[reg_num], t); + break; +#endif + default: + g_assert_not_reached(); + } + } +} + static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { target_ulong next_pc; @@ -426,6 +561,10 @@ static void mark_fs_dirty(DisasContext *ctx) { TCGv tmp; + if (!has_ext(ctx, RVF)) { + return; + } + if (ctx->mstatus_fs != MSTATUS_FS) { /* Remember the state change for the rest of the TB. */ ctx->mstatus_fs = MSTATUS_FS; @@ -549,6 +688,13 @@ EX_SH(12) } \ } while (0) +#define REQUIRE_EITHER_EXT(ctx, A, B) do { \ + if (!ctx->cfg_ptr->ext_##A && \ + !ctx->cfg_ptr->ext_##B) { \ + return false; \ + } \ +} while (0) + static int ex_rvc_register(DisasContext *ctx, int reg) { return 8 + reg; @@ -861,6 +1007,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvv.c.inc" #include "insn_trans/trans_rvb.c.inc" #include "insn_trans/trans_rvzfh.c.inc" +#include "insn_trans/trans_rvk.c.inc" #include "insn_trans/trans_privileged.c.inc" #include "insn_trans/trans_svinval.c.inc" #include "insn_trans/trans_xventanacondops.c.inc" @@ -951,6 +1098,8 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cs = cs; ctx->ntemp = 0; memset(ctx->temp, 0, sizeof(ctx->temp)); + ctx->nftemp = 0; + memset(ctx->ftemp, 0, sizeof(ctx->ftemp)); ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED); ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED); ctx->zero = tcg_constant_tl(0); @@ -972,16 +1121,22 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cpu->env_ptr; uint16_t opcode16 = translator_lduw(env, &ctx->base, ctx->base.pc_next); + int i; ctx->ol = ctx->xl; decode_opc(env, ctx, opcode16); ctx->base.pc_next = ctx->pc_succ_insn; - for (int i = ctx->ntemp - 1; i >= 0; --i) { + for (i = ctx->ntemp - 1; i >= 0; --i) { tcg_temp_free(ctx->temp[i]); ctx->temp[i] = NULL; } ctx->ntemp = 0; + for (i = ctx->nftemp - 1; i >= 0; --i) { + tcg_temp_free_i64(ctx->ftemp[i]); + ctx->ftemp[i] = NULL; + } + ctx->nftemp = 0; if (ctx->base.is_jmp == DISAS_NEXT) { target_ulong page_start; @@ -1008,18 +1163,20 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void riscv_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void riscv_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { #ifndef CONFIG_USER_ONLY RISCVCPU *rvcpu = RISCV_CPU(cpu); CPURISCVState *env = &rvcpu->env; #endif - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); #ifndef CONFIG_USER_ONLY - qemu_log("Priv: "TARGET_FMT_ld"; Virt: "TARGET_FMT_ld"\n", env->priv, env->virt); + fprintf(logfile, "Priv: "TARGET_FMT_ld"; Virt: "TARGET_FMT_ld"\n", + env->priv, env->virt); #endif - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps riscv_tr_ops = { diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 3bd4aac9c9..576b14e5a3 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -79,7 +79,7 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, * Note that vector data is stored in host-endian 64-bit chunks, * so addressing units smaller than that needs a host-endian fixup. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define H1(x) ((x) ^ 7) #define H1_2(x) ((x) ^ 6) #define H1_4(x) ((x) ^ 4) @@ -4888,25 +4888,20 @@ GEN_VEXT_VCOMPRESS_VM(vcompress_vm_w, uint32_t, H4) GEN_VEXT_VCOMPRESS_VM(vcompress_vm_d, uint64_t, H8) /* Vector Whole Register Move */ -#define GEN_VEXT_VMV_WHOLE(NAME, LEN) \ -void HELPER(NAME)(void *vd, void *vs2, CPURISCVState *env, \ - uint32_t desc) \ -{ \ - /* EEW = 8 */ \ - uint32_t maxsz = simd_maxsz(desc); \ - uint32_t i = env->vstart; \ - \ - memcpy((uint8_t *)vd + H1(i), \ - (uint8_t *)vs2 + H1(i), \ - maxsz - env->vstart); \ - \ - env->vstart = 0; \ -} +void HELPER(vmvr_v)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) +{ + /* EEW = SEW */ + uint32_t maxsz = simd_maxsz(desc); + uint32_t sewb = 1 << FIELD_EX64(env->vtype, VTYPE, VSEW); + uint32_t startb = env->vstart * sewb; + uint32_t i = startb; -GEN_VEXT_VMV_WHOLE(vmv1r_v, 1) -GEN_VEXT_VMV_WHOLE(vmv2r_v, 2) -GEN_VEXT_VMV_WHOLE(vmv4r_v, 4) -GEN_VEXT_VMV_WHOLE(vmv8r_v, 8) + memcpy((uint8_t *)vd + H1(i), + (uint8_t *)vs2 + H1(i), + maxsz - startb); + + env->vstart = 0; +} /* Vector Integer Extension */ #define GEN_VEXT_INT_EXT(NAME, ETYPE, DTYPE, HD, HS1) \ diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h index 7310558e0c..4533759d96 100644 --- a/target/rx/cpu-qom.h +++ b/target/rx/cpu-qom.h @@ -26,8 +26,7 @@ #define TYPE_RX62N_CPU RX_CPU_TYPE_NAME("rx62n") -OBJECT_DECLARE_TYPE(RXCPU, RXCPUClass, - RX_CPU) +OBJECT_DECLARE_CPU_TYPE(RXCPU, RXCPUClass, RX_CPU) /* * RXCPUClass: @@ -45,6 +44,4 @@ struct RXCPUClass { DeviceReset parent_reset; }; -#define CPUArchState struct CPURXState - #endif diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 25a4aa2976..fb30080ac4 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -20,7 +20,6 @@ #include "qemu/qemu-print.h" #include "qapi/error.h" #include "cpu.h" -#include "qemu-common.h" #include "migration/vmstate.h" #include "exec/exec-all.h" #include "hw/loader.h" diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 58adf9edf6..5655dffeff 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -24,6 +24,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" /* PSW define */ REG32(PSW, 0) @@ -65,7 +66,7 @@ enum { NUM_REGS = 16, }; -typedef struct CPURXState { +typedef struct CPUArchState { /* CPU registers */ uint32_t regs[NUM_REGS]; /* general registers */ uint32_t psw_o; /* O bit of status register */ @@ -105,7 +106,7 @@ typedef struct CPURXState { * * A RX CPU */ -struct RXCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -114,8 +115,6 @@ struct RXCPU { CPURXState env; }; -typedef RXCPU ArchCPU; - #define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU #define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_RX_CPU @@ -150,6 +149,7 @@ static inline void cpu_get_tb_cpu_state(CPURXState *env, target_ulong *pc, *pc = env->pc; *cs_base = 0; *flags = FIELD_DP32(0, PSW, PM, env->psw_pm); + *flags = FIELD_DP32(*flags, PSW, U, env->psw_u); } static inline int cpu_mmu_index(CPURXState *env, bool ifetch) diff --git a/target/rx/gdbstub.c b/target/rx/gdbstub.c index c811d4810b..7eb2059a84 100644 --- a/target/rx/gdbstub.c +++ b/target/rx/gdbstub.c @@ -16,7 +16,6 @@ * this program. If not, see . */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "exec/gdbstub.h" diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index 11f952d340..9ca32dcc82 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -24,8 +24,9 @@ #include "exec/cpu_ldst.h" #include "fpu/softfloat.h" -static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index, - uintptr_t retaddr); +static inline G_NORETURN +void raise_exception(CPURXState *env, int index, + uintptr_t retaddr); static void _set_psw(CPURXState *env, uint32_t psw, uint32_t rte) { @@ -418,8 +419,9 @@ uint32_t helper_divu(CPURXState *env, uint32_t num, uint32_t den) } /* exception */ -static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index, - uintptr_t retaddr) +static inline G_NORETURN +void raise_exception(CPURXState *env, int index, + uintptr_t retaddr) { CPUState *cs = env_cpu(env); @@ -427,36 +429,37 @@ static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index, cpu_loop_exit_restore(cs, retaddr); } -void QEMU_NORETURN helper_raise_privilege_violation(CPURXState *env) +G_NORETURN void helper_raise_privilege_violation(CPURXState *env) { raise_exception(env, 20, GETPC()); } -void QEMU_NORETURN helper_raise_access_fault(CPURXState *env) +G_NORETURN void helper_raise_access_fault(CPURXState *env) { raise_exception(env, 21, GETPC()); } -void QEMU_NORETURN helper_raise_illegal_instruction(CPURXState *env) +G_NORETURN void helper_raise_illegal_instruction(CPURXState *env) { raise_exception(env, 23, GETPC()); } -void QEMU_NORETURN helper_wait(CPURXState *env) +G_NORETURN void helper_wait(CPURXState *env) { CPUState *cs = env_cpu(env); cs->halted = 1; env->in_sleep = 1; + env->psw_i = 1; raise_exception(env, EXCP_HLT, 0); } -void QEMU_NORETURN helper_rxint(CPURXState *env, uint32_t vec) +G_NORETURN void helper_rxint(CPURXState *env, uint32_t vec) { raise_exception(env, 0x100 + vec, 0); } -void QEMU_NORETURN helper_rxbrk(CPURXState *env) +G_NORETURN void helper_rxbrk(CPURXState *env) { raise_exception(env, 0x100, 0); } diff --git a/target/rx/translate.c b/target/rx/translate.c index 5db8f79a82..62aee66937 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -32,6 +32,7 @@ typedef struct DisasContext { DisasContextBase base; CPURXState *env; uint32_t pc; + uint32_t tb_flags; } DisasContext; typedef struct DisasCompare { @@ -231,7 +232,7 @@ static inline TCGv rx_load_source(DisasContext *ctx, TCGv mem, /* Processor mode check */ static int is_privileged(DisasContext *ctx, int is_exception) { - if (FIELD_EX32(ctx->base.tb->flags, PSW, PM)) { + if (FIELD_EX32(ctx->tb_flags, PSW, PM)) { if (is_exception) { gen_helper_raise_privilege_violation(cpu_env); } @@ -310,9 +311,8 @@ static void psw_cond(DisasCompare *dc, uint32_t cond) } } -static void move_from_cr(TCGv ret, int cr, uint32_t pc) +static void move_from_cr(DisasContext *ctx, TCGv ret, int cr, uint32_t pc) { - TCGv z = tcg_const_i32(0); switch (cr) { case 0: /* PSW */ gen_helper_pack_psw(ret, cpu_env); @@ -321,8 +321,11 @@ static void move_from_cr(TCGv ret, int cr, uint32_t pc) tcg_gen_movi_i32(ret, pc); break; case 2: /* USP */ - tcg_gen_movcond_i32(TCG_COND_NE, ret, - cpu_psw_u, z, cpu_sp, cpu_usp); + if (FIELD_EX32(ctx->tb_flags, PSW, U)) { + tcg_gen_mov_i32(ret, cpu_sp); + } else { + tcg_gen_mov_i32(ret, cpu_usp); + } break; case 3: /* FPSW */ tcg_gen_mov_i32(ret, cpu_fpsw); @@ -334,8 +337,11 @@ static void move_from_cr(TCGv ret, int cr, uint32_t pc) tcg_gen_mov_i32(ret, cpu_bpc); break; case 10: /* ISP */ - tcg_gen_movcond_i32(TCG_COND_EQ, ret, - cpu_psw_u, z, cpu_sp, cpu_isp); + if (FIELD_EX32(ctx->tb_flags, PSW, U)) { + tcg_gen_mov_i32(ret, cpu_isp); + } else { + tcg_gen_mov_i32(ret, cpu_sp); + } break; case 11: /* FINTV */ tcg_gen_mov_i32(ret, cpu_fintv); @@ -349,28 +355,31 @@ static void move_from_cr(TCGv ret, int cr, uint32_t pc) tcg_gen_movi_i32(ret, 0); break; } - tcg_temp_free(z); } static void move_to_cr(DisasContext *ctx, TCGv val, int cr) { - TCGv z; if (cr >= 8 && !is_privileged(ctx, 0)) { /* Some control registers can only be written in privileged mode. */ qemu_log_mask(LOG_GUEST_ERROR, "disallow control register write %s", rx_crname(cr)); return; } - z = tcg_const_i32(0); switch (cr) { case 0: /* PSW */ gen_helper_set_psw(cpu_env, val); + if (is_privileged(ctx, 0)) { + /* PSW.{I,U} may be updated here. exit TB. */ + ctx->base.is_jmp = DISAS_UPDATE; + } break; /* case 1: to PC not supported */ case 2: /* USP */ - tcg_gen_mov_i32(cpu_usp, val); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_sp, - cpu_psw_u, z, cpu_usp, cpu_sp); + if (FIELD_EX32(ctx->tb_flags, PSW, U)) { + tcg_gen_mov_i32(cpu_sp, val); + } else { + tcg_gen_mov_i32(cpu_usp, val); + } break; case 3: /* FPSW */ gen_helper_set_fpsw(cpu_env, val); @@ -382,10 +391,11 @@ static void move_to_cr(DisasContext *ctx, TCGv val, int cr) tcg_gen_mov_i32(cpu_bpc, val); break; case 10: /* ISP */ - tcg_gen_mov_i32(cpu_isp, val); - /* if PSW.U is 0, copy isp to r0 */ - tcg_gen_movcond_i32(TCG_COND_EQ, cpu_sp, - cpu_psw_u, z, cpu_isp, cpu_sp); + if (FIELD_EX32(ctx->tb_flags, PSW, U)) { + tcg_gen_mov_i32(cpu_isp, val); + } else { + tcg_gen_mov_i32(cpu_sp, val); + } break; case 11: /* FINTV */ tcg_gen_mov_i32(cpu_fintv, val); @@ -398,7 +408,6 @@ static void move_to_cr(DisasContext *ctx, TCGv val, int cr) "Unimplement control register %d", cr); break; } - tcg_temp_free(z); } static void push(TCGv val) @@ -626,10 +635,6 @@ static bool trans_POPC(DisasContext *ctx, arg_POPC *a) val = tcg_temp_new(); pop(val); move_to_cr(ctx, val, a->cr); - if (a->cr == 0 && is_privileged(ctx, 0)) { - /* PSW.I may be updated here. exit TB. */ - ctx->base.is_jmp = DISAS_UPDATE; - } tcg_temp_free(val); return true; } @@ -682,7 +687,7 @@ static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) { TCGv val; val = tcg_temp_new(); - move_from_cr(val, a->cr, ctx->pc); + move_from_cr(ctx, val, a->cr, ctx->pc); push(val); tcg_temp_free(val); return true; @@ -2160,7 +2165,12 @@ static inline void clrsetpsw(DisasContext *ctx, int cb, int val) ctx->base.is_jmp = DISAS_UPDATE; break; case PSW_U: - tcg_gen_movi_i32(cpu_psw_u, val); + if (FIELD_EX32(ctx->tb_flags, PSW, U) != val) { + ctx->tb_flags = FIELD_DP32(ctx->tb_flags, PSW, U, val); + tcg_gen_movi_i32(cpu_psw_u, val); + tcg_gen_mov_i32(val ? cpu_isp : cpu_usp, cpu_sp); + tcg_gen_mov_i32(cpu_sp, val ? cpu_usp : cpu_isp); + } break; default: qemu_log_mask(LOG_GUEST_ERROR, "Invalid distination %d", cb); @@ -2200,9 +2210,6 @@ static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) imm = tcg_const_i32(a->imm); move_to_cr(ctx, imm, a->cr); - if (a->cr == 0 && is_privileged(ctx, 0)) { - ctx->base.is_jmp = DISAS_UPDATE; - } tcg_temp_free(imm); return true; } @@ -2211,16 +2218,13 @@ static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) static bool trans_MVTC_r(DisasContext *ctx, arg_MVTC_r *a) { move_to_cr(ctx, cpu_regs[a->rs], a->cr); - if (a->cr == 0 && is_privileged(ctx, 0)) { - ctx->base.is_jmp = DISAS_UPDATE; - } return true; } /* mvfc rs, rd */ static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a) { - move_from_cr(cpu_regs[a->rd], a->cr, ctx->pc); + move_from_cr(ctx, cpu_regs[a->rd], a->cr, ctx->pc); return true; } @@ -2281,7 +2285,7 @@ static bool trans_INT(DisasContext *ctx, arg_INT *a) static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a) { if (is_privileged(ctx, 1)) { - tcg_gen_addi_i32(cpu_pc, cpu_pc, 2); + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); gen_helper_wait(cpu_env); } return true; @@ -2292,6 +2296,7 @@ static void rx_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) CPURXState *env = cs->env_ptr; DisasContext *ctx = container_of(dcbase, DisasContext, base); ctx->env = env; + ctx->tb_flags = ctx->base.tb->flags; } static void rx_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) @@ -2342,10 +2347,11 @@ static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void rx_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void rx_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { - qemu_log("IN:\n"); /* , lookup_symbol(dcbase->pc_first)); */ - log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps rx_tr_ops = { diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index 9f3a0d86c5..00cae2b131 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -25,12 +25,13 @@ #define TYPE_S390_CPU "s390x-cpu" -OBJECT_DECLARE_TYPE(S390CPU, S390CPUClass, - S390_CPU) +OBJECT_DECLARE_CPU_TYPE(S390CPU, S390CPUClass, S390_CPU) typedef struct S390CPUModel S390CPUModel; typedef struct S390CPUDef S390CPUDef; +typedef struct CPUArchState CPUS390XState; + typedef enum cpu_reset_type { S390_CPU_RESET_NORMAL, S390_CPU_RESET_INITIAL, @@ -63,6 +64,4 @@ struct S390CPUClass { void (*reset)(CPUState *cpu, cpu_reset_type type); }; -typedef struct CPUS390XState CPUS390XState; - #endif diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c index 5471e01ee8..948e4bd3e0 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-sysemu.c @@ -76,7 +76,7 @@ static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs) S390CPU *cpu = S390_CPU(cs); cpu_synchronize_state(cs); - panic_info = g_malloc0(sizeof(GuestPanicInformation)); + panic_info = g_new0(GuestPanicInformation, 1); panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390; panic_info->u.s390.core = cpu->env.core_id; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index ccdbaf84d5..c31bb2351f 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -178,7 +178,6 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type) static void s390_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_s390_64; - info->print_insn = print_insn_s390; info->cap_arch = CS_ARCH_SYSZ; info->cap_insn_unit = 2; info->cap_insn_split = 6; diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index a75e559134..7d6d01325b 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -28,6 +28,7 @@ #include "cpu-qom.h" #include "cpu_models.h" #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" #define ELF_MACHINE_UNAME "S390X" @@ -51,7 +52,7 @@ typedef struct PSW { uint64_t addr; } PSW; -struct CPUS390XState { +struct CPUArchState { uint64_t regs[16]; /* GP registers */ /* * The floating point registers are part of the vector registers. @@ -163,7 +164,7 @@ static inline uint64_t *get_freg(CPUS390XState *cs, int nr) * * An S/390 CPU. */ -struct S390CPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -840,9 +841,6 @@ uint64_t s390_cpu_get_psw_mask(CPUS390XState *env); /* outside of target/s390x/ */ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); -typedef CPUS390XState CPUArchState; -typedef S390CPU ArchCPU; - #include "exec/cpu-all.h" #endif diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 17ae771939..1a562d2801 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -86,10 +86,9 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x3932, 16, 1, 47, 0x08000000U, "gen16b", "IBM 3932 GA1"), }; -#define QEMU_MAX_CPU_TYPE 0x3906 -#define QEMU_MAX_CPU_GEN 14 -#define QEMU_MAX_CPU_EC_GA 2 -static const S390FeatInit qemu_max_cpu_feat_init = { S390_FEAT_LIST_QEMU_MAX }; +#define QEMU_MAX_CPU_TYPE 0x8561 +#define QEMU_MAX_CPU_GEN 15 +#define QEMU_MAX_CPU_EC_GA 1 static S390FeatBitmap qemu_max_cpu_feat; /* features part of a base model but not relevant for finding a base model */ @@ -728,7 +727,6 @@ static void s390_cpu_model_initfn(Object *obj) } } -static S390CPUDef s390_qemu_cpu_def; static S390CPUModel s390_qemu_cpu_model; /* Set the qemu CPU model (on machine initialization). Must not be called @@ -742,17 +740,8 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, g_assert(def); g_assert(QTAILQ_EMPTY_RCU(&cpus)); - /* TCG emulates some features that can usually not be enabled with - * the emulated machine generation. Make sure they can be enabled - * when using the QEMU model by adding them to full_feat. We have - * to copy the definition to do that. - */ - memcpy(&s390_qemu_cpu_def, def, sizeof(s390_qemu_cpu_def)); - bitmap_or(s390_qemu_cpu_def.full_feat, s390_qemu_cpu_def.full_feat, - qemu_max_cpu_feat, S390_FEAT_MAX); - /* build the CPU model */ - s390_qemu_cpu_model.def = &s390_qemu_cpu_def; + s390_qemu_cpu_model.def = def; bitmap_zero(s390_qemu_cpu_model.features, S390_FEAT_MAX); s390_init_feat_bitmap(feat_init, s390_qemu_cpu_model.features); } @@ -885,9 +874,8 @@ static void s390_max_cpu_model_class_init(ObjectClass *oc, void *data) /* * The "max" model is neither static nor migration safe. Under KVM - * it represents the "host" model. Under TCG it represents some kind of - * "qemu" CPU model without compat handling and maybe with some additional - * CPU features that are not yet unlocked in the "qemu" model. + * it represents the "host" model. Under TCG it represents the "qemu" CPU + * model of the latest QEMU machine. */ xcc->desc = "Enables all features supported by the accelerator in the current host"; @@ -966,13 +954,13 @@ static void init_ignored_base_feat(void) static void register_types(void) { - static const S390FeatInit qemu_latest_init = { S390_FEAT_LIST_QEMU_LATEST }; + static const S390FeatInit qemu_max_init = { S390_FEAT_LIST_QEMU_MAX }; int i; init_ignored_base_feat(); /* init all bitmaps from gnerated data initially */ - s390_init_feat_bitmap(qemu_max_cpu_feat_init, qemu_max_cpu_feat); + s390_init_feat_bitmap(qemu_max_init, qemu_max_cpu_feat); for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) { s390_init_feat_bitmap(s390_cpu_defs[i].base_init, s390_cpu_defs[i].base_feat); @@ -982,9 +970,9 @@ static void register_types(void) s390_cpu_defs[i].full_feat); } - /* initialize the qemu model with latest definition */ + /* initialize the qemu model with the maximum definition ("max" model) */ s390_set_qemu_cpu_model(QEMU_MAX_CPU_TYPE, QEMU_MAX_CPU_GEN, - QEMU_MAX_CPU_EC_GA, qemu_latest_init); + QEMU_MAX_CPU_EC_GA, qemu_max_init); for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) { char *base_name = s390_base_cpu_type_name(s390_cpu_defs[i].name); diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index 05c3ccaaff..d8a141a023 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_sysemu.c @@ -15,7 +15,6 @@ #include "s390x-internal.h" #include "kvm/kvm_s390x.h" #include "sysemu/kvm.h" -#include "sysemu/tcg.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "qapi/qmp/qerror.h" diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 7cb1a6ec10..c03ec2c9a9 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -731,17 +731,24 @@ static uint16_t qemu_V6_0[] = { S390_FEAT_ESOP, }; -static uint16_t qemu_LATEST[] = { +static uint16_t qemu_V6_2[] = { S390_FEAT_INSTRUCTION_EXEC_PROT, S390_FEAT_MISC_INSTRUCTION_EXT2, S390_FEAT_MSA_EXT_8, S390_FEAT_VECTOR_ENH, }; -/* add all new definitions before this point */ +static uint16_t qemu_V7_0[] = { + S390_FEAT_MISC_INSTRUCTION_EXT3, +}; + +/* + * Features for the "qemu" CPU model of the latest QEMU machine and the "max" + * CPU model under TCG. Don't include features that are not part of the full + * feature set of the current "max" CPU model generation. + */ static uint16_t qemu_MAX[] = { - /* generates a dependency warning, leave it out for now */ - S390_FEAT_MSA_EXT_5, + S390_FEAT_VECTOR_ENH2, }; /****** END FEATURE DEFS ******/ @@ -862,7 +869,8 @@ static FeatGroupDefSpec QemuFeatDef[] = { QEMU_FEAT_INITIALIZER(V4_0), QEMU_FEAT_INITIALIZER(V4_1), QEMU_FEAT_INITIALIZER(V6_0), - QEMU_FEAT_INITIALIZER(LATEST), + QEMU_FEAT_INITIALIZER(V6_2), + QEMU_FEAT_INITIALIZER(V7_0), QEMU_FEAT_INITIALIZER(MAX), }; diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 6e35473c7f..473c8e51b0 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -27,7 +27,6 @@ #include "hw/s390x/pv.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" -#include "sysemu/tcg.h" void s390x_tod_timer(void *opaque) { diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 271b081e8c..bf33d86f74 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -4,6 +4,7 @@ DEF_HELPER_FLAGS_4(nc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_FLAGS_4(oc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_FLAGS_4(xc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_FLAGS_4(mvc, TCG_CALL_NO_WG, void, env, i32, i64, i64) +DEF_HELPER_FLAGS_4(mvcrl, TCG_CALL_NO_WG, void, env, i64, i64, i64) DEF_HELPER_FLAGS_4(mvcin, TCG_CALL_NO_WG, void, env, i32, i64, i64) DEF_HELPER_FLAGS_4(clc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_3(mvcl, i32, env, i32, i32) @@ -202,8 +203,11 @@ DEF_HELPER_FLAGS_3(gvec_vpopct16, TCG_CALL_NO_RWG, void, ptr, cptr, i32) DEF_HELPER_FLAGS_4(gvec_verim8, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) DEF_HELPER_FLAGS_4(gvec_verim16, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) DEF_HELPER_FLAGS_4(gvec_vsl, TCG_CALL_NO_RWG, void, ptr, cptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_vsl_ve2, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) DEF_HELPER_FLAGS_4(gvec_vsra, TCG_CALL_NO_RWG, void, ptr, cptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_vsra_ve2, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) DEF_HELPER_FLAGS_4(gvec_vsrl, TCG_CALL_NO_RWG, void, ptr, cptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_vsrl_ve2, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) DEF_HELPER_FLAGS_4(gvec_vscbi8, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) DEF_HELPER_FLAGS_4(gvec_vscbi16, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) DEF_HELPER_4(gvec_vtm, void, ptr, cptr, env, i32) @@ -245,6 +249,12 @@ DEF_HELPER_6(gvec_vstrc_cc32, void, ptr, cptr, cptr, cptr, env, i32) DEF_HELPER_6(gvec_vstrc_cc_rt8, void, ptr, cptr, cptr, cptr, env, i32) DEF_HELPER_6(gvec_vstrc_cc_rt16, void, ptr, cptr, cptr, cptr, env, i32) DEF_HELPER_6(gvec_vstrc_cc_rt32, void, ptr, cptr, cptr, cptr, env, i32) +DEF_HELPER_6(gvec_vstrs_8, void, ptr, cptr, cptr, cptr, env, i32) +DEF_HELPER_6(gvec_vstrs_16, void, ptr, cptr, cptr, cptr, env, i32) +DEF_HELPER_6(gvec_vstrs_32, void, ptr, cptr, cptr, cptr, env, i32) +DEF_HELPER_6(gvec_vstrs_zs8, void, ptr, cptr, cptr, cptr, env, i32) +DEF_HELPER_6(gvec_vstrs_zs16, void, ptr, cptr, cptr, cptr, env, i32) +DEF_HELPER_6(gvec_vstrs_zs32, void, ptr, cptr, cptr, cptr, env, i32) /* === Vector Floating-Point Instructions */ DEF_HELPER_FLAGS_5(gvec_vfa32, TCG_CALL_NO_WG, void, ptr, cptr, cptr, env, i32) @@ -274,6 +284,10 @@ DEF_HELPER_FLAGS_5(gvec_vfche64, TCG_CALL_NO_WG, void, ptr, cptr, cptr, env, i32 DEF_HELPER_5(gvec_vfche64_cc, void, ptr, cptr, cptr, env, i32) DEF_HELPER_FLAGS_5(gvec_vfche128, TCG_CALL_NO_WG, void, ptr, cptr, cptr, env, i32) DEF_HELPER_5(gvec_vfche128_cc, void, ptr, cptr, cptr, env, i32) +DEF_HELPER_FLAGS_4(gvec_vcdg32, TCG_CALL_NO_WG, void, ptr, cptr, env, i32) +DEF_HELPER_FLAGS_4(gvec_vcdlg32, TCG_CALL_NO_WG, void, ptr, cptr, env, i32) +DEF_HELPER_FLAGS_4(gvec_vcgd32, TCG_CALL_NO_WG, void, ptr, cptr, env, i32) +DEF_HELPER_FLAGS_4(gvec_vclgd32, TCG_CALL_NO_WG, void, ptr, cptr, env, i32) DEF_HELPER_FLAGS_4(gvec_vcdg64, TCG_CALL_NO_WG, void, ptr, cptr, env, i32) DEF_HELPER_FLAGS_4(gvec_vcdlg64, TCG_CALL_NO_WG, void, ptr, cptr, env, i32) DEF_HELPER_FLAGS_4(gvec_vcgd64, TCG_CALL_NO_WG, void, ptr, cptr, env, i32) diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 6acf14d5ec..53098bf541 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -24,7 +24,6 @@ #include #include -#include "qemu-common.h" #include "cpu.h" #include "s390x-internal.h" #include "kvm_s390x.h" diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 6fc8cad2d5..6aba7fd0ca 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -280,9 +280,9 @@ void s390_cpu_record_sigbus(CPUState *cs, vaddr address, bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); #endif diff --git a/target/s390x/tcg/cc_helper.c b/target/s390x/tcg/cc_helper.c index 8d04097f78..b2e8d3d9f5 100644 --- a/target/s390x/tcg/cc_helper.c +++ b/target/s390x/tcg/cc_helper.c @@ -136,7 +136,7 @@ static uint32_t cc_calc_subu(uint64_t borrow_out, uint64_t result) static uint32_t cc_calc_add_64(int64_t a1, int64_t a2, int64_t ar) { - if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) { + if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar >= 0)) { return 3; /* overflow */ } else { if (ar < 0) { @@ -151,7 +151,7 @@ static uint32_t cc_calc_add_64(int64_t a1, int64_t a2, int64_t ar) static uint32_t cc_calc_sub_64(int64_t a1, int64_t a2, int64_t ar) { - if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { + if ((a1 >= 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { return 3; /* overflow */ } else { if (ar < 0) { @@ -196,7 +196,7 @@ static uint32_t cc_calc_comp_64(int64_t dst) static uint32_t cc_calc_add_32(int32_t a1, int32_t a2, int32_t ar) { - if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) { + if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar >= 0)) { return 3; /* overflow */ } else { if (ar < 0) { @@ -211,7 +211,7 @@ static uint32_t cc_calc_add_32(int32_t a1, int32_t a2, int32_t ar) static uint32_t cc_calc_sub_32(int32_t a1, int32_t a2, int32_t ar) { - if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { + if ((a1 >= 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { return 3; /* overflow */ } else { if (ar < 0) { diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index be6c966cfa..29ccf70df1 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -34,8 +34,8 @@ #include "hw/boards.h" #endif -void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, - uint32_t code, uintptr_t ra) +G_NORETURN void tcg_s390_program_interrupt(CPUS390XState *env, + uint32_t code, uintptr_t ra) { CPUState *cs = env_cpu(env); @@ -46,8 +46,8 @@ void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, cpu_loop_exit(cs); } -void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, - uintptr_t ra) +G_NORETURN void tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, + uintptr_t ra) { g_assert(dxc <= 0xff); #if !defined(CONFIG_USER_ONLY) @@ -63,8 +63,8 @@ void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, tcg_s390_program_interrupt(env, PGM_DATA, ra); } -void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc, - uintptr_t ra) +G_NORETURN void tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc, + uintptr_t ra) { g_assert(vxc <= 0xff); #if !defined(CONFIG_USER_ONLY) @@ -88,7 +88,8 @@ void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc) * this is only for the atomic operations, for which we want to raise a * specification exception. */ -static void QEMU_NORETURN do_unaligned_access(CPUState *cs, uintptr_t retaddr) +static G_NORETURN +void do_unaligned_access(CPUState *cs, uintptr_t retaddr) { S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; @@ -620,9 +621,10 @@ void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, do_unaligned_access(cs, retaddr); } -static void QEMU_NORETURN monitor_event(CPUS390XState *env, - uint64_t monitor_code, - uint8_t monitor_class, uintptr_t ra) +static G_NORETURN +void monitor_event(CPUS390XState *env, + uint64_t monitor_code, + uint8_t monitor_class, uintptr_t ra) { /* Store the Monitor Code and the Monitor Class Number into the lowcore */ stq_phys(env_cpu(env)->as, diff --git a/target/s390x/tcg/insn-data.def b/target/s390x/tcg/insn-data.def index 1c3e115712..5e448bb2c4 100644 --- a/target/s390x/tcg/insn-data.def +++ b/target/s390x/tcg/insn-data.def @@ -105,6 +105,9 @@ D(0xa507, NILL, RI_a, Z, r1_o, i2_16u, r1, 0, andi, 0, 0x1000) D(0x9400, NI, SI, Z, la1, i2_8u, new, 0, ni, nz64, MO_UB) D(0xeb54, NIY, SIY, LD, la1, i2_8u, new, 0, ni, nz64, MO_UB) +/* AND WITH COMPLEMENT */ + C(0xb9f5, NCRK, RRF_a, MIE3, r2, r3, new, r1_32, andc, nz32) + C(0xb9e5, NCGRK, RRF_a, MIE3, r2, r3, r1, 0, andc, nz64) /* BRANCH AND LINK */ C(0x0500, BALR, RR_a, Z, 0, r2_nz, r1, 0, bal, 0) @@ -640,6 +643,8 @@ C(0xeb8e, MVCLU, RSY_a, E2, 0, a2, 0, 0, mvclu, 0) /* MOVE NUMERICS */ C(0xd100, MVN, SS_a, Z, la1, a2, 0, 0, mvn, 0) +/* MOVE RIGHT TO LEFT */ + C(0xe50a, MVCRL, SSE, MIE3, la1, a2, 0, 0, mvcrl, 0) /* MOVE PAGE */ C(0xb254, MVPG, RRE, Z, 0, 0, 0, 0, mvpg, 0) /* MOVE STRING */ @@ -707,6 +712,16 @@ F(0xed0f, MSEB, RXF, Z, e1, m2_32u, new, e1, mseb, 0, IF_BFP) F(0xed1f, MSDB, RXF, Z, f1, m2_64, new, f1, msdb, 0, IF_BFP) +/* NAND */ + C(0xb974, NNRK, RRF_a, MIE3, r2, r3, new, r1_32, nand, nz32) + C(0xb964, NNGRK, RRF_a, MIE3, r2, r3, r1, 0, nand, nz64) +/* NOR */ + C(0xb976, NORK, RRF_a, MIE3, r2, r3, new, r1_32, nor, nz32) + C(0xb966, NOGRK, RRF_a, MIE3, r2, r3, r1, 0, nor, nz64) +/* NOT EXCLUSIVE OR */ + C(0xb977, NXRK, RRF_a, MIE3, r2, r3, new, r1_32, nxor, nz32) + C(0xb967, NXGRK, RRF_a, MIE3, r2, r3, r1, 0, nxor, nz64) + /* OR */ C(0x1600, OR, RR_a, Z, r1, r2, new, r1_32, or, nz32) C(0xb9f6, ORK, RRF_a, DO, r2, r3, new, r1_32, or, nz32) @@ -725,6 +740,9 @@ D(0xa50b, OILL, RI_a, Z, r1_o, i2_16u, r1, 0, ori, 0, 0x1000) D(0x9600, OI, SI, Z, la1, i2_8u, new, 0, oi, nz64, MO_UB) D(0xeb56, OIY, SIY, LD, la1, i2_8u, new, 0, oi, nz64, MO_UB) +/* OR WITH COMPLEMENT */ + C(0xb975, OCRK, RRF_a, MIE3, r2, r3, new, r1_32, orc, nz32) + C(0xb965, OCGRK, RRF_a, MIE3, r2, r3, r1, 0, orc, nz64) /* PACK */ /* Really format SS_b, but we pack both lengths into one argument @@ -735,6 +753,9 @@ /* PACK UNICODE */ C(0xe100, PKU, SS_f, E2, la1, a2, 0, 0, pku, 0) +/* POPULATION COUNT */ + C(0xb9e1, POPCNT, RRF_c, PC, 0, r2_o, r1, 0, popcnt, nz64) + /* PREFETCH */ /* Implemented as nops of course. */ C(0xe336, PFD, RXY_b, GIE, 0, 0, 0, 0, 0, 0) @@ -743,9 +764,6 @@ /* Implemented as nop of course. */ C(0xb2e8, PPA, RRF_c, PPA, 0, 0, 0, 0, 0, 0) -/* POPULATION COUNT */ - C(0xb9e1, POPCNT, RRE, PC, 0, r2_o, r1, 0, popcnt, nz64) - /* ROTATE LEFT SINGLE LOGICAL */ C(0xeb1d, RLL, RSY_a, Z, r3_o, sh, new, r1_32, rll32, 0) C(0xeb1c, RLLG, RSY_a, Z, r3_o, sh, r1, 0, rll64, 0) @@ -765,6 +783,12 @@ /* SEARCH STRING UNICODE */ C(0xb9be, SRSTU, RRE, ETF3, 0, 0, 0, 0, srstu, 0) +/* SELECT */ + C(0xb9f0, SELR, RRF_a, MIE3, r3, r2, new, r1_32, loc, 0) + C(0xb9e3, SELGR, RRF_a, MIE3, r3, r2, r1, 0, loc, 0) +/* SELECT HIGH */ + C(0xb9c0, SELFHR, RRF_a, MIE3, r3_sr32, r2_sr32, new, r1_32h, loc, 0) + /* SET ACCESS */ C(0xb24e, SAR, RRE, Z, 0, r2_o, 0, 0, sar, 0) /* SET ADDRESSING MODE */ @@ -1003,6 +1027,16 @@ F(0xe756, VLR, VRR_a, V, 0, 0, 0, 0, vlr, 0, IF_VEC) /* VECTOR LOAD AND REPLICATE */ F(0xe705, VLREP, VRX, V, la2, 0, 0, 0, vlrep, 0, IF_VEC) +/* VECTOR LOAD BYTE REVERSED ELEMENT */ + E(0xe601, VLEBRH, VRX, VE2, la2, 0, 0, 0, vlebr, 0, ES_16, IF_VEC) + E(0xe603, VLEBRF, VRX, VE2, la2, 0, 0, 0, vlebr, 0, ES_32, IF_VEC) + E(0xe602, VLEBRG, VRX, VE2, la2, 0, 0, 0, vlebr, 0, ES_64, IF_VEC) +/* VECTOR LOAD BYTE REVERSED ELEMENT AND REPLICATE */ + F(0xe605, VLBRREP, VRX, VE2, la2, 0, 0, 0, vlbrrep, 0, IF_VEC) +/* VECTOR LOAD BYTE REVERSED ELEMENT AND ZERO */ + F(0xe604, VLLEBRZ, VRX, VE2, la2, 0, 0, 0, vllebrz, 0, IF_VEC) +/* VECTOR LOAD BYTE REVERSED ELEMENTS */ + F(0xe606, VLBR, VRX, VE2, la2, 0, 0, 0, vlbr, 0, IF_VEC) /* VECTOR LOAD ELEMENT */ E(0xe700, VLEB, VRX, V, la2, 0, 0, 0, vle, 0, ES_8, IF_VEC) E(0xe701, VLEH, VRX, V, la2, 0, 0, 0, vle, 0, ES_16, IF_VEC) @@ -1013,6 +1047,8 @@ E(0xe741, VLEIH, VRI_a, V, 0, 0, 0, 0, vlei, 0, ES_16, IF_VEC) E(0xe743, VLEIF, VRI_a, V, 0, 0, 0, 0, vlei, 0, ES_32, IF_VEC) E(0xe742, VLEIG, VRI_a, V, 0, 0, 0, 0, vlei, 0, ES_64, IF_VEC) +/* VECTOR LOAD ELEMENTS REVERSED */ + F(0xe607, VLER, VRX, VE2, la2, 0, 0, 0, vler, 0, IF_VEC) /* VECTOR LOAD GR FROM VR ELEMENT */ F(0xe721, VLGV, VRS_c, V, la2, 0, r1, 0, vlgv, 0, IF_VEC) /* VECTOR LOAD LOGICAL ELEMENT AND ZERO */ @@ -1053,11 +1089,19 @@ F(0xe75f, VSEG, VRR_a, V, 0, 0, 0, 0, vseg, 0, IF_VEC) /* VECTOR STORE */ F(0xe70e, VST, VRX, V, la2, 0, 0, 0, vst, 0, IF_VEC) +/* VECTOR STORE BYTE REVERSED ELEMENT */ + E(0xe609, VSTEBRH, VRX, VE2, la2, 0, 0, 0, vstebr, 0, ES_16, IF_VEC) + E(0xe60b, VSTEBRF, VRX, VE2, la2, 0, 0, 0, vstebr, 0, ES_32, IF_VEC) + E(0xe60a, VSTEBRG, VRX, VE2, la2, 0, 0, 0, vstebr, 0, ES_64, IF_VEC) +/* VECTOR STORE BYTE REVERSED ELEMENTS */ + F(0xe60e, VSTBR, VRX, VE2, la2, 0, 0, 0, vstbr, 0, IF_VEC) /* VECTOR STORE ELEMENT */ E(0xe708, VSTEB, VRX, V, la2, 0, 0, 0, vste, 0, ES_8, IF_VEC) E(0xe709, VSTEH, VRX, V, la2, 0, 0, 0, vste, 0, ES_16, IF_VEC) E(0xe70b, VSTEF, VRX, V, la2, 0, 0, 0, vste, 0, ES_32, IF_VEC) E(0xe70a, VSTEG, VRX, V, la2, 0, 0, 0, vste, 0, ES_64, IF_VEC) +/* VECTOR STORE ELEMENTS REVERSED */ + F(0xe60f, VSTER, VRX, VE2, la2, 0, 0, 0, vster, 0, IF_VEC) /* VECTOR STORE MULTIPLE */ F(0xe73e, VSTM, VRS_a, V, la2, 0, 0, 0, vstm, 0, IF_VEC) /* VECTOR STORE WITH LENGTH */ @@ -1180,19 +1224,23 @@ F(0xe778, VESRLV, VRR_c, V, 0, 0, 0, 0, vesv, 0, IF_VEC) F(0xe738, VESRL, VRS_a, V, la2, 0, 0, 0, ves, 0, IF_VEC) /* VECTOR SHIFT LEFT */ - F(0xe774, VSL, VRR_c, V, 0, 0, 0, 0, vsl, 0, IF_VEC) + E(0xe774, VSL, VRR_c, V, 0, 0, 0, 0, vsl, 0, 0, IF_VEC) /* VECTOR SHIFT LEFT BY BYTE */ - F(0xe775, VSLB, VRR_c, V, 0, 0, 0, 0, vsl, 0, IF_VEC) + E(0xe775, VSLB, VRR_c, V, 0, 0, 0, 0, vsl, 0, 1, IF_VEC) +/* VECTOR SHIFT LEFT DOUBLE BY BIT */ + E(0xe786, VSLD, VRI_d, VE2, 0, 0, 0, 0, vsld, 0, 0, IF_VEC) /* VECTOR SHIFT LEFT DOUBLE BY BYTE */ - F(0xe777, VSLDB, VRI_d, V, 0, 0, 0, 0, vsldb, 0, IF_VEC) + E(0xe777, VSLDB, VRI_d, V, 0, 0, 0, 0, vsld, 0, 1, IF_VEC) /* VECTOR SHIFT RIGHT ARITHMETIC */ - F(0xe77e, VSRA, VRR_c, V, 0, 0, 0, 0, vsra, 0, IF_VEC) + E(0xe77e, VSRA, VRR_c, V, 0, 0, 0, 0, vsra, 0, 0, IF_VEC) /* VECTOR SHIFT RIGHT ARITHMETIC BY BYTE */ - F(0xe77f, VSRAB, VRR_c, V, 0, 0, 0, 0, vsra, 0, IF_VEC) + E(0xe77f, VSRAB, VRR_c, V, 0, 0, 0, 0, vsra, 0, 1, IF_VEC) +/* VECTOR SHIFT RIGHT DOUBLE BY BIT */ + F(0xe787, VSRD, VRI_d, VE2, 0, 0, 0, 0, vsrd, 0, IF_VEC) /* VECTOR SHIFT RIGHT LOGICAL */ - F(0xe77c, VSRL, VRR_c, V, 0, 0, 0, 0, vsrl, 0, IF_VEC) + E(0xe77c, VSRL, VRR_c, V, 0, 0, 0, 0, vsrl, 0, 0, IF_VEC) /* VECTOR SHIFT RIGHT LOGICAL BY BYTE */ - F(0xe77d, VSRLB, VRR_c, V, 0, 0, 0, 0, vsrl, 0, IF_VEC) + E(0xe77d, VSRLB, VRR_c, V, 0, 0, 0, 0, vsrl, 0, 1, IF_VEC) /* VECTOR SUBTRACT */ F(0xe7f7, VS, VRR_c, V, 0, 0, 0, 0, vs, 0, IF_VEC) /* VECTOR SUBTRACT COMPUTE BORROW INDICATION */ @@ -1222,6 +1270,8 @@ F(0xe75c, VISTR, VRR_a, V, 0, 0, 0, 0, vistr, 0, IF_VEC) /* VECTOR STRING RANGE COMPARE */ F(0xe78a, VSTRC, VRR_d, V, 0, 0, 0, 0, vstrc, 0, IF_VEC) +/* VECTOR STRING SEARCH */ + F(0xe78b, VSTRS, VRR_d, VE2, 0, 0, 0, 0, vstrs, 0, IF_VEC) /* === Vector Floating-Point Instructions */ @@ -1317,7 +1367,7 @@ /* SET ADDRESS SPACE CONTROL FAST */ F(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0, IF_PRIV) /* SET CLOCK */ - F(0xb204, SCK, S, Z, la2, 0, 0, 0, sck, 0, IF_PRIV | IF_IO) + F(0xb204, SCK, S, Z, 0, m2_64a, 0, 0, sck, 0, IF_PRIV | IF_IO) /* SET CLOCK COMPARATOR */ F(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0, IF_PRIV | IF_IO) /* SET CLOCK PROGRAMMABLE FIELD */ diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index b5523ef3c7..fc52aa128b 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -547,6 +547,26 @@ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) do_helper_mvc(env, l, dest, src, GETPC()); } +/* move right to left */ +void HELPER(mvcrl)(CPUS390XState *env, uint64_t l, uint64_t dest, uint64_t src) +{ + const int mmu_idx = cpu_mmu_index(env, false); + const uint64_t ra = GETPC(); + S390Access srca, desta; + int32_t i; + + /* MVCRL always copies one more byte than specified - maximum is 256 */ + l++; + + srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + + for (i = l - 1; i >= 0; i--) { + uint8_t byte = access_get_byte(env, &srca, i, ra); + access_set_byte(env, &desta, i, byte, ra); + } +} + /* move inverse */ void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { diff --git a/target/s390x/tcg/tcg_s390x.h b/target/s390x/tcg/tcg_s390x.h index 2f54ccb027..78558912f9 100644 --- a/target/s390x/tcg/tcg_s390x.h +++ b/target/s390x/tcg/tcg_s390x.h @@ -14,11 +14,11 @@ #define TCG_S390X_H void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque); -void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, - uint32_t code, uintptr_t ra); -void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, - uintptr_t ra); -void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc, - uintptr_t ra); +G_NORETURN void tcg_s390_program_interrupt(CPUS390XState *env, + uint32_t code, uintptr_t ra); +G_NORETURN void tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, + uintptr_t ra); +G_NORETURN void tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc, + uintptr_t ra); #endif /* TCG_S390X_H */ diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 46dea73357..b40cb84bae 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -263,7 +263,7 @@ static inline int vec_reg_offset(uint8_t reg, uint8_t enr, MemOp es) * 16 byte operations to handle it in a special way. */ g_assert(es <= MO_64); -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN offs ^= (8 - bytes); #endif return offs + vec_full_reg_offset(reg); @@ -1201,7 +1201,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, bool is_imm, int imm, TCGv_i64 cdest) { DisasJumpType ret; - uint64_t dest = s->base.pc_next + 2 * imm; + uint64_t dest = s->base.pc_next + (int64_t)imm * 2; TCGLabel *lab; /* Take care of the special cases first. */ @@ -1498,6 +1498,36 @@ static DisasJumpType op_andi(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_andc(DisasContext *s, DisasOps *o) +{ + tcg_gen_andc_i64(o->out, o->in1, o->in2); + return DISAS_NEXT; +} + +static DisasJumpType op_orc(DisasContext *s, DisasOps *o) +{ + tcg_gen_orc_i64(o->out, o->in1, o->in2); + return DISAS_NEXT; +} + +static DisasJumpType op_nand(DisasContext *s, DisasOps *o) +{ + tcg_gen_nand_i64(o->out, o->in1, o->in2); + return DISAS_NEXT; +} + +static DisasJumpType op_nor(DisasContext *s, DisasOps *o) +{ + tcg_gen_nor_i64(o->out, o->in1, o->in2); + return DISAS_NEXT; +} + +static DisasJumpType op_nxor(DisasContext *s, DisasOps *o) +{ + tcg_gen_eqv_i64(o->out, o->in1, o->in2); + return DISAS_NEXT; +} + static DisasJumpType op_ni(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -1567,7 +1597,7 @@ static DisasJumpType op_bal(DisasContext *s, DisasOps *o) static DisasJumpType op_basi(DisasContext *s, DisasOps *o) { pc_to_link_info(o->out, s, s->pc_tmp); - return help_goto_direct(s, s->base.pc_next + 2 * get_field(s, i2)); + return help_goto_direct(s, s->base.pc_next + (int64_t)get_field(s, i2) * 2); } static DisasJumpType op_bc(DisasContext *s, DisasOps *o) @@ -2592,7 +2622,7 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld8u(tmp, o->in2, get_mem_index(s)); tcg_gen_addi_i64(o->in2, o->in2, 1); tcg_gen_deposit_i64(o->out, o->out, tmp, pos, 8); - ccm |= 0xff << pos; + ccm |= 0xffull << pos; } m3 = (m3 << 1) & 0xf; pos -= 8; @@ -2958,7 +2988,13 @@ static DisasJumpType op_loc(DisasContext *s, DisasOps *o) { DisasCompare c; - disas_jcc(s, &c, get_field(s, m3)); + if (have_field(s, m3)) { + /* LOAD * ON CONDITION */ + disas_jcc(s, &c, get_field(s, m3)); + } else { + /* SELECT */ + disas_jcc(s, &c, get_field(s, m4)); + } if (c.is_64) { tcg_gen_movcond_i64(c.cond, o->out, c.u.s64.a, c.u.s64.b, @@ -3358,6 +3394,12 @@ static DisasJumpType op_mvc(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_mvcrl(DisasContext *s, DisasOps *o) +{ + gen_helper_mvcrl(cpu_env, regs[0], o->addr1, o->in2); + return DISAS_NEXT; +} + static DisasJumpType op_mvcin(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s, l1)); @@ -3744,7 +3786,13 @@ static DisasJumpType op_pku(DisasContext *s, DisasOps *o) static DisasJumpType op_popcnt(DisasContext *s, DisasOps *o) { - gen_helper_popcnt(o->out, o->in2); + const uint8_t m3 = get_field(s, m3); + + if ((m3 & 8) && s390_has_feat(S390_FEAT_MISC_INSTRUCTION_EXT3)) { + tcg_gen_ctpop_i64(o->out, o->in2); + } else { + gen_helper_popcnt(o->out, o->in2); + } return DISAS_NEXT; } @@ -4290,8 +4338,7 @@ static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_sck(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TEUQ | MO_ALIGN); - gen_helper_sck(cc_op, cpu_env, o->in1); + gen_helper_sck(cc_op, cpu_env, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -5668,6 +5715,13 @@ static void in1_r3_D32(DisasContext *s, DisasOps *o) } #define SPEC_in1_r3_D32 SPEC_r3_even +static void in1_r3_sr32(DisasContext *s, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_shri_i64(o->in1, regs[get_field(s, r3)], 32); +} +#define SPEC_in1_r3_sr32 0 + static void in1_e1(DisasContext *s, DisasOps *o) { o->in1 = load_freg32_i64(get_field(s, r1)); @@ -6168,8 +6222,10 @@ enum DisasInsnEnum { #define FAC_PCI S390_FEAT_ZPCI /* z/PCI facility */ #define FAC_AIS S390_FEAT_ADAPTER_INT_SUPPRESSION #define FAC_V S390_FEAT_VECTOR /* vector facility */ -#define FAC_VE S390_FEAT_VECTOR_ENH /* vector enhancements facility 1 */ +#define FAC_VE S390_FEAT_VECTOR_ENH /* vector enhancements facility 1 */ +#define FAC_VE2 S390_FEAT_VECTOR_ENH2 /* vector enhancements facility 2 */ #define FAC_MIE2 S390_FEAT_MISC_INSTRUCTION_EXT2 /* miscellaneous-instruction-extensions facility 2 */ +#define FAC_MIE3 S390_FEAT_MISC_INSTRUCTION_EXT3 /* miscellaneous-instruction-extensions facility 3 */ static const DisasInsn insn_info[] = { #include "insn-data.def" @@ -6605,16 +6661,17 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void s390x_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void s390x_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { DisasContext *dc = container_of(dcbase, DisasContext, base); if (unlikely(dc->ex_value)) { - /* ??? Unfortunately log_target_disas can't use host memory. */ - qemu_log("IN: EXECUTE %016" PRIx64, dc->ex_value); + /* ??? Unfortunately target_disas can't use host memory. */ + fprintf(logfile, "IN: EXECUTE %016" PRIx64, dc->ex_value); } else { - qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cs, dc->base.pc_first, dc->base.tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); + target_disas(logfile, cs, dc->base.pc_first, dc->base.tb->size); } } diff --git a/target/s390x/tcg/translate_vx.c.inc b/target/s390x/tcg/translate_vx.c.inc index 98eb7710a4..3526ba3e3b 100644 --- a/target/s390x/tcg/translate_vx.c.inc +++ b/target/s390x/tcg/translate_vx.c.inc @@ -175,7 +175,7 @@ static void get_vec_element_ptr_i64(TCGv_ptr ptr, uint8_t reg, TCGv_i64 enr, /* convert it to an element offset relative to cpu_env (vec_reg_offset() */ tcg_gen_shli_i64(tmp, tmp, es); -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN tcg_gen_xori_i64(tmp, tmp, 8 - NUM_VEC_ELEMENT_BYTES(es)); #endif tcg_gen_addi_i64(tmp, tmp, vec_full_reg_offset(reg)); @@ -457,6 +457,129 @@ static DisasJumpType op_vlrep(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_vlebr(DisasContext *s, DisasOps *o) +{ + const uint8_t es = s->insn->data; + const uint8_t enr = get_field(s, m3); + TCGv_i64 tmp; + + if (!valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_LE | es); + write_vec_element_i64(tmp, get_field(s, v1), enr, es); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vlbrrep(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s, m3); + TCGv_i64 tmp; + + if (es < ES_16 || es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_LE | es); + gen_gvec_dup_i64(es, get_field(s, v1), tmp); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vllebrz(DisasContext *s, DisasOps *o) +{ + const uint8_t m3 = get_field(s, m3); + TCGv_i64 tmp; + int es, lshift; + + switch (m3) { + case ES_16: + case ES_32: + case ES_64: + es = m3; + lshift = 0; + break; + case 6: + es = ES_32; + lshift = 32; + break; + default: + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_LE | es); + tcg_gen_shli_i64(tmp, tmp, lshift); + + write_vec_element_i64(tmp, get_field(s, v1), 0, ES_64); + write_vec_element_i64(tcg_constant_i64(0), get_field(s, v1), 1, ES_64); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vlbr(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s, m3); + TCGv_i64 t0, t1; + + if (es < ES_16 || es > ES_128) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + + if (es == ES_128) { + tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_LEUQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + tcg_gen_qemu_ld_i64(t0, o->addr1, get_mem_index(s), MO_LEUQ); + goto write; + } + + /* Begin with byte reversed doublewords... */ + tcg_gen_qemu_ld_i64(t0, o->addr1, get_mem_index(s), MO_LEUQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_LEUQ); + + /* + * For 16 and 32-bit elements, the doubleword bswap also reversed + * the order of the elements. Perform a larger order swap to put + * them back into place. For the 128-bit "element", finish the + * bswap by swapping the doublewords. + */ + switch (es) { + case ES_16: + tcg_gen_hswap_i64(t0, t0); + tcg_gen_hswap_i64(t1, t1); + break; + case ES_32: + tcg_gen_wswap_i64(t0, t0); + tcg_gen_wswap_i64(t1, t1); + break; + case ES_64: + break; + default: + g_assert_not_reached(); + } + +write: + write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); + + tcg_temp_free(t0); + tcg_temp_free(t1); + return DISAS_NEXT; +} + static DisasJumpType op_vle(DisasContext *s, DisasOps *o) { const uint8_t es = s->insn->data; @@ -492,6 +615,46 @@ static DisasJumpType op_vlei(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_vler(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s, m3); + + if (es < ES_16 || es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + /* Begin with the two doublewords swapped... */ + tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_TEUQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + tcg_gen_qemu_ld_i64(t0, o->addr1, get_mem_index(s), MO_TEUQ); + + /* ... then swap smaller elements within the doublewords as required. */ + switch (es) { + case MO_16: + tcg_gen_hswap_i64(t1, t1); + tcg_gen_hswap_i64(t0, t0); + break; + case MO_32: + tcg_gen_wswap_i64(t1, t1); + tcg_gen_wswap_i64(t0, t0); + break; + case MO_64: + break; + default: + g_assert_not_reached(); + } + + write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); + tcg_temp_free(t0); + tcg_temp_free(t1); + return DISAS_NEXT; +} + static DisasJumpType op_vlgv(DisasContext *s, DisasOps *o) { const uint8_t es = get_field(s, m4); @@ -958,6 +1121,81 @@ static DisasJumpType op_vst(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_vstebr(DisasContext *s, DisasOps *o) +{ + const uint8_t es = s->insn->data; + const uint8_t enr = get_field(s, m3); + TCGv_i64 tmp; + + if (!valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + read_vec_element_i64(tmp, get_field(s, v1), enr, es); + tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_LE | es); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vstbr(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s, m3); + TCGv_i64 t0, t1; + + if (es < ES_16 || es > ES_128) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + /* Probe write access before actually modifying memory */ + gen_helper_probe_write_access(cpu_env, o->addr1, tcg_constant_i64(16)); + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + + if (es == ES_128) { + read_vec_element_i64(t1, get_field(s, v1), 0, ES_64); + read_vec_element_i64(t0, get_field(s, v1), 1, ES_64); + goto write; + } + + read_vec_element_i64(t0, get_field(s, v1), 0, ES_64); + read_vec_element_i64(t1, get_field(s, v1), 1, ES_64); + + /* + * For 16 and 32-bit elements, the doubleword bswap below will + * reverse the order of the elements. Perform a larger order + * swap to put them back into place. For the 128-bit "element", + * finish the bswap by swapping the doublewords. + */ + switch (es) { + case MO_16: + tcg_gen_hswap_i64(t0, t0); + tcg_gen_hswap_i64(t1, t1); + break; + case MO_32: + tcg_gen_wswap_i64(t0, t0); + tcg_gen_wswap_i64(t1, t1); + break; + case MO_64: + break; + default: + g_assert_not_reached(); + } + +write: + tcg_gen_qemu_st_i64(t0, o->addr1, get_mem_index(s), MO_LEUQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + tcg_gen_qemu_st_i64(t1, o->addr1, get_mem_index(s), MO_LEUQ); + + tcg_temp_free(t0); + tcg_temp_free(t1); + return DISAS_NEXT; +} + static DisasJumpType op_vste(DisasContext *s, DisasOps *o) { const uint8_t es = s->insn->data; @@ -976,6 +1214,50 @@ static DisasJumpType op_vste(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_vster(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s, m3); + TCGv_i64 t0, t1; + + if (es < ES_16 || es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + /* Probe write access before actually modifying memory */ + gen_helper_probe_write_access(cpu_env, o->addr1, tcg_constant_i64(16)); + + /* Begin with the two doublewords swapped... */ + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + read_vec_element_i64(t1, get_field(s, v1), 0, ES_64); + read_vec_element_i64(t0, get_field(s, v1), 1, ES_64); + + /* ... then swap smaller elements within the doublewords as required. */ + switch (es) { + case MO_16: + tcg_gen_hswap_i64(t1, t1); + tcg_gen_hswap_i64(t0, t0); + break; + case MO_32: + tcg_gen_wswap_i64(t1, t1); + tcg_gen_wswap_i64(t0, t0); + break; + case MO_64: + break; + default: + g_assert_not_reached(); + } + + tcg_gen_qemu_st_i64(t0, o->addr1, get_mem_index(s), MO_TEUQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + tcg_gen_qemu_st_i64(t1, o->addr1, get_mem_index(s), MO_TEUQ); + + tcg_temp_free(t0); + tcg_temp_free(t1); + return DISAS_NEXT; +} + static DisasJumpType op_vstm(DisasContext *s, DisasOps *o) { const uint8_t v3 = get_field(s, v3); @@ -2018,31 +2300,61 @@ static DisasJumpType op_ves(DisasContext *s, DisasOps *o) return DISAS_NEXT; } -static DisasJumpType op_vsl(DisasContext *s, DisasOps *o) +static DisasJumpType gen_vsh_by_byte(DisasContext *s, DisasOps *o, + gen_helper_gvec_2i *gen, + gen_helper_gvec_3 *gen_ve2) { - TCGv_i64 shift = tcg_temp_new_i64(); + bool byte = s->insn->data; - read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); - if (s->fields.op2 == 0x74) { - tcg_gen_andi_i64(shift, shift, 0x7); + if (!byte && s390_has_feat(S390_FEAT_VECTOR_ENH2)) { + gen_gvec_3_ool(get_field(s, v1), get_field(s, v2), + get_field(s, v3), 0, gen_ve2); } else { - tcg_gen_andi_i64(shift, shift, 0x78); - } + TCGv_i64 shift = tcg_temp_new_i64(); - gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), - shift, 0, gen_helper_gvec_vsl); - tcg_temp_free_i64(shift); + read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); + tcg_gen_andi_i64(shift, shift, byte ? 0x78 : 7); + gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), shift, 0, gen); + tcg_temp_free_i64(shift); + } return DISAS_NEXT; } -static DisasJumpType op_vsldb(DisasContext *s, DisasOps *o) +static DisasJumpType op_vsl(DisasContext *s, DisasOps *o) { - const uint8_t i4 = get_field(s, i4) & 0xf; - const int left_shift = (i4 & 7) * 8; - const int right_shift = 64 - left_shift; - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + return gen_vsh_by_byte(s, o, gen_helper_gvec_vsl, + gen_helper_gvec_vsl_ve2); +} + +static DisasJumpType op_vsra(DisasContext *s, DisasOps *o) +{ + return gen_vsh_by_byte(s, o, gen_helper_gvec_vsra, + gen_helper_gvec_vsra_ve2); +} + +static DisasJumpType op_vsrl(DisasContext *s, DisasOps *o) +{ + return gen_vsh_by_byte(s, o, gen_helper_gvec_vsrl, + gen_helper_gvec_vsrl_ve2); +} + +static DisasJumpType op_vsld(DisasContext *s, DisasOps *o) +{ + const bool byte = s->insn->data; + const uint8_t mask = byte ? 15 : 7; + const uint8_t mul = byte ? 8 : 1; + const uint8_t i4 = get_field(s, i4); + const int right_shift = 64 - (i4 & 7) * mul; + TCGv_i64 t0, t1, t2; + + if (i4 & ~mask) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); if ((i4 & 8) == 0) { read_vec_element_i64(t0, get_field(s, v2), 0, ES_64); @@ -2053,8 +2365,10 @@ static DisasJumpType op_vsldb(DisasContext *s, DisasOps *o) read_vec_element_i64(t1, get_field(s, v3), 0, ES_64); read_vec_element_i64(t2, get_field(s, v3), 1, ES_64); } + tcg_gen_extract2_i64(t0, t1, t0, right_shift); tcg_gen_extract2_i64(t1, t2, t1, right_shift); + write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); @@ -2064,37 +2378,33 @@ static DisasJumpType op_vsldb(DisasContext *s, DisasOps *o) return DISAS_NEXT; } -static DisasJumpType op_vsra(DisasContext *s, DisasOps *o) +static DisasJumpType op_vsrd(DisasContext *s, DisasOps *o) { - TCGv_i64 shift = tcg_temp_new_i64(); + const uint8_t i4 = get_field(s, i4); + TCGv_i64 t0, t1, t2; - read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); - if (s->fields.op2 == 0x7e) { - tcg_gen_andi_i64(shift, shift, 0x7); - } else { - tcg_gen_andi_i64(shift, shift, 0x78); + if (i4 & ~7) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; } - gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), - shift, 0, gen_helper_gvec_vsra); - tcg_temp_free_i64(shift); - return DISAS_NEXT; -} + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); -static DisasJumpType op_vsrl(DisasContext *s, DisasOps *o) -{ - TCGv_i64 shift = tcg_temp_new_i64(); + read_vec_element_i64(t0, get_field(s, v2), 1, ES_64); + read_vec_element_i64(t1, get_field(s, v3), 0, ES_64); + read_vec_element_i64(t2, get_field(s, v3), 1, ES_64); - read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); - if (s->fields.op2 == 0x7c) { - tcg_gen_andi_i64(shift, shift, 0x7); - } else { - tcg_gen_andi_i64(shift, shift, 0x78); - } + tcg_gen_extract2_i64(t0, t1, t0, i4); + tcg_gen_extract2_i64(t1, t2, t1, i4); - gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), - shift, 0, gen_helper_gvec_vsrl); - tcg_temp_free_i64(shift); + write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); return DISAS_NEXT; } @@ -2497,6 +2807,31 @@ static DisasJumpType op_vstrc(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_vstrs(DisasContext *s, DisasOps *o) +{ + typedef void (*helper_vstrs)(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); + static const helper_vstrs fns[3][2] = { + { gen_helper_gvec_vstrs_8, gen_helper_gvec_vstrs_zs8 }, + { gen_helper_gvec_vstrs_16, gen_helper_gvec_vstrs_zs16 }, + { gen_helper_gvec_vstrs_32, gen_helper_gvec_vstrs_zs32 }, + }; + const uint8_t es = get_field(s, m5); + const uint8_t m6 = get_field(s, m6); + const bool zs = extract32(m6, 1, 1); + + if (es > ES_32 || m6 & ~2) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + gen_gvec_4_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), + cpu_env, 0, fns[es][zs]); + set_cc_static(s); + return DISAS_NEXT; +} + static DisasJumpType op_vfa(DisasContext *s, DisasOps *o) { const uint8_t fpf = get_field(s, m4); @@ -2720,23 +3055,59 @@ static DisasJumpType op_vcdg(DisasContext *s, DisasOps *o) switch (s->fields.op2) { case 0xc3: - if (fpf == FPF_LONG) { + switch (fpf) { + case FPF_LONG: fn = gen_helper_gvec_vcdg64; + break; + case FPF_SHORT: + if (s390_has_feat(S390_FEAT_VECTOR_ENH2)) { + fn = gen_helper_gvec_vcdg32; + } + break; + default: + break; } break; case 0xc1: - if (fpf == FPF_LONG) { + switch (fpf) { + case FPF_LONG: fn = gen_helper_gvec_vcdlg64; + break; + case FPF_SHORT: + if (s390_has_feat(S390_FEAT_VECTOR_ENH2)) { + fn = gen_helper_gvec_vcdlg32; + } + break; + default: + break; } break; case 0xc2: - if (fpf == FPF_LONG) { + switch (fpf) { + case FPF_LONG: fn = gen_helper_gvec_vcgd64; + break; + case FPF_SHORT: + if (s390_has_feat(S390_FEAT_VECTOR_ENH2)) { + fn = gen_helper_gvec_vcgd32; + } + break; + default: + break; } break; case 0xc0: - if (fpf == FPF_LONG) { + switch (fpf) { + case FPF_LONG: fn = gen_helper_gvec_vclgd64; + break; + case FPF_SHORT: + if (s390_has_feat(S390_FEAT_VECTOR_ENH2)) { + fn = gen_helper_gvec_vclgd32; + } + break; + default: + break; } break; case 0xc7: diff --git a/target/s390x/tcg/vec.h b/target/s390x/tcg/vec.h index a6e361869b..8d095efcfc 100644 --- a/target/s390x/tcg/vec.h +++ b/target/s390x/tcg/vec.h @@ -38,7 +38,7 @@ typedef union S390Vector { * W: [ 1][ 0] - [ 3][ 2] * DW: [ 0] - [ 1] */ -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN #define H1(x) ((x) ^ 7) #define H2(x) ((x) ^ 3) #define H4(x) ((x) ^ 1) diff --git a/target/s390x/tcg/vec_fpu_helper.c b/target/s390x/tcg/vec_fpu_helper.c index 1a77993471..2a618a1093 100644 --- a/target/s390x/tcg/vec_fpu_helper.c +++ b/target/s390x/tcg/vec_fpu_helper.c @@ -10,7 +10,6 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "s390x-internal.h" #include "vec.h" @@ -176,6 +175,30 @@ static void vop128_2(S390Vector *v1, const S390Vector *v2, CPUS390XState *env, *v1 = tmp; } +static float32 vcdg32(float32 a, float_status *s) +{ + return int32_to_float32(a, s); +} + +static float32 vcdlg32(float32 a, float_status *s) +{ + return uint32_to_float32(a, s); +} + +static float32 vcgd32(float32 a, float_status *s) +{ + const float32 tmp = float32_to_int32(a, s); + + return float32_is_any_nan(a) ? INT32_MIN : tmp; +} + +static float32 vclgd32(float32 a, float_status *s) +{ + const float32 tmp = float32_to_uint32(a, s); + + return float32_is_any_nan(a) ? 0 : tmp; +} + static float64 vcdg64(float64 a, float_status *s) { return int64_to_float64(a, s); @@ -211,6 +234,9 @@ void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, CPUS390XState *env, \ vop##BITS##_2(v1, v2, env, se, XxC, erm, FN, GETPC()); \ } +#define DEF_GVEC_VOP2_32(NAME) \ +DEF_GVEC_VOP2_FN(NAME, NAME##32, 32) + #define DEF_GVEC_VOP2_64(NAME) \ DEF_GVEC_VOP2_FN(NAME, NAME##64, 64) @@ -219,6 +245,10 @@ DEF_GVEC_VOP2_FN(NAME, float32_##OP, 32) \ DEF_GVEC_VOP2_FN(NAME, float64_##OP, 64) \ DEF_GVEC_VOP2_FN(NAME, float128_##OP, 128) +DEF_GVEC_VOP2_32(vcdg) +DEF_GVEC_VOP2_32(vcdlg) +DEF_GVEC_VOP2_32(vcgd) +DEF_GVEC_VOP2_32(vclgd) DEF_GVEC_VOP2_64(vcdg) DEF_GVEC_VOP2_64(vcdlg) DEF_GVEC_VOP2_64(vcgd) diff --git a/target/s390x/tcg/vec_helper.c b/target/s390x/tcg/vec_helper.c index ededf13cf0..48d86722b2 100644 --- a/target/s390x/tcg/vec_helper.c +++ b/target/s390x/tcg/vec_helper.c @@ -200,7 +200,6 @@ void HELPER(vstl)(CPUS390XState *env, const void *v1, uint64_t addr, addr = wrap_address(env, addr + 8); cpu_stq_data_ra(env, addr, s390_vec_read_element64(v1, 1), GETPC()); } else { - S390Vector tmp = {}; int i; for (i = 0; i < bytes; i++) { @@ -209,6 +208,5 @@ void HELPER(vstl)(CPUS390XState *env, const void *v1, uint64_t addr, cpu_stb_data_ra(env, addr, byte, GETPC()); addr = wrap_address(env, addr + 1); } - *(S390Vector *)v1 = tmp; } } diff --git a/target/s390x/tcg/vec_int_helper.c b/target/s390x/tcg/vec_int_helper.c index 5561b3ed90..53ab5c5eb3 100644 --- a/target/s390x/tcg/vec_int_helper.c +++ b/target/s390x/tcg/vec_int_helper.c @@ -10,7 +10,6 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "vec.h" #include "exec/helper-proto.h" @@ -540,18 +539,73 @@ void HELPER(gvec_vsl)(void *v1, const void *v2, uint64_t count, s390_vec_shl(v1, v2, count); } +void HELPER(gvec_vsl_ve2)(void *v1, const void *v2, const void *v3, + uint32_t desc) +{ + S390Vector tmp; + uint32_t sh, e0, e1 = 0; + int i; + + for (i = 15; i >= 0; --i, e1 = e0) { + e0 = s390_vec_read_element8(v2, i); + sh = s390_vec_read_element8(v3, i) & 7; + + s390_vec_write_element8(&tmp, i, rol32(e0 | (e1 << 24), sh)); + } + + *(S390Vector *)v1 = tmp; +} + void HELPER(gvec_vsra)(void *v1, const void *v2, uint64_t count, uint32_t desc) { s390_vec_sar(v1, v2, count); } +void HELPER(gvec_vsra_ve2)(void *v1, const void *v2, const void *v3, + uint32_t desc) +{ + S390Vector tmp; + uint32_t sh, e0, e1 = 0; + int i = 0; + + /* Byte 0 is special only. */ + e0 = (int32_t)(int8_t)s390_vec_read_element8(v2, i); + sh = s390_vec_read_element8(v3, i) & 7; + s390_vec_write_element8(&tmp, i, e0 >> sh); + + e1 = e0; + for (i = 1; i < 16; ++i, e1 = e0) { + e0 = s390_vec_read_element8(v2, i); + sh = s390_vec_read_element8(v3, i) & 7; + s390_vec_write_element8(&tmp, i, (e0 | e1 << 8) >> sh); + } + + *(S390Vector *)v1 = tmp; +} + void HELPER(gvec_vsrl)(void *v1, const void *v2, uint64_t count, uint32_t desc) { s390_vec_shr(v1, v2, count); } +void HELPER(gvec_vsrl_ve2)(void *v1, const void *v2, const void *v3, + uint32_t desc) +{ + S390Vector tmp; + uint32_t sh, e0, e1 = 0; + + for (int i = 0; i < 16; ++i, e1 = e0) { + e0 = s390_vec_read_element8(v2, i); + sh = s390_vec_read_element8(v3, i) & 7; + + s390_vec_write_element8(&tmp, i, (e0 | (e1 << 8)) >> sh); + } + + *(S390Vector *)v1 = tmp; +} + #define DEF_VSCBI(BITS) \ void HELPER(gvec_vscbi##BITS)(void *v1, const void *v2, const void *v3, \ uint32_t desc) \ diff --git a/target/s390x/tcg/vec_string_helper.c b/target/s390x/tcg/vec_string_helper.c index ac315eb095..9b85becdfb 100644 --- a/target/s390x/tcg/vec_string_helper.c +++ b/target/s390x/tcg/vec_string_helper.c @@ -10,7 +10,6 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "s390x-internal.h" #include "vec.h" @@ -471,3 +470,102 @@ void HELPER(gvec_vstrc_cc_rt##BITS)(void *v1, const void *v2, const void *v3, \ DEF_VSTRC_CC_RT_HELPER(8) DEF_VSTRC_CC_RT_HELPER(16) DEF_VSTRC_CC_RT_HELPER(32) + +static int vstrs(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, + const S390Vector *v4, uint8_t es, bool zs) +{ + int substr_elen, substr_0, str_elen, i, j, k, cc; + int nelem = 16 >> es; + bool eos = false; + + substr_elen = s390_vec_read_element8(v4, 7) >> es; + + /* If ZS, bound substr length by min(nelem, strlen(v3)). */ + if (zs) { + substr_elen = MIN(substr_elen, nelem); + for (i = 0; i < substr_elen; i++) { + if (s390_vec_read_element(v3, i, es) == 0) { + substr_elen = i; + break; + } + } + } + + if (substr_elen == 0) { + cc = 2; /* full match for degenerate case of empty substr */ + k = 0; + goto done; + } + + /* If ZS, look for eos in the searched string. */ + if (zs) { + for (k = 0; k < nelem; k++) { + if (s390_vec_read_element(v2, k, es) == 0) { + eos = true; + break; + } + } + str_elen = k; + } else { + str_elen = nelem; + } + + substr_0 = s390_vec_read_element(v3, 0, es); + + for (k = 0; ; k++) { + for (; k < str_elen; k++) { + if (s390_vec_read_element(v2, k, es) == substr_0) { + break; + } + } + + /* If we reached the end of the string, no match. */ + if (k == str_elen) { + cc = eos; /* no match (with or without zero char) */ + goto done; + } + + /* If the substring is only one char, match. */ + if (substr_elen == 1) { + cc = 2; /* full match */ + goto done; + } + + /* If the match begins at the last char, we have a partial match. */ + if (k == str_elen - 1) { + cc = 3; /* partial match */ + goto done; + } + + i = MIN(nelem, k + substr_elen); + for (j = k + 1; j < i; j++) { + uint32_t e2 = s390_vec_read_element(v2, j, es); + uint32_t e3 = s390_vec_read_element(v3, j - k, es); + if (e2 != e3) { + break; + } + } + if (j == i) { + /* Matched up until "end". */ + cc = i - k == substr_elen ? 2 : 3; /* full or partial match */ + goto done; + } + } + + done: + s390_vec_write_element64(v1, 0, k << es); + s390_vec_write_element64(v1, 1, 0); + return cc; +} + +#define DEF_VSTRS_HELPER(BITS) \ +void QEMU_FLATTEN HELPER(gvec_vstrs_##BITS)(void *v1, const void *v2, \ + const void *v3, const void *v4, CPUS390XState *env, uint32_t desc) \ + { env->cc_op = vstrs(v1, v2, v3, v4, MO_##BITS, false); } \ +void QEMU_FLATTEN HELPER(gvec_vstrs_zs##BITS)(void *v1, const void *v2, \ + const void *v3, const void *v4, CPUS390XState *env, uint32_t desc) \ + { env->cc_op = vstrs(v1, v2, v3, v4, MO_##BITS, true); } + +DEF_VSTRS_HELPER(8) +DEF_VSTRS_HELPER(16) +DEF_VSTRS_HELPER(32) diff --git a/target/sh4/README.sh4 b/target/sh4/README.sh4 deleted file mode 100644 index a192ca7540..0000000000 --- a/target/sh4/README.sh4 +++ /dev/null @@ -1,150 +0,0 @@ -qemu target: sh4 -author: Samuel Tardieu -last modified: Tue Dec 6 07:22:44 CET 2005 - -The sh4 target is not ready at all yet for integration in qemu. This -file describes the current state of implementation. - -Most places requiring attention and/or modification can be detected by -looking for "XXXXX" or "abort()". - -The sh4 core is located in target/sh4/*, while the 7750 peripheral -features (IO ports for example) are located in hw/sh7750.[ch]. The -main board description is in hw/shix.c, and the NAND flash in -hw/tc58128.[ch]. - -All the shortcomings indicated here will eventually be resolved. This -is a work in progress. Features are added in a semi-random order: if a -point is blocking to progress on booting the Linux kernel for the shix -board, it is addressed first; if feedback is necessary and no progress -can be made on blocking points until it is received, a random feature -is worked on. - -Goals ------ - -The primary model being worked on is the soft MMU target to be able to -emulate the Shix 2.0 board by Alexis Polti, described at -https://web.archive.org/web/20070917001736/http://perso.enst.fr/~polti/realisations/shix20/ - -Ultimately, qemu will be coupled with a system C or a verilog -simulator to simulate the whole board functionalities. - -A sh4 user-mode has also somewhat started but will be worked on -afterwards. The goal is to automate tests for GNAT (GNU Ada) compiler -that I ported recently to the sh4-linux target. - -Registers ---------- - -16 general purpose registers are available at any time. The first 8 -registers are banked and the non-directly visible ones can be accessed -by privileged instructions. In qemu, we define 24 general purpose -registers and the code generation use either [0-7]+[8-15] or -[16-23]+[8-15] depending on the MD and RB flags in the sr -configuration register. - -Instructions ------------- - -Most sh4 instructions have been implemented. The missing ones at this -time are: - - FPU related instructions - - LDTLB to load a new MMU entry - - SLEEP to put the processor in sleep mode - -Most instructions could be optimized a lot. This will be worked on -after the current model is fully functional unless debugging -convenience requires that it is done early. - -Many instructions did not have a chance to be tested yet. The plan is -to implement unit and regression testing of those in the future. - -MMU ---- - -The MMU is implemented in the sh4 core. MMU management has not been -tested at all yet. In the sh7750, it can be manipulated through memory -mapped registers and this part has not yet been implemented. - -Exceptions ----------- - -Exceptions are implemented as described in the sh4 reference manual -but have not been tested yet. They do not use qemu EXCP_ features -yet. - -IRQ ---- - -IRQ are not implemented yet. - -Peripheral features -------------------- - - + Serial ports - -Configuration and use of the first serial port (SCI) without -interrupts is supported. Input has not yet been tested. - -Configuration of the second serial port (SCIF) is supported. FIFO -handling infrastructure has been started but is not completed yet. - - + GPIO ports - -GPIO ports have been implemented. A registration function allows -external modules to register interest in some port changes (see -hw/tc58128.[ch] for an example) and will be called back. Interrupt -generation is not yet supported but some infrastructure is in place -for this purpose. Note that in the current model a peripheral module -cannot directly simulate a H->L->H input port transition and have an -interrupt generated on the low level. - - + TC58128 NAND flash - -TC58128 NAND flash is partially implemented through GPIO ports. It -supports reading from flash. - -GDB ---- - -GDB remote target support has been implemented and lightly tested. - -Files ------ - -File names are hardcoded at this time. The bootloader must be stored in -shix_bios.bin in the current directory. The initial Linux image must -be stored in shix_linux_nand.bin in the current directory in NAND -format. Test files can be obtained from -http://perso.enst.fr/~polti/robot/ as well as the various datasheets I -use. - -qemu disk parameter on the command line is unused. You can supply any -existing image and it will be ignored. As the goal is to simulate an -embedded target, it is not clear how this parameter will be handled in -the future. - -To build an ELF kernel image from the NAND image, 16 bytes have to be -stripped off the end of every 528 bytes, keeping only 512 of them. The -following Python code snippet does it: - -#! /usr/bin/python - -def denand (infd, outfd): - while True: - d = infd.read (528) - if not d: return - outfd.write (d[:512]) - -if __name__ == '__main__': - import sys - denand (open (sys.argv[1], 'rb'), - open (sys.argv[2], 'wb')) - -Style isssues -------------- - -There is currently a mix between my style (space before opening -parenthesis) and qemu style. This will be resolved before final -integration is proposed. diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index 8903b4b9c7..d4192d1090 100644 --- a/target/sh4/cpu-qom.h +++ b/target/sh4/cpu-qom.h @@ -29,8 +29,7 @@ #define TYPE_SH7751R_CPU SUPERH_CPU_TYPE_NAME("sh7751r") #define TYPE_SH7785_CPU SUPERH_CPU_TYPE_NAME("sh7785") -OBJECT_DECLARE_TYPE(SuperHCPU, SuperHCPUClass, - SUPERH_CPU) +OBJECT_DECLARE_CPU_TYPE(SuperHCPU, SuperHCPUClass, SUPERH_CPU) /** * SuperHCPUClass: diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index fb9dd9db2f..9f15ef913c 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" /* CPU Subtypes */ #define SH_CPU_SH7750 (1 << 0) @@ -130,7 +131,7 @@ typedef struct memory_content { struct memory_content *next; } memory_content; -typedef struct CPUSH4State { +typedef struct CPUArchState { uint32_t flags; /* general execution flags */ uint32_t gregs[24]; /* general registers */ float32 fregs[32]; /* floating point registers */ @@ -195,7 +196,7 @@ typedef struct CPUSH4State { * * A SuperH CPU. */ -struct SuperHCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -209,9 +210,9 @@ void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); void sh4_translate_init(void); void sh4_cpu_list(void); @@ -264,9 +265,6 @@ static inline int cpu_mmu_index (CPUSH4State *env, bool ifetch) } } -typedef CPUSH4State CPUArchState; -typedef SuperHCPU ArchCPU; - #include "exec/cpu-all.h" /* MMU control register */ diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index 752669825f..a663335c39 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -57,8 +57,9 @@ void helper_ldtlb(CPUSH4State *env) #endif } -static inline void QEMU_NORETURN raise_exception(CPUSH4State *env, int index, - uintptr_t retaddr) +static inline G_NORETURN +void raise_exception(CPUSH4State *env, int index, + uintptr_t retaddr) { CPUState *cs = env_cpu(env); diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 43bc88b7b3..f1b190e7cf 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -2352,10 +2352,11 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void sh4_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +static void sh4_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cs, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps sh4_tr_ops = { diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index f33949aaee..86ed37d933 100644 --- a/target/sparc/cpu-qom.h +++ b/target/sparc/cpu-qom.h @@ -29,8 +29,7 @@ #define TYPE_SPARC_CPU "sparc-cpu" #endif -OBJECT_DECLARE_TYPE(SPARCCPU, SPARCCPUClass, - SPARC_CPU) +OBJECT_DECLARE_CPU_TYPE(SPARCCPU, SPARCCPUClass, SPARC_CPU) typedef struct sparc_def_t sparc_def_t; /** diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 5a7f1ed5d6..f80ea2e8cf 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -4,6 +4,7 @@ #include "qemu/bswap.h" #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" #if !defined(TARGET_SPARC64) #define TARGET_DPREGS 16 @@ -420,7 +421,7 @@ struct CPUTimer typedef struct CPUTimer CPUTimer; -typedef struct CPUSPARCState CPUSPARCState; +typedef struct CPUArchState CPUSPARCState; #if defined(TARGET_SPARC64) typedef union { uint64_t mmuregs[16]; @@ -439,7 +440,7 @@ typedef union { }; } SparcV9MMU; #endif -struct CPUSPARCState { +struct CPUArchState { target_ulong gregs[8]; /* general registers */ target_ulong *regwptr; /* pointer to current register window */ target_ulong pc; /* program counter */ @@ -556,7 +557,7 @@ struct CPUSPARCState { * * A SPARC CPU. */ -struct SPARCCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -574,11 +575,11 @@ void sparc_cpu_do_interrupt(CPUState *cpu); hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int sparc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - int mmu_idx, - uintptr_t retaddr); -void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t) QEMU_NORETURN; +G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + int mmu_idx, + uintptr_t retaddr); +G_NORETURN void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t); #ifndef NO_CPU_IO_DEFS /* cpu_init.c */ @@ -743,9 +744,6 @@ static inline int cpu_pil_allowed(CPUSPARCState *env1, int pil) #endif } -typedef CPUSPARCState CPUArchState; -typedef SPARCCPU ArchCPU; - #include "exec/cpu-all.h" #ifdef TARGET_SPARC64 diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 346a6dfa35..919448a494 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -925,10 +925,10 @@ hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } #ifndef CONFIG_USER_ONLY -void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, - int mmu_idx, - uintptr_t retaddr) +G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, + uintptr_t retaddr) { SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 4c7c7b5347..2e28222d31 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5901,10 +5901,11 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void sparc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void sparc_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps sparc_tr_ops = { diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index f917e5992d..3afdc6975c 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -42,7 +42,7 @@ target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize) GET_FIELD_SP(pixel_addr, 11, 12); } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN #define VIS_B64(n) b[7 - (n)] #define VIS_W64(n) w[3 - (n)] #define VIS_SW64(n) sw[3 - (n)] @@ -470,7 +470,7 @@ uint64_t helper_bshuffle(uint64_t gsr, uint64_t src1, uint64_t src2) uint32_t i, mask, host; /* Set up S such that we can index across all of the bytes. */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN s.ll[0] = src1; s.ll[1] = src2; host = 0; diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index 59bfd01bbc..ee24e9fa76 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -24,8 +24,7 @@ #define TYPE_TRICORE_CPU "tricore-cpu" -OBJECT_DECLARE_TYPE(TriCoreCPU, TriCoreCPUClass, - TRICORE_CPU) +OBJECT_DECLARE_CPU_TYPE(TriCoreCPU, TriCoreCPUClass, TRICORE_CPU) struct TriCoreCPUClass { /*< private >*/ diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index c461387e71..3b9c533a7c 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -22,14 +22,14 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "qemu/cpu-float.h" #include "tricore-defs.h" struct tricore_boot_info; typedef struct tricore_def_t tricore_def_t; -typedef struct CPUTriCoreState CPUTriCoreState; -struct CPUTriCoreState { +typedef struct CPUArchState { /* GPR Register */ uint32_t gpr_a[16]; uint32_t gpr_d[16]; @@ -189,7 +189,7 @@ struct CPUTriCoreState { const tricore_def_t *cpu_model; void *irq[8]; struct QEMUTimer *timer; /* Internal timer */ -}; +} CPUTriCoreState; /** * TriCoreCPU: @@ -197,7 +197,7 @@ struct CPUTriCoreState { * * A TriCore CPU. */ -struct TriCoreCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -369,9 +369,6 @@ static inline int cpu_mmu_index(CPUTriCoreState *env, bool ifetch) return 0; } -typedef CPUTriCoreState CPUArchState; -typedef TriCoreCPU ArchCPU; - #include "exec/cpu-all.h" void cpu_state_reset(CPUTriCoreState *s); diff --git a/target/tricore/gdbstub.c b/target/tricore/gdbstub.c index 3ce55abb8e..ebf32defde 100644 --- a/target/tricore/gdbstub.c +++ b/target/tricore/gdbstub.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "exec/gdbstub.h" diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index 9476d10d00..a79c838a92 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -25,9 +25,9 @@ /* Exception helpers */ -static void QEMU_NORETURN -raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin, - uintptr_t pc, uint32_t fcd_pc) +static G_NORETURN +void raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin, + uintptr_t pc, uint32_t fcd_pc) { CPUState *cs = env_cpu(env); /* in case we come from a helper-call we need to restore the PC */ diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 417edbd3f0..d170500fa5 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -8861,10 +8861,11 @@ static void tricore_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void tricore_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void tricore_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps tricore_tr_ops = { diff --git a/target/xtensa/core-de233_fpu.c b/target/xtensa/core-de233_fpu.c index c7cbeb1b48..41af8057fb 100644 --- a/target/xtensa/core-de233_fpu.c +++ b/target/xtensa/core-de233_fpu.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" -#include "qemu-common.h" #include "qemu/host-utils.h" #include "core-de233_fpu/core-isa.h" diff --git a/target/xtensa/core-dsp3400.c b/target/xtensa/core-dsp3400.c index 4e0bc8a8c4..81e425c568 100644 --- a/target/xtensa/core-dsp3400.c +++ b/target/xtensa/core-dsp3400.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" -#include "qemu-common.h" #include "qemu/host-utils.h" #include "core-dsp3400/core-isa.h" diff --git a/target/xtensa/core-test_mmuhifi_c3.c b/target/xtensa/core-test_mmuhifi_c3.c index 123c630b0d..c0e5d32d1e 100644 --- a/target/xtensa/core-test_mmuhifi_c3.c +++ b/target/xtensa/core-test_mmuhifi_c3.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" -#include "qemu-common.h" #include "qemu/host-utils.h" #include "core-test_mmuhifi_c3/core-isa.h" diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 41d9859673..4fc35ee49b 100644 --- a/target/xtensa/cpu-qom.h +++ b/target/xtensa/cpu-qom.h @@ -34,8 +34,7 @@ #define TYPE_XTENSA_CPU "xtensa-cpu" -OBJECT_DECLARE_TYPE(XtensaCPU, XtensaCPUClass, - XTENSA_CPU) +OBJECT_DECLARE_CPU_TYPE(XtensaCPU, XtensaCPUClass, XTENSA_CPU) typedef struct XtensaConfig XtensaConfig; diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 02143f2f77..d4b8268146 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -29,6 +29,7 @@ #define XTENSA_CPU_H #include "cpu-qom.h" +#include "qemu/cpu-float.h" #include "exec/cpu-defs.h" #include "xtensa-isa.h" @@ -306,7 +307,7 @@ typedef enum { INTTYPE_MAX } interrupt_type; -struct CPUXtensaState; +typedef struct CPUArchState CPUXtensaState; typedef struct xtensa_tlb_entry { uint32_t vaddr; @@ -344,7 +345,7 @@ typedef struct XtensaGdbRegmap { } XtensaGdbRegmap; typedef struct XtensaCcompareTimer { - struct CPUXtensaState *env; + CPUXtensaState *env; QEMUTimer *timer; } XtensaCcompareTimer; @@ -494,7 +495,7 @@ typedef struct XtensaConfigList { struct XtensaConfigList *next; } XtensaConfigList; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN enum { FP_F32_HIGH, FP_F32_LOW, @@ -506,7 +507,7 @@ enum { }; #endif -typedef struct CPUXtensaState { +struct CPUArchState { const XtensaConfig *config; uint32_t regs[16]; uint32_t pc; @@ -545,7 +546,7 @@ typedef struct CPUXtensaState { /* Watchpoints for DBREAK registers */ struct CPUWatchpoint *cpu_watchpoint[MAX_NDBREAK]; -} CPUXtensaState; +}; /** * XtensaCPU: @@ -553,7 +554,7 @@ typedef struct CPUXtensaState { * * An Xtensa CPU. */ -struct XtensaCPU { +struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ @@ -580,9 +581,9 @@ void xtensa_count_regs(const XtensaConfig *config, unsigned *n_regs, unsigned *n_core_regs); int xtensa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int xtensa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr) QEMU_NORETURN; +G_NORETURN void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); #define cpu_list xtensa_cpu_list @@ -590,7 +591,7 @@ void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, #define XTENSA_CPU_TYPE_NAME(model) model XTENSA_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_XTENSA_CPU -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN #define XTENSA_DEFAULT_CPU_MODEL "fsf" #define XTENSA_DEFAULT_CPU_NOMMU_MODEL "fsf" #else @@ -722,9 +723,6 @@ static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch) #define XTENSA_CSBASE_LBEG_OFF_MASK 0x00ff0000 #define XTENSA_CSBASE_LBEG_OFF_SHIFT 16 -typedef CPUXtensaState CPUArchState; -typedef XtensaCPU ArchCPU; - #include "exec/cpu-all.h" static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh index df66d09393..b4c15556c2 100755 --- a/target/xtensa/import_core.sh +++ b/target/xtensa/import_core.sh @@ -42,7 +42,6 @@ cat < "${TARGET}.c" #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" -#include "qemu-common.h" #include "qemu/host-utils.h" #include "core-$NAME/core-isa.h" diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h index 78720734fe..701c00eed2 100644 --- a/target/xtensa/overlay_tool.h +++ b/target/xtensa/overlay_tool.h @@ -449,7 +449,7 @@ #endif -#if (defined(TARGET_WORDS_BIGENDIAN) != 0) == (XCHAL_HAVE_BE != 0) +#if TARGET_BIG_ENDIAN == (XCHAL_HAVE_BE != 0) #define REGISTER_CORE(core) \ static void __attribute__((constructor)) register_core(void) \ { \ diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index b1491ed625..1485df2f22 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1296,10 +1296,11 @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void xtensa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +static void xtensa_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu, FILE *logfile) { - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); + fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); + target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps xtensa_translator_ops = { @@ -1471,14 +1472,14 @@ static void translate_b(DisasContext *dc, const OpcodeArg arg[], static void translate_bb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN TCGv_i32 bit = tcg_const_i32(0x80000000u); #else TCGv_i32 bit = tcg_const_i32(0x00000001u); #endif TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, arg[1].in, 0x1f); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN tcg_gen_shr_i32(bit, bit, tmp); #else tcg_gen_shl_i32(bit, bit, tmp); @@ -1493,7 +1494,7 @@ static void translate_bbi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { TCGv_i32 tmp = tcg_temp_new_i32(); -#ifdef TARGET_WORDS_BIGENDIAN +#if TARGET_BIG_ENDIAN tcg_gen_andi_i32(tmp, arg[0].in, 0x80000000u >> arg[1].imm); #else tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm); diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 077fc51401..61e284bb5c 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1086,9 +1086,9 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, opc = I3405_MOVZ; } s0 = ctz64(t0) & (63 & -16); - t1 = t0 & ~(0xffffUL << s0); + t1 = t0 & ~(0xffffull << s0); s1 = ctz64(t1) & (63 & -16); - t2 = t1 & ~(0xffffUL << s1); + t2 = t1 & ~(0xffffull << s1); if (t2 == 0) { tcg_out_insn_3405(s, opc, type, rd, t0 >> s0, s0); if (t1 != 0) { @@ -1557,7 +1557,7 @@ static void tcg_out_adr(TCGContext *s, TCGReg rd, const void *target) */ static void * const qemu_ld_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_ldub_mmu, -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN [MO_16] = helper_be_lduw_mmu, [MO_32] = helper_be_ldul_mmu, [MO_64] = helper_be_ldq_mmu, @@ -1574,7 +1574,7 @@ static void * const qemu_ld_helpers[MO_SIZE + 1] = { */ static void * const qemu_st_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_stb_mmu, -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN [MO_16] = helper_be_stw_mmu, [MO_32] = helper_be_stl_mmu, [MO_64] = helper_be_stq_mmu, diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 876af589ce..485f685bd2 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -131,6 +131,9 @@ typedef enum { #define TCG_TARGET_HAS_andc_vec 1 #define TCG_TARGET_HAS_orc_vec 1 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 #define TCG_TARGET_HAS_not_vec 1 #define TCG_TARGET_HAS_neg_vec 1 #define TCG_TARGET_HAS_abs_vec 1 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index e1ea69669c..2c6c353eea 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1296,7 +1296,7 @@ static void tcg_out_vldst(TCGContext *s, ARMInsn insn, static void * const qemu_ld_helpers[MO_SSIZE + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN [MO_UW] = helper_be_lduw_mmu, [MO_UL] = helper_be_ldul_mmu, [MO_UQ] = helper_be_ldq_mmu, @@ -1316,7 +1316,7 @@ static void * const qemu_ld_helpers[MO_SSIZE + 1] = { */ static void * const qemu_st_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_stb_mmu, -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN [MO_16] = helper_be_stw_mmu, [MO_32] = helper_be_stl_mmu, [MO_64] = helper_be_stq_mmu, @@ -1689,8 +1689,21 @@ static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, /* LDRD requires alignment; double-check that. */ if (get_alignment_bits(opc) >= MO_64 && (datalo & 1) == 0 && datahi == datalo + 1) { - tcg_out_ldrd_r(s, COND_AL, datalo, addrlo, addend); - } else if (scratch_addend) { + /* + * Rm (the second address op) must not overlap Rt or Rt + 1. + * Since datalo is aligned, we can simplify the test via alignment. + * Flip the two address arguments if that works. + */ + if ((addend & ~1) != datalo) { + tcg_out_ldrd_r(s, COND_AL, datalo, addrlo, addend); + break; + } + if ((addrlo & ~1) != datalo) { + tcg_out_ldrd_r(s, COND_AL, datalo, addend, addrlo); + break; + } + } + if (scratch_addend) { tcg_out_ld32_rwb(s, COND_AL, datalo, addend, addrlo); tcg_out_ld32_12(s, COND_AL, datahi, addend, 4); } else { diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 27c27a1f14..7e96495392 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -130,6 +130,9 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_andc_vec 1 #define TCG_TARGET_HAS_orc_vec 1 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 #define TCG_TARGET_HAS_not_vec 1 #define TCG_TARGET_HAS_neg_vec 1 #define TCG_TARGET_HAS_abs_vec 1 diff --git a/tcg/i386/tcg-target-con-set.h b/tcg/i386/tcg-target-con-set.h index 78774d1005..91ceb0e1da 100644 --- a/tcg/i386/tcg-target-con-set.h +++ b/tcg/i386/tcg-target-con-set.h @@ -45,6 +45,7 @@ C_O1_I2(r, r, rI) C_O1_I2(x, x, x) C_N1_I2(r, r, r) C_N1_I2(r, r, rW) +C_O1_I3(x, 0, x, x) C_O1_I3(x, x, x, x) C_O1_I4(r, r, re, r, 0) C_O1_I4(r, r, r, ri, ri) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index faa15eecab..b5c6159853 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -171,6 +171,10 @@ bool have_bmi1; bool have_popcnt; bool have_avx1; bool have_avx2; +bool have_avx512bw; +bool have_avx512dq; +bool have_avx512vbmi2; +bool have_avx512vl; bool have_movbe; #ifdef CONFIG_CPUID_H @@ -258,6 +262,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define P_SIMDF3 0x20000 /* 0xf3 opcode prefix */ #define P_SIMDF2 0x40000 /* 0xf2 opcode prefix */ #define P_VEXL 0x80000 /* Set VEX.L = 1 */ +#define P_EVEX 0x100000 /* Requires EVEX encoding */ #define OPC_ARITH_EvIz (0x81) #define OPC_ARITH_EvIb (0x83) @@ -308,6 +313,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_PABSB (0x1c | P_EXT38 | P_DATA16) #define OPC_PABSW (0x1d | P_EXT38 | P_DATA16) #define OPC_PABSD (0x1e | P_EXT38 | P_DATA16) +#define OPC_VPABSQ (0x1f | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_PACKSSDW (0x6b | P_EXT | P_DATA16) #define OPC_PACKSSWB (0x63 | P_EXT | P_DATA16) #define OPC_PACKUSDW (0x2b | P_EXT38 | P_DATA16) @@ -334,15 +340,19 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_PMAXSB (0x3c | P_EXT38 | P_DATA16) #define OPC_PMAXSW (0xee | P_EXT | P_DATA16) #define OPC_PMAXSD (0x3d | P_EXT38 | P_DATA16) +#define OPC_VPMAXSQ (0x3d | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_PMAXUB (0xde | P_EXT | P_DATA16) #define OPC_PMAXUW (0x3e | P_EXT38 | P_DATA16) #define OPC_PMAXUD (0x3f | P_EXT38 | P_DATA16) +#define OPC_VPMAXUQ (0x3f | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_PMINSB (0x38 | P_EXT38 | P_DATA16) #define OPC_PMINSW (0xea | P_EXT | P_DATA16) #define OPC_PMINSD (0x39 | P_EXT38 | P_DATA16) +#define OPC_VPMINSQ (0x39 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_PMINUB (0xda | P_EXT | P_DATA16) #define OPC_PMINUW (0x3a | P_EXT38 | P_DATA16) #define OPC_PMINUD (0x3b | P_EXT38 | P_DATA16) +#define OPC_VPMINUQ (0x3b | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_PMOVSXBW (0x20 | P_EXT38 | P_DATA16) #define OPC_PMOVSXWD (0x23 | P_EXT38 | P_DATA16) #define OPC_PMOVSXDQ (0x25 | P_EXT38 | P_DATA16) @@ -351,19 +361,21 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_PMOVZXDQ (0x35 | P_EXT38 | P_DATA16) #define OPC_PMULLW (0xd5 | P_EXT | P_DATA16) #define OPC_PMULLD (0x40 | P_EXT38 | P_DATA16) +#define OPC_VPMULLQ (0x40 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_POR (0xeb | P_EXT | P_DATA16) #define OPC_PSHUFB (0x00 | P_EXT38 | P_DATA16) #define OPC_PSHUFD (0x70 | P_EXT | P_DATA16) #define OPC_PSHUFLW (0x70 | P_EXT | P_SIMDF2) #define OPC_PSHUFHW (0x70 | P_EXT | P_SIMDF3) #define OPC_PSHIFTW_Ib (0x71 | P_EXT | P_DATA16) /* /2 /6 /4 */ -#define OPC_PSHIFTD_Ib (0x72 | P_EXT | P_DATA16) /* /2 /6 /4 */ +#define OPC_PSHIFTD_Ib (0x72 | P_EXT | P_DATA16) /* /1 /2 /6 /4 */ #define OPC_PSHIFTQ_Ib (0x73 | P_EXT | P_DATA16) /* /2 /6 /4 */ #define OPC_PSLLW (0xf1 | P_EXT | P_DATA16) #define OPC_PSLLD (0xf2 | P_EXT | P_DATA16) #define OPC_PSLLQ (0xf3 | P_EXT | P_DATA16) #define OPC_PSRAW (0xe1 | P_EXT | P_DATA16) #define OPC_PSRAD (0xe2 | P_EXT | P_DATA16) +#define OPC_VPSRAQ (0x72 | P_EXT | P_DATA16 | P_VEXW | P_EVEX) #define OPC_PSRLW (0xd1 | P_EXT | P_DATA16) #define OPC_PSRLD (0xd2 | P_EXT | P_DATA16) #define OPC_PSRLQ (0xd3 | P_EXT | P_DATA16) @@ -414,11 +426,29 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_VPBROADCASTQ (0x59 | P_EXT38 | P_DATA16) #define OPC_VPERMQ (0x00 | P_EXT3A | P_DATA16 | P_VEXW) #define OPC_VPERM2I128 (0x46 | P_EXT3A | P_DATA16 | P_VEXL) +#define OPC_VPROLVD (0x15 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPROLVQ (0x15 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPRORVD (0x14 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPRORVQ (0x14 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSHLDW (0x70 | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSHLDD (0x71 | P_EXT3A | P_DATA16 | P_EVEX) +#define OPC_VPSHLDQ (0x71 | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSHLDVW (0x70 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSHLDVD (0x71 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPSHLDVQ (0x71 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSHRDVW (0x72 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSHRDVD (0x73 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPSHRDVQ (0x73 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSLLVW (0x12 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_VPSLLVD (0x47 | P_EXT38 | P_DATA16) #define OPC_VPSLLVQ (0x47 | P_EXT38 | P_DATA16 | P_VEXW) +#define OPC_VPSRAVW (0x11 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_VPSRAVD (0x46 | P_EXT38 | P_DATA16) +#define OPC_VPSRAVQ (0x46 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPSRLVW (0x10 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) #define OPC_VPSRLVD (0x45 | P_EXT38 | P_DATA16) #define OPC_VPSRLVQ (0x45 | P_EXT38 | P_DATA16 | P_VEXW) +#define OPC_VPTERNLOGQ (0x25 | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) #define OPC_VZEROUPPER (0x77 | P_EXT) #define OPC_XCHG_ax_r32 (0x90) @@ -622,9 +652,57 @@ static void tcg_out_vex_opc(TCGContext *s, int opc, int r, int v, tcg_out8(s, opc); } +static void tcg_out_evex_opc(TCGContext *s, int opc, int r, int v, + int rm, int index) +{ + /* The entire 4-byte evex prefix; with R' and V' set. */ + uint32_t p = 0x08041062; + int mm, pp; + + tcg_debug_assert(have_avx512vl); + + /* EVEX.mm */ + if (opc & P_EXT3A) { + mm = 3; + } else if (opc & P_EXT38) { + mm = 2; + } else if (opc & P_EXT) { + mm = 1; + } else { + g_assert_not_reached(); + } + + /* EVEX.pp */ + if (opc & P_DATA16) { + pp = 1; /* 0x66 */ + } else if (opc & P_SIMDF3) { + pp = 2; /* 0xf3 */ + } else if (opc & P_SIMDF2) { + pp = 3; /* 0xf2 */ + } else { + pp = 0; + } + + p = deposit32(p, 8, 2, mm); + p = deposit32(p, 13, 1, (rm & 8) == 0); /* EVEX.RXB.B */ + p = deposit32(p, 14, 1, (index & 8) == 0); /* EVEX.RXB.X */ + p = deposit32(p, 15, 1, (r & 8) == 0); /* EVEX.RXB.R */ + p = deposit32(p, 16, 2, pp); + p = deposit32(p, 19, 4, ~v); + p = deposit32(p, 23, 1, (opc & P_VEXW) != 0); + p = deposit32(p, 29, 2, (opc & P_VEXL) != 0); + + tcg_out32(s, p); + tcg_out8(s, opc); +} + static void tcg_out_vex_modrm(TCGContext *s, int opc, int r, int v, int rm) { - tcg_out_vex_opc(s, opc, r, v, rm, 0); + if (opc & P_EVEX) { + tcg_out_evex_opc(s, opc, r, v, rm, 0); + } else { + tcg_out_vex_opc(s, opc, r, v, rm, 0); + } tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); } @@ -2746,7 +2824,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, OPC_PSUBUB, OPC_PSUBUW, OPC_UD2, OPC_UD2 }; static int const mul_insn[4] = { - OPC_UD2, OPC_PMULLW, OPC_PMULLD, OPC_UD2 + OPC_UD2, OPC_PMULLW, OPC_PMULLD, OPC_VPMULLQ }; static int const shift_imm_insn[4] = { OPC_UD2, OPC_PSHIFTW_Ib, OPC_PSHIFTD_Ib, OPC_PSHIFTQ_Ib @@ -2770,28 +2848,31 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, OPC_PACKUSWB, OPC_PACKUSDW, OPC_UD2, OPC_UD2 }; static int const smin_insn[4] = { - OPC_PMINSB, OPC_PMINSW, OPC_PMINSD, OPC_UD2 + OPC_PMINSB, OPC_PMINSW, OPC_PMINSD, OPC_VPMINSQ }; static int const smax_insn[4] = { - OPC_PMAXSB, OPC_PMAXSW, OPC_PMAXSD, OPC_UD2 + OPC_PMAXSB, OPC_PMAXSW, OPC_PMAXSD, OPC_VPMAXSQ }; static int const umin_insn[4] = { - OPC_PMINUB, OPC_PMINUW, OPC_PMINUD, OPC_UD2 + OPC_PMINUB, OPC_PMINUW, OPC_PMINUD, OPC_VPMINUQ }; static int const umax_insn[4] = { - OPC_PMAXUB, OPC_PMAXUW, OPC_PMAXUD, OPC_UD2 + OPC_PMAXUB, OPC_PMAXUW, OPC_PMAXUD, OPC_VPMAXUQ + }; + static int const rotlv_insn[4] = { + OPC_UD2, OPC_UD2, OPC_VPROLVD, OPC_VPROLVQ + }; + static int const rotrv_insn[4] = { + OPC_UD2, OPC_UD2, OPC_VPRORVD, OPC_VPRORVQ }; static int const shlv_insn[4] = { - /* TODO: AVX512 adds support for MO_16. */ - OPC_UD2, OPC_UD2, OPC_VPSLLVD, OPC_VPSLLVQ + OPC_UD2, OPC_VPSLLVW, OPC_VPSLLVD, OPC_VPSLLVQ }; static int const shrv_insn[4] = { - /* TODO: AVX512 adds support for MO_16. */ - OPC_UD2, OPC_UD2, OPC_VPSRLVD, OPC_VPSRLVQ + OPC_UD2, OPC_VPSRLVW, OPC_VPSRLVD, OPC_VPSRLVQ }; static int const sarv_insn[4] = { - /* TODO: AVX512 adds support for MO_16, MO_64. */ - OPC_UD2, OPC_UD2, OPC_VPSRAVD, OPC_UD2 + OPC_UD2, OPC_VPSRAVW, OPC_VPSRAVD, OPC_VPSRAVQ }; static int const shls_insn[4] = { OPC_UD2, OPC_PSLLW, OPC_PSLLD, OPC_PSLLQ @@ -2800,16 +2881,24 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, OPC_UD2, OPC_PSRLW, OPC_PSRLD, OPC_PSRLQ }; static int const sars_insn[4] = { - OPC_UD2, OPC_PSRAW, OPC_PSRAD, OPC_UD2 + OPC_UD2, OPC_PSRAW, OPC_PSRAD, OPC_VPSRAQ + }; + static int const vpshldi_insn[4] = { + OPC_UD2, OPC_VPSHLDW, OPC_VPSHLDD, OPC_VPSHLDQ + }; + static int const vpshldv_insn[4] = { + OPC_UD2, OPC_VPSHLDVW, OPC_VPSHLDVD, OPC_VPSHLDVQ + }; + static int const vpshrdv_insn[4] = { + OPC_UD2, OPC_VPSHRDVW, OPC_VPSHRDVD, OPC_VPSHRDVQ }; static int const abs_insn[4] = { - /* TODO: AVX512 adds support for MO_64. */ - OPC_PABSB, OPC_PABSW, OPC_PABSD, OPC_UD2 + OPC_PABSB, OPC_PABSW, OPC_PABSD, OPC_VPABSQ }; TCGType type = vecl + TCG_TYPE_V64; int insn, sub; - TCGArg a0, a1, a2; + TCGArg a0, a1, a2, a3; a0 = args[0]; a1 = args[1]; @@ -2867,6 +2956,12 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_sarv_vec: insn = sarv_insn[vece]; goto gen_simd; + case INDEX_op_rotlv_vec: + insn = rotlv_insn[vece]; + goto gen_simd; + case INDEX_op_rotrv_vec: + insn = rotrv_insn[vece]; + goto gen_simd; case INDEX_op_shls_vec: insn = shls_insn[vece]; goto gen_simd; @@ -2888,6 +2983,16 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_x86_packus_vec: insn = packus_insn[vece]; goto gen_simd; + case INDEX_op_x86_vpshldv_vec: + insn = vpshldv_insn[vece]; + a1 = a2; + a2 = args[3]; + goto gen_simd; + case INDEX_op_x86_vpshrdv_vec: + insn = vpshrdv_insn[vece]; + a1 = a2; + a2 = args[3]; + goto gen_simd; #if TCG_TARGET_REG_BITS == 32 case INDEX_op_dup2_vec: /* First merge the two 32-bit inputs to a single 64-bit element. */ @@ -2931,17 +3036,30 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_shli_vec: + insn = shift_imm_insn[vece]; sub = 6; goto gen_shift; case INDEX_op_shri_vec: + insn = shift_imm_insn[vece]; sub = 2; goto gen_shift; case INDEX_op_sari_vec: - tcg_debug_assert(vece != MO_64); + if (vece == MO_64) { + insn = OPC_PSHIFTD_Ib | P_VEXW | P_EVEX; + } else { + insn = shift_imm_insn[vece]; + } sub = 4; + goto gen_shift; + case INDEX_op_rotli_vec: + insn = OPC_PSHIFTD_Ib | P_EVEX; /* VPROL[DQ] */ + if (vece == MO_64) { + insn |= P_VEXW; + } + sub = 1; + goto gen_shift; gen_shift: tcg_debug_assert(vece != MO_8); - insn = shift_imm_insn[vece]; if (type == TCG_TYPE_V256) { insn |= P_VEXL; } @@ -2977,7 +3095,51 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, insn = OPC_VPERM2I128; sub = args[3]; goto gen_simd_imm8; + case INDEX_op_x86_vpshldi_vec: + insn = vpshldi_insn[vece]; + sub = args[3]; + goto gen_simd_imm8; + + case INDEX_op_not_vec: + insn = OPC_VPTERNLOGQ; + a2 = a1; + sub = 0x33; /* !B */ + goto gen_simd_imm8; + case INDEX_op_nor_vec: + insn = OPC_VPTERNLOGQ; + sub = 0x11; /* norCB */ + goto gen_simd_imm8; + case INDEX_op_nand_vec: + insn = OPC_VPTERNLOGQ; + sub = 0x77; /* nandCB */ + goto gen_simd_imm8; + case INDEX_op_eqv_vec: + insn = OPC_VPTERNLOGQ; + sub = 0x99; /* xnorCB */ + goto gen_simd_imm8; + case INDEX_op_orc_vec: + insn = OPC_VPTERNLOGQ; + sub = 0xdd; /* orB!C */ + goto gen_simd_imm8; + + case INDEX_op_bitsel_vec: + insn = OPC_VPTERNLOGQ; + a3 = args[3]; + if (a0 == a1) { + a1 = a2; + a2 = a3; + sub = 0xca; /* A?B:C */ + } else if (a0 == a2) { + a2 = a3; + sub = 0xe2; /* B?A:C */ + } else { + tcg_out_mov(s, type, a0, a3); + sub = 0xb8; /* B?C:A */ + } + goto gen_simd_imm8; + gen_simd_imm8: + tcg_debug_assert(insn != OPC_UD2); if (type == TCG_TYPE_V256) { insn |= P_VEXL; } @@ -3196,6 +3358,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_or_vec: case INDEX_op_xor_vec: case INDEX_op_andc_vec: + case INDEX_op_orc_vec: + case INDEX_op_nand_vec: + case INDEX_op_nor_vec: + case INDEX_op_eqv_vec: case INDEX_op_ssadd_vec: case INDEX_op_usadd_vec: case INDEX_op_sssub_vec: @@ -3207,10 +3373,11 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_shlv_vec: case INDEX_op_shrv_vec: case INDEX_op_sarv_vec: + case INDEX_op_rotlv_vec: + case INDEX_op_rotrv_vec: case INDEX_op_shls_vec: case INDEX_op_shrs_vec: case INDEX_op_sars_vec: - case INDEX_op_rotls_vec: case INDEX_op_cmp_vec: case INDEX_op_x86_shufps_vec: case INDEX_op_x86_blend_vec: @@ -3219,6 +3386,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_x86_vperm2i128_vec: case INDEX_op_x86_punpckl_vec: case INDEX_op_x86_punpckh_vec: + case INDEX_op_x86_vpshldi_vec: #if TCG_TARGET_REG_BITS == 32 case INDEX_op_dup2_vec: #endif @@ -3226,12 +3394,19 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_abs_vec: case INDEX_op_dup_vec: + case INDEX_op_not_vec: case INDEX_op_shli_vec: case INDEX_op_shri_vec: case INDEX_op_sari_vec: + case INDEX_op_rotli_vec: case INDEX_op_x86_psrldq_vec: return C_O1_I1(x, x); + case INDEX_op_x86_vpshldv_vec: + case INDEX_op_x86_vpshrdv_vec: + return C_O1_I3(x, 0, x, x); + + case INDEX_op_bitsel_vec: case INDEX_op_x86_vpblendvb_vec: return C_O1_I3(x, x, x, x); @@ -3249,53 +3424,96 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_or_vec: case INDEX_op_xor_vec: case INDEX_op_andc_vec: + case INDEX_op_orc_vec: + case INDEX_op_nand_vec: + case INDEX_op_nor_vec: + case INDEX_op_eqv_vec: + case INDEX_op_not_vec: + case INDEX_op_bitsel_vec: return 1; - case INDEX_op_rotli_vec: case INDEX_op_cmp_vec: case INDEX_op_cmpsel_vec: return -1; + case INDEX_op_rotli_vec: + return have_avx512vl && vece >= MO_32 ? 1 : -1; + case INDEX_op_shli_vec: case INDEX_op_shri_vec: /* We must expand the operation for MO_8. */ return vece == MO_8 ? -1 : 1; case INDEX_op_sari_vec: - /* We must expand the operation for MO_8. */ - if (vece == MO_8) { + switch (vece) { + case MO_8: return -1; - } - /* We can emulate this for MO_64, but it does not pay off - unless we're producing at least 4 values. */ - if (vece == MO_64) { + case MO_16: + case MO_32: + return 1; + case MO_64: + if (have_avx512vl) { + return 1; + } + /* + * We can emulate this for MO_64, but it does not pay off + * unless we're producing at least 4 values. + */ return type >= TCG_TYPE_V256 ? -1 : 0; } - return 1; + return 0; case INDEX_op_shls_vec: case INDEX_op_shrs_vec: return vece >= MO_16; case INDEX_op_sars_vec: - return vece >= MO_16 && vece <= MO_32; + switch (vece) { + case MO_16: + case MO_32: + return 1; + case MO_64: + return have_avx512vl; + } + return 0; case INDEX_op_rotls_vec: return vece >= MO_16 ? -1 : 0; case INDEX_op_shlv_vec: case INDEX_op_shrv_vec: - return have_avx2 && vece >= MO_32; + switch (vece) { + case MO_16: + return have_avx512bw; + case MO_32: + case MO_64: + return have_avx2; + } + return 0; case INDEX_op_sarv_vec: - return have_avx2 && vece == MO_32; + switch (vece) { + case MO_16: + return have_avx512bw; + case MO_32: + return have_avx2; + case MO_64: + return have_avx512vl; + } + return 0; case INDEX_op_rotlv_vec: case INDEX_op_rotrv_vec: - return have_avx2 && vece >= MO_32 ? -1 : 0; + switch (vece) { + case MO_16: + return have_avx512vbmi2 ? -1 : 0; + case MO_32: + case MO_64: + return have_avx512vl ? 1 : have_avx2 ? -1 : 0; + } + return 0; case INDEX_op_mul_vec: - if (vece == MO_8) { - /* We can expand the operation for MO_8. */ + switch (vece) { + case MO_8: return -1; - } - if (vece == MO_64) { - return 0; + case MO_64: + return have_avx512dq; } return 1; @@ -3309,7 +3527,7 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_umin_vec: case INDEX_op_umax_vec: case INDEX_op_abs_vec: - return vece <= MO_32; + return vece <= MO_32 || have_avx512vl; default: return 0; @@ -3427,6 +3645,12 @@ static void expand_vec_rotli(TCGType type, unsigned vece, return; } + if (have_avx512vbmi2) { + vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece, + tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v1), imm); + return; + } + t = tcg_temp_new_vec(type); tcg_gen_shli_vec(vece, t, v1, imm); tcg_gen_shri_vec(vece, v0, v1, (8 << vece) - imm); @@ -3434,31 +3658,19 @@ static void expand_vec_rotli(TCGType type, unsigned vece, tcg_temp_free_vec(t); } -static void expand_vec_rotls(TCGType type, unsigned vece, - TCGv_vec v0, TCGv_vec v1, TCGv_i32 lsh) -{ - TCGv_i32 rsh; - TCGv_vec t; - - tcg_debug_assert(vece != MO_8); - - t = tcg_temp_new_vec(type); - rsh = tcg_temp_new_i32(); - - tcg_gen_neg_i32(rsh, lsh); - tcg_gen_andi_i32(rsh, rsh, (8 << vece) - 1); - tcg_gen_shls_vec(vece, t, v1, lsh); - tcg_gen_shrs_vec(vece, v0, v1, rsh); - tcg_gen_or_vec(vece, v0, v0, t); - tcg_temp_free_vec(t); - tcg_temp_free_i32(rsh); -} - static void expand_vec_rotv(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGv_vec sh, bool right) { - TCGv_vec t = tcg_temp_new_vec(type); + TCGv_vec t; + if (have_avx512vbmi2) { + vec_gen_4(right ? INDEX_op_x86_vpshrdv_vec : INDEX_op_x86_vpshldv_vec, + type, vece, tcgv_vec_arg(v0), tcgv_vec_arg(v1), + tcgv_vec_arg(v1), tcgv_vec_arg(sh)); + return; + } + + t = tcg_temp_new_vec(type); tcg_gen_dupi_vec(vece, t, 8 << vece); tcg_gen_sub_vec(vece, t, t, sh); if (right) { @@ -3472,6 +3684,35 @@ static void expand_vec_rotv(TCGType type, unsigned vece, TCGv_vec v0, tcg_temp_free_vec(t); } +static void expand_vec_rotls(TCGType type, unsigned vece, + TCGv_vec v0, TCGv_vec v1, TCGv_i32 lsh) +{ + TCGv_vec t = tcg_temp_new_vec(type); + + tcg_debug_assert(vece != MO_8); + + if (vece >= MO_32 ? have_avx512vl : have_avx512vbmi2) { + tcg_gen_dup_i32_vec(vece, t, lsh); + if (vece >= MO_32) { + tcg_gen_rotlv_vec(vece, v0, v1, t); + } else { + expand_vec_rotv(type, vece, v0, v1, t, false); + } + } else { + TCGv_i32 rsh = tcg_temp_new_i32(); + + tcg_gen_neg_i32(rsh, lsh); + tcg_gen_andi_i32(rsh, rsh, (8 << vece) - 1); + tcg_gen_shls_vec(vece, t, v1, lsh); + tcg_gen_shrs_vec(vece, v0, v1, rsh); + tcg_gen_or_vec(vece, v0, v0, t); + + tcg_temp_free_i32(rsh); + } + + tcg_temp_free_vec(t); +} + static void expand_vec_mul(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGv_vec v2) { @@ -3567,28 +3808,28 @@ static bool expand_vec_cmp_noinv(TCGType type, unsigned vece, TCGv_vec v0, fixup = NEED_SWAP | NEED_INV; break; case TCG_COND_LEU: - if (vece <= MO_32) { + if (tcg_can_emit_vec_op(INDEX_op_umin_vec, type, vece)) { fixup = NEED_UMIN; } else { fixup = NEED_BIAS | NEED_INV; } break; case TCG_COND_GTU: - if (vece <= MO_32) { + if (tcg_can_emit_vec_op(INDEX_op_umin_vec, type, vece)) { fixup = NEED_UMIN | NEED_INV; } else { fixup = NEED_BIAS; } break; case TCG_COND_GEU: - if (vece <= MO_32) { + if (tcg_can_emit_vec_op(INDEX_op_umax_vec, type, vece)) { fixup = NEED_UMAX; } else { fixup = NEED_BIAS | NEED_SWAP | NEED_INV; } break; case TCG_COND_LTU: - if (vece <= MO_32) { + if (tcg_can_emit_vec_op(INDEX_op_umax_vec, type, vece)) { fixup = NEED_UMAX | NEED_INV; } else { fixup = NEED_BIAS | NEED_SWAP; @@ -3839,12 +4080,12 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) static void tcg_target_init(TCGContext *s) { #ifdef CONFIG_CPUID_H - unsigned a, b, c, d, b7 = 0; + unsigned a, b, c, d, b7 = 0, c7 = 0; unsigned max = __get_cpuid_max(0, 0); if (max >= 7) { /* BMI1 is available on AMD Piledriver and Intel Haswell CPUs. */ - __cpuid_count(7, 0, a, b7, c, d); + __cpuid_count(7, 0, a, b7, c7, d); have_bmi1 = (b7 & bit_BMI) != 0; have_bmi2 = (b7 & bit_BMI2) != 0; } @@ -3874,6 +4115,22 @@ static void tcg_target_init(TCGContext *s) if ((xcrl & 6) == 6) { have_avx1 = (c & bit_AVX) != 0; have_avx2 = (b7 & bit_AVX2) != 0; + + /* + * There are interesting instructions in AVX512, so long + * as we have AVX512VL, which indicates support for EVEX + * on sizes smaller than 512 bits. We are required to + * check that OPMASK and all extended ZMM state are enabled + * even if we're not using them -- the insns will fault. + */ + if ((xcrl & 0xe0) == 0xe0 + && (b7 & bit_AVX512F) + && (b7 & bit_AVX512VL)) { + have_avx512vl = true; + have_avx512bw = (b7 & bit_AVX512BW) != 0; + have_avx512dq = (b7 & bit_AVX512DQ) != 0; + have_avx512vbmi2 = (c7 & bit_AVX512VBMI2) != 0; + } } } } diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 3b2c9437a0..00fcbe297d 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -103,6 +103,10 @@ extern bool have_bmi1; extern bool have_popcnt; extern bool have_avx1; extern bool have_avx2; +extern bool have_avx512bw; +extern bool have_avx512dq; +extern bool have_avx512vbmi2; +extern bool have_avx512vl; extern bool have_movbe; /* optional instructions */ @@ -184,20 +188,23 @@ extern bool have_movbe; #define TCG_TARGET_HAS_v256 have_avx2 #define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec 0 -#define TCG_TARGET_HAS_not_vec 0 +#define TCG_TARGET_HAS_orc_vec have_avx512vl +#define TCG_TARGET_HAS_nand_vec have_avx512vl +#define TCG_TARGET_HAS_nor_vec have_avx512vl +#define TCG_TARGET_HAS_eqv_vec have_avx512vl +#define TCG_TARGET_HAS_not_vec have_avx512vl #define TCG_TARGET_HAS_neg_vec 0 #define TCG_TARGET_HAS_abs_vec 1 -#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_roti_vec have_avx512vl #define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 +#define TCG_TARGET_HAS_rotv_vec have_avx512vl #define TCG_TARGET_HAS_shi_vec 1 #define TCG_TARGET_HAS_shs_vec 1 #define TCG_TARGET_HAS_shv_vec have_avx2 #define TCG_TARGET_HAS_mul_vec 1 #define TCG_TARGET_HAS_sat_vec 1 #define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 0 +#define TCG_TARGET_HAS_bitsel_vec have_avx512vl #define TCG_TARGET_HAS_cmpsel_vec -1 #define TCG_TARGET_deposit_i32_valid(ofs, len) \ diff --git a/tcg/i386/tcg-target.opc.h b/tcg/i386/tcg-target.opc.h index 1312941800..b5f403e35e 100644 --- a/tcg/i386/tcg-target.opc.h +++ b/tcg/i386/tcg-target.opc.h @@ -33,3 +33,6 @@ DEF(x86_psrldq_vec, 1, 1, 1, IMPLVEC) DEF(x86_vperm2i128_vec, 1, 2, 1, IMPLVEC) DEF(x86_punpckl_vec, 1, 2, 0, IMPLVEC) DEF(x86_punpckh_vec, 1, 2, 0, IMPLVEC) +DEF(x86_vpshldi_vec, 1, 2, 1, IMPLVEC) +DEF(x86_vpshldv_vec, 1, 3, 0, IMPLVEC) +DEF(x86_vpshrdv_vec, 1, 3, 0, IMPLVEC) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 993149d18a..bd76f0c97f 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -26,7 +26,7 @@ #include "../tcg-ldst.c.inc" -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN # define MIPS_BE 1 #else # define MIPS_BE 0 diff --git a/tcg/optimize.c b/tcg/optimize.c index e573000951..ae081ab29c 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -359,13 +359,13 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) CASE_OP_32_64_VEC(orc): return x | ~y; - CASE_OP_32_64(eqv): + CASE_OP_32_64_VEC(eqv): return ~(x ^ y); - CASE_OP_32_64(nand): + CASE_OP_32_64_VEC(nand): return ~(x & y); - CASE_OP_32_64(nor): + CASE_OP_32_64_VEC(nor): return ~(x | y); case INDEX_op_clz_i32: @@ -552,10 +552,10 @@ static bool do_constant_folding_cond_eq(TCGCond c) static int do_constant_folding_cond(TCGType type, TCGArg x, TCGArg y, TCGCond c) { - uint64_t xv = arg_info(x)->val; - uint64_t yv = arg_info(y)->val; - if (arg_is_const(x) && arg_is_const(y)) { + uint64_t xv = arg_info(x)->val; + uint64_t yv = arg_info(y)->val; + switch (type) { case TCG_TYPE_I32: return do_constant_folding_cond_32(xv, yv, c); @@ -567,7 +567,7 @@ static int do_constant_folding_cond(TCGType type, TCGArg x, } } else if (args_are_copies(x, y)) { return do_constant_folding_cond_eq(c); - } else if (arg_is_const(y) && yv == 0) { + } else if (arg_is_const(y) && arg_info(y)->val == 0) { switch (c) { case TCG_COND_LTU: return 0; @@ -2119,7 +2119,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_dup2_vec: done = fold_dup2(&ctx, op); break; - CASE_OP_32_64(eqv): + CASE_OP_32_64_VEC(eqv): done = fold_eqv(&ctx, op); break; CASE_OP_32_64(extract): @@ -2170,13 +2170,13 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(mulu2): done = fold_multiply2(&ctx, op); break; - CASE_OP_32_64(nand): + CASE_OP_32_64_VEC(nand): done = fold_nand(&ctx, op); break; CASE_OP_32_64(neg): done = fold_neg(&ctx, op); break; - CASE_OP_32_64(nor): + CASE_OP_32_64_VEC(nor): done = fold_nor(&ctx, op); break; CASE_OP_32_64_VEC(not): diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index dea24f23c4..cfcd121f9c 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1864,7 +1864,7 @@ void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, i1 = ADDIS | TAI(TCG_REG_TB, TCG_REG_TB, hi >> 16); i2 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, lo); } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN pair = (uint64_t)i1 << 32 | i2; #else pair = (uint64_t)i2 << 32 | i1; @@ -3122,6 +3122,9 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_xor_vec: case INDEX_op_andc_vec: case INDEX_op_not_vec: + case INDEX_op_nor_vec: + case INDEX_op_eqv_vec: + case INDEX_op_nand_vec: return 1; case INDEX_op_orc_vec: return have_isa_2_07; @@ -3232,7 +3235,7 @@ static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out_mem_long(s, 0, LVEBX, out, base, offset); } elt = extract32(offset, 0, 4); -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN elt ^= 15; #endif tcg_out32(s, VSPLTB | VRT(out) | VRB(out) | (elt << 16)); @@ -3245,7 +3248,7 @@ static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out_mem_long(s, 0, LVEHX, out, base, offset); } elt = extract32(offset, 1, 3); -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN elt ^= 7; #endif tcg_out32(s, VSPLTH | VRT(out) | VRB(out) | (elt << 16)); @@ -3258,7 +3261,7 @@ static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, tcg_debug_assert((offset & 3) == 0); tcg_out_mem_long(s, 0, LVEWX, out, base, offset); elt = extract32(offset, 2, 2); -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN elt ^= 3; #endif tcg_out32(s, VSPLTW | VRT(out) | VRB(out) | (elt << 16)); @@ -3272,7 +3275,7 @@ static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out_mem_long(s, 0, LVX, out, base, offset & -16); tcg_out_vsldoi(s, TCG_VEC_TMP1, out, out, 8); elt = extract32(offset, 3, 1); -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN elt = !elt; #endif if (elt) { @@ -3400,6 +3403,15 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_orc_vec: insn = VORC; break; + case INDEX_op_nand_vec: + insn = VNAND; + break; + case INDEX_op_nor_vec: + insn = VNOR; + break; + case INDEX_op_eqv_vec: + insn = VEQV; + break; case INDEX_op_cmp_vec: switch (args[3]) { @@ -3787,6 +3799,9 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_xor_vec: case INDEX_op_andc_vec: case INDEX_op_orc_vec: + case INDEX_op_nor_vec: + case INDEX_op_eqv_vec: + case INDEX_op_nand_vec: case INDEX_op_cmp_vec: case INDEX_op_ssadd_vec: case INDEX_op_sssub_vec: @@ -3987,3 +4002,9 @@ void tcg_register_jit(const void *buf, size_t buf_size) tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); } #endif /* __ELF__ */ +#undef VMULEUB +#undef VMULEUH +#undef VMULEUW +#undef VMULOUB +#undef VMULOUH +#undef VMULOUW diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index c775c97b61..e6cf72503f 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -162,6 +162,9 @@ extern bool have_vsx; #define TCG_TARGET_HAS_andc_vec 1 #define TCG_TARGET_HAS_orc_vec have_isa_2_07 +#define TCG_TARGET_HAS_nand_vec have_isa_2_07 +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec have_isa_2_07 #define TCG_TARGET_HAS_not_vec 1 #define TCG_TARGET_HAS_neg_vec have_isa_3_00 #define TCG_TARGET_HAS_abs_vec 0 diff --git a/tcg/region.c b/tcg/region.c index 72afb35738..71ea81d671 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -26,6 +26,7 @@ #include "qemu/units.h" #include "qemu/madvise.h" #include "qemu/mprotect.h" +#include "qemu/memalign.h" #include "qemu/cacheinfo.h" #include "qapi/error.h" #include "exec/exec-all.h" @@ -487,14 +488,14 @@ static int alloc_code_gen_buffer(size_t tb_size, int splitwx, Error **errp) /* page-align the beginning and end of the buffer */ buf = static_code_gen_buffer; end = static_code_gen_buffer + sizeof(static_code_gen_buffer); - buf = QEMU_ALIGN_PTR_UP(buf, qemu_real_host_page_size); - end = QEMU_ALIGN_PTR_DOWN(end, qemu_real_host_page_size); + buf = QEMU_ALIGN_PTR_UP(buf, qemu_real_host_page_size()); + end = QEMU_ALIGN_PTR_DOWN(end, qemu_real_host_page_size()); size = end - buf; /* Honor a command-line option limiting the size of the buffer. */ if (size > tb_size) { - size = QEMU_ALIGN_DOWN(tb_size, qemu_real_host_page_size); + size = QEMU_ALIGN_DOWN(tb_size, qemu_real_host_page_size()); } region.start_aligned = buf; @@ -728,7 +729,7 @@ static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) */ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) { - const size_t page_size = qemu_real_host_page_size; + const size_t page_size = qemu_real_host_page_size(); size_t region_size; int have_prot, need_prot; diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 6409d9c3d5..81a83e45b1 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -854,7 +854,7 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) static void * const qemu_ld_helpers[MO_SSIZE + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN [MO_UW] = helper_be_lduw_mmu, [MO_SW] = helper_be_ldsw_mmu, [MO_UL] = helper_be_ldul_mmu, @@ -879,7 +879,7 @@ static void * const qemu_ld_helpers[MO_SSIZE + 1] = { */ static void * const qemu_st_helpers[MO_SIZE + 1] = { [MO_8] = helper_ret_stb_mmu, -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN [MO_16] = helper_be_stw_mmu, [MO_32] = helper_be_stl_mmu, [MO_64] = helper_be_stq_mmu, diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index d56c1e51e4..33becd7694 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -290,7 +290,9 @@ typedef enum S390Opcode { VRRc_VMXL = 0xe7fd, VRRc_VN = 0xe768, VRRc_VNC = 0xe769, + VRRc_VNN = 0xe76e, VRRc_VNO = 0xe76b, + VRRc_VNX = 0xe76c, VRRc_VO = 0xe76a, VRRc_VOC = 0xe76f, VRRc_VPKS = 0xe797, /* we leave the m5 cs field 0 */ @@ -2673,6 +2675,7 @@ static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, if (vece == MO_64) { return true; } + src = dst; } /* @@ -2713,7 +2716,7 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, msb = clz32(val); lsb = 31 - ctz32(val); } - tcg_out_insn(s, VRIb, VGM, dst, lsb, msb, MO_32); + tcg_out_insn(s, VRIb, VGM, dst, msb, lsb, MO_32); return; } } else { @@ -2727,7 +2730,7 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, msb = clz64(val); lsb = 63 - ctz64(val); } - tcg_out_insn(s, VRIb, VGM, dst, lsb, msb, MO_64); + tcg_out_insn(s, VRIb, VGM, dst, msb, lsb, MO_64); return; } } @@ -2805,6 +2808,15 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_xor_vec: tcg_out_insn(s, VRRc, VX, a0, a1, a2, 0); break; + case INDEX_op_nand_vec: + tcg_out_insn(s, VRRc, VNN, a0, a1, a2, 0); + break; + case INDEX_op_nor_vec: + tcg_out_insn(s, VRRc, VNO, a0, a1, a2, 0); + break; + case INDEX_op_eqv_vec: + tcg_out_insn(s, VRRc, VNX, a0, a1, a2, 0); + break; case INDEX_op_shli_vec: tcg_out_insn(s, VRSa, VESL, a0, a2, TCG_REG_NONE, a1, vece); @@ -2857,7 +2869,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_bitsel_vec: - tcg_out_insn(s, VRRe, VSEL, a0, a1, a2, args[3]); + tcg_out_insn(s, VRRe, VSEL, a0, a2, args[3], a1); break; case INDEX_op_cmp_vec: @@ -2901,7 +2913,10 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_and_vec: case INDEX_op_andc_vec: case INDEX_op_bitsel_vec: + case INDEX_op_eqv_vec: + case INDEX_op_nand_vec: case INDEX_op_neg_vec: + case INDEX_op_nor_vec: case INDEX_op_not_vec: case INDEX_op_or_vec: case INDEX_op_orc_vec: @@ -3246,6 +3261,9 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_or_vec: case INDEX_op_orc_vec: case INDEX_op_xor_vec: + case INDEX_op_nand_vec: + case INDEX_op_nor_vec: + case INDEX_op_eqv_vec: case INDEX_op_cmp_vec: case INDEX_op_mul_vec: case INDEX_op_rotlv_vec: diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 69217d995b..23e2063667 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -145,6 +145,9 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_andc_vec 1 #define TCG_TARGET_HAS_orc_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_nand_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec HAVE_FACILITY(VECTOR_ENH1) #define TCG_TARGET_HAS_not_vec 1 #define TCG_TARGET_HAS_neg_vec 1 #define TCG_TARGET_HAS_abs_vec 1 diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index ffe55e908f..079a761b04 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -836,6 +836,30 @@ static void expand_4_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_temp_free_i32(t0); } +static void expand_4i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t oprsz, int32_t c, + void (*fni)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, + int32_t)) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + uint32_t i; + + for (i = 0; i < oprsz; i += 4) { + tcg_gen_ld_i32(t1, cpu_env, aofs + i); + tcg_gen_ld_i32(t2, cpu_env, bofs + i); + tcg_gen_ld_i32(t3, cpu_env, cofs + i); + fni(t0, t1, t2, t3, c); + tcg_gen_st_i32(t0, cpu_env, dofs + i); + } + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); +} + /* Expand OPSZ bytes worth of two-operand operations using i64 elements. */ static void expand_2_i64(uint32_t dofs, uint32_t aofs, uint32_t oprsz, bool load_dest, void (*fni)(TCGv_i64, TCGv_i64)) @@ -971,6 +995,30 @@ static void expand_4_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_temp_free_i64(t0); } +static void expand_4i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t oprsz, int64_t c, + void (*fni)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, + int64_t)) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + uint32_t i; + + for (i = 0; i < oprsz; i += 8) { + tcg_gen_ld_i64(t1, cpu_env, aofs + i); + tcg_gen_ld_i64(t2, cpu_env, bofs + i); + tcg_gen_ld_i64(t3, cpu_env, cofs + i); + fni(t0, t1, t2, t3, c); + tcg_gen_st_i64(t0, cpu_env, dofs + i); + } + tcg_temp_free_i64(t3); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t0); +} + /* Expand OPSZ bytes worth of two-operand operations using host vectors. */ static void expand_2_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t tysz, TCGType type, @@ -1121,6 +1169,35 @@ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_temp_free_vec(t0); } +/* + * Expand OPSZ bytes worth of four-vector operands and an immediate operand + * using host vectors. + */ +static void expand_4i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t cofs, uint32_t oprsz, + uint32_t tysz, TCGType type, int64_t c, + void (*fni)(unsigned, TCGv_vec, TCGv_vec, + TCGv_vec, TCGv_vec, int64_t)) +{ + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); + TCGv_vec t3 = tcg_temp_new_vec(type); + uint32_t i; + + for (i = 0; i < oprsz; i += tysz) { + tcg_gen_ld_vec(t1, cpu_env, aofs + i); + tcg_gen_ld_vec(t2, cpu_env, bofs + i); + tcg_gen_ld_vec(t3, cpu_env, cofs + i); + fni(vece, t0, t1, t2, t3, c); + tcg_gen_st_vec(t0, cpu_env, dofs + i); + } + tcg_temp_free_vec(t3); + tcg_temp_free_vec(t2); + tcg_temp_free_vec(t1); + tcg_temp_free_vec(t0); +} + /* Expand a vector two-operand operation. */ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) @@ -1533,6 +1610,75 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, } } +/* Expand a vector four-operand operation. */ +void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz, int64_t c, + const GVecGen4i *g) +{ + const TCGOpcode *this_list = g->opt_opc ? : vecop_list_empty; + const TCGOpcode *hold_list = tcg_swap_vecop_list(this_list); + TCGType type; + uint32_t some; + + check_size_align(oprsz, maxsz, dofs | aofs | bofs | cofs); + check_overlap_4(dofs, aofs, bofs, cofs, maxsz); + + type = 0; + if (g->fniv) { + type = choose_vector_type(g->opt_opc, g->vece, oprsz, g->prefer_i64); + } + switch (type) { + case TCG_TYPE_V256: + /* + * Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ + some = QEMU_ALIGN_DOWN(oprsz, 32); + expand_4i_vec(g->vece, dofs, aofs, bofs, cofs, some, + 32, TCG_TYPE_V256, c, g->fniv); + if (some == oprsz) { + break; + } + dofs += some; + aofs += some; + bofs += some; + cofs += some; + oprsz -= some; + maxsz -= some; + /* fallthru */ + case TCG_TYPE_V128: + expand_4i_vec(g->vece, dofs, aofs, bofs, cofs, oprsz, + 16, TCG_TYPE_V128, c, g->fniv); + break; + case TCG_TYPE_V64: + expand_4i_vec(g->vece, dofs, aofs, bofs, cofs, oprsz, + 8, TCG_TYPE_V64, c, g->fniv); + break; + + case 0: + if (g->fni8 && check_size_impl(oprsz, 8)) { + expand_4i_i64(dofs, aofs, bofs, cofs, oprsz, c, g->fni8); + } else if (g->fni4 && check_size_impl(oprsz, 4)) { + expand_4i_i32(dofs, aofs, bofs, cofs, oprsz, c, g->fni4); + } else { + assert(g->fno != NULL); + tcg_gen_gvec_4_ool(dofs, aofs, bofs, cofs, + oprsz, maxsz, c, g->fno); + oprsz = maxsz; + } + break; + + default: + g_assert_not_reached(); + } + tcg_swap_vecop_list(hold_list); + + if (oprsz < maxsz) { + expand_clr(dofs + oprsz, maxsz - oprsz); + } +} + /* * Expand specific vector operations. */ diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index faf30f9cdd..463dabf515 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -371,23 +371,32 @@ void tcg_gen_orc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) void tcg_gen_nand_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) { - /* TODO: Add TCG_TARGET_HAS_nand_vec when adding a backend supports it. */ - tcg_gen_and_vec(0, r, a, b); - tcg_gen_not_vec(0, r, r); + if (TCG_TARGET_HAS_nand_vec) { + vec_gen_op3(INDEX_op_nand_vec, 0, r, a, b); + } else { + tcg_gen_and_vec(0, r, a, b); + tcg_gen_not_vec(0, r, r); + } } void tcg_gen_nor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) { - /* TODO: Add TCG_TARGET_HAS_nor_vec when adding a backend supports it. */ - tcg_gen_or_vec(0, r, a, b); - tcg_gen_not_vec(0, r, r); + if (TCG_TARGET_HAS_nor_vec) { + vec_gen_op3(INDEX_op_nor_vec, 0, r, a, b); + } else { + tcg_gen_or_vec(0, r, a, b); + tcg_gen_not_vec(0, r, r); + } } void tcg_gen_eqv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) { - /* TODO: Add TCG_TARGET_HAS_eqv_vec when adding a backend supports it. */ - tcg_gen_xor_vec(0, r, a, b); - tcg_gen_not_vec(0, r, r); + if (TCG_TARGET_HAS_eqv_vec) { + vec_gen_op3(INDEX_op_eqv_vec, 0, r, a, b); + } else { + tcg_gen_xor_vec(0, r, a, b); + tcg_gen_not_vec(0, r, r); + } } static bool do_op2(unsigned vece, TCGv_vec r, TCGv_vec a, TCGOpcode opc) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index f7c067de6c..292222975b 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1056,6 +1056,12 @@ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) } } +void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + /* Swapping 2 16-bit elements is a rotate. */ + tcg_gen_rotli_i32(ret, arg, 16); +} + void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); @@ -1156,7 +1162,7 @@ void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { /* Since arg2 and ret have different types, they cannot be the same temporary */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); #else @@ -1167,7 +1173,7 @@ void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); #else @@ -1792,6 +1798,30 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) } } +void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + uint64_t m = 0x0000ffff0000ffffull; + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + /* See include/qemu/bitops.h, hswap64. */ + tcg_gen_rotli_i64(t1, arg, 32); + tcg_gen_andi_i64(t0, t1, m); + tcg_gen_shli_i64(t0, t0, 16); + tcg_gen_shri_i64(t1, t1, 16); + tcg_gen_andi_i64(t1, t1, m); + tcg_gen_or_i64(ret, t0, t1); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + /* Swapping 2 32-bit elements is a rotate. */ + tcg_gen_rotli_i64(ret, arg, 32); +} + void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 32) { diff --git a/tcg/tcg.c b/tcg/tcg.c index 754544595e..7b68244ebc 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -51,7 +51,7 @@ #else # define ELF_CLASS ELFCLASS64 #endif -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN # define ELF_DATA ELFDATA2MSB #else # define ELF_DATA ELFDATA2LSB @@ -320,7 +320,8 @@ static void set_jmp_reset_offset(TCGContext *s, int which) } /* Signal overflow, starting over with fewer guest insns. */ -static void QEMU_NORETURN tcg_raise_tb_overflow(TCGContext *s) +static G_NORETURN +void tcg_raise_tb_overflow(TCGContext *s) { siglongjmp(s->jmp_trans, -2); } @@ -766,32 +767,35 @@ void tcg_prologue_init(TCGContext *s) #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { - FILE *logfile = qemu_log_lock(); - qemu_log("PROLOGUE: [size=%zu]\n", prologue_size); - if (s->data_gen_ptr) { - size_t code_size = s->data_gen_ptr - s->code_gen_ptr; - size_t data_size = prologue_size - code_size; - size_t i; + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "PROLOGUE: [size=%zu]\n", prologue_size); + if (s->data_gen_ptr) { + size_t code_size = s->data_gen_ptr - s->code_gen_ptr; + size_t data_size = prologue_size - code_size; + size_t i; - log_disas(s->code_gen_ptr, code_size); + disas(logfile, s->code_gen_ptr, code_size); - for (i = 0; i < data_size; i += sizeof(tcg_target_ulong)) { - if (sizeof(tcg_target_ulong) == 8) { - qemu_log("0x%08" PRIxPTR ": .quad 0x%016" PRIx64 "\n", - (uintptr_t)s->data_gen_ptr + i, - *(uint64_t *)(s->data_gen_ptr + i)); - } else { - qemu_log("0x%08" PRIxPTR ": .long 0x%08x\n", - (uintptr_t)s->data_gen_ptr + i, - *(uint32_t *)(s->data_gen_ptr + i)); + for (i = 0; i < data_size; i += sizeof(tcg_target_ulong)) { + if (sizeof(tcg_target_ulong) == 8) { + fprintf(logfile, + "0x%08" PRIxPTR ": .quad 0x%016" PRIx64 "\n", + (uintptr_t)s->data_gen_ptr + i, + *(uint64_t *)(s->data_gen_ptr + i)); + } else { + fprintf(logfile, + "0x%08" PRIxPTR ": .long 0x%08x\n", + (uintptr_t)s->data_gen_ptr + i, + *(uint32_t *)(s->data_gen_ptr + i)); + } } + } else { + disas(logfile, s->code_gen_ptr, prologue_size); } - } else { - log_disas(s->code_gen_ptr, prologue_size); + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); } - qemu_log("\n"); - qemu_log_flush(); - qemu_log_unlock(logfile); } #endif @@ -893,7 +897,7 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, TCGTemp *base_ts = tcgv_ptr_temp(base); TCGTemp *ts = tcg_global_alloc(s); int indirect_reg = 0, bigendian = 0; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN bigendian = 1; #endif @@ -1034,9 +1038,18 @@ void tcg_temp_free_internal(TCGTemp *ts) TCGContext *s = tcg_ctx; int k, idx; - /* In order to simplify users of tcg_constant_*, silently ignore free. */ - if (ts->kind == TEMP_CONST) { + switch (ts->kind) { + case TEMP_CONST: + /* + * In order to simplify users of tcg_constant_*, + * silently ignore free. + */ return; + case TEMP_NORMAL: + case TEMP_LOCAL: + break; + default: + g_assert_not_reached(); } #if defined(CONFIG_DEBUG_TCG) @@ -1046,7 +1059,6 @@ void tcg_temp_free_internal(TCGTemp *ts) } #endif - tcg_debug_assert(ts->kind < TEMP_GLOBAL); tcg_debug_assert(ts->temp_allocated != 0); ts->temp_allocated = 0; @@ -1417,6 +1429,12 @@ bool tcg_op_supported(TCGOpcode op) return have_vec && TCG_TARGET_HAS_andc_vec; case INDEX_op_orc_vec: return have_vec && TCG_TARGET_HAS_orc_vec; + case INDEX_op_nand_vec: + return have_vec && TCG_TARGET_HAS_nand_vec; + case INDEX_op_nor_vec: + return have_vec && TCG_TARGET_HAS_nor_vec; + case INDEX_op_eqv_vec: + return have_vec && TCG_TARGET_HAS_eqv_vec; case INDEX_op_mul_vec: return have_vec && TCG_TARGET_HAS_mul_vec; case INDEX_op_shli_vec: @@ -1551,7 +1569,7 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args) } #else if (TCG_TARGET_REG_BITS < 64 && (typemask & 6) == dh_typecode_i64) { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN op->args[pi++] = temp_arg(ret + 1); op->args[pi++] = temp_arg(ret); #else @@ -1604,7 +1622,7 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args) * have to get more complicated to differentiate between * stack arguments and register arguments. */ -#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP) +#if HOST_BIG_ENDIAN != defined(TCG_TARGET_STACK_GROWSUP) op->args[pi++] = temp_arg(args[i] + 1); op->args[pi++] = temp_arg(args[i]); #else @@ -1678,6 +1696,7 @@ static void tcg_reg_alloc_start(TCGContext *s) case TEMP_GLOBAL: break; case TEMP_NORMAL: + case TEMP_EBB: val = TEMP_VAL_DEAD; /* fall through */ case TEMP_LOCAL: @@ -1705,6 +1724,9 @@ static char *tcg_get_arg_str_ptr(TCGContext *s, char *buf, int buf_size, case TEMP_LOCAL: snprintf(buf, buf_size, "loc%d", idx - s->nb_globals); break; + case TEMP_EBB: + snprintf(buf, buf_size, "ebb%d", idx - s->nb_globals); + break; case TEMP_NORMAL: snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals); break; @@ -1808,7 +1830,11 @@ static inline TCGReg tcg_regset_first(TCGRegSet d) } } -static void tcg_dump_ops(TCGContext *s, bool have_prefs) +/* Return only the number of characters output -- no error return. */ +#define ne_fprintf(...) \ + ({ int ret_ = fprintf(__VA_ARGS__); ret_ >= 0 ? ret_ : 0; }) + +static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) { char buf[128]; TCGOp *op; @@ -1824,7 +1850,7 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) if (c == INDEX_op_insn_start) { nb_oargs = 0; - col += qemu_log("\n ----"); + col += ne_fprintf(f, "\n ----"); for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { target_ulong a; @@ -1833,7 +1859,7 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) #else a = op->args[i]; #endif - col += qemu_log(" " TARGET_FMT_lx, a); + col += ne_fprintf(f, " " TARGET_FMT_lx, a); } } else if (c == INDEX_op_call) { const TCGHelperInfo *info = tcg_call_info(op); @@ -1844,7 +1870,7 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) nb_iargs = TCGOP_CALLI(op); nb_cargs = def->nb_cargs; - col += qemu_log(" %s ", def->name); + col += ne_fprintf(f, " %s ", def->name); /* * Print the function name from TCGHelperInfo, if available. @@ -1852,15 +1878,15 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) * but the actual function pointer comes from the plugin. */ if (func == info->func) { - col += qemu_log("%s", info->name); + col += ne_fprintf(f, "%s", info->name); } else { - col += qemu_log("plugin(%p)", func); + col += ne_fprintf(f, "plugin(%p)", func); } - col += qemu_log(",$0x%x,$%d", info->flags, nb_oargs); + col += ne_fprintf(f, ",$0x%x,$%d", info->flags, nb_oargs); for (i = 0; i < nb_oargs; i++) { - col += qemu_log(",%s", tcg_get_arg_str(s, buf, sizeof(buf), - op->args[i])); + col += ne_fprintf(f, ",%s", tcg_get_arg_str(s, buf, sizeof(buf), + op->args[i])); } for (i = 0; i < nb_iargs; i++) { TCGArg arg = op->args[nb_oargs + i]; @@ -1868,34 +1894,32 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) if (arg != TCG_CALL_DUMMY_ARG) { t = tcg_get_arg_str(s, buf, sizeof(buf), arg); } - col += qemu_log(",%s", t); + col += ne_fprintf(f, ",%s", t); } } else { - col += qemu_log(" %s ", def->name); + col += ne_fprintf(f, " %s ", def->name); nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; nb_cargs = def->nb_cargs; if (def->flags & TCG_OPF_VECTOR) { - col += qemu_log("v%d,e%d,", 64 << TCGOP_VECL(op), - 8 << TCGOP_VECE(op)); + col += ne_fprintf(f, "v%d,e%d,", 64 << TCGOP_VECL(op), + 8 << TCGOP_VECE(op)); } k = 0; for (i = 0; i < nb_oargs; i++) { - if (k != 0) { - col += qemu_log(","); - } - col += qemu_log("%s", tcg_get_arg_str(s, buf, sizeof(buf), - op->args[k++])); + const char *sep = k ? "," : ""; + col += ne_fprintf(f, "%s%s", sep, + tcg_get_arg_str(s, buf, sizeof(buf), + op->args[k++])); } for (i = 0; i < nb_iargs; i++) { - if (k != 0) { - col += qemu_log(","); - } - col += qemu_log("%s", tcg_get_arg_str(s, buf, sizeof(buf), - op->args[k++])); + const char *sep = k ? "," : ""; + col += ne_fprintf(f, "%s%s", sep, + tcg_get_arg_str(s, buf, sizeof(buf), + op->args[k++])); } switch (c) { case INDEX_op_brcond_i32: @@ -1910,9 +1934,9 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) case INDEX_op_cmpsel_vec: if (op->args[k] < ARRAY_SIZE(cond_name) && cond_name[op->args[k]]) { - col += qemu_log(",%s", cond_name[op->args[k++]]); + col += ne_fprintf(f, ",%s", cond_name[op->args[k++]]); } else { - col += qemu_log(",$0x%" TCG_PRIlx, op->args[k++]); + col += ne_fprintf(f, ",$0x%" TCG_PRIlx, op->args[k++]); } i = 1; break; @@ -1927,12 +1951,12 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) unsigned ix = get_mmuidx(oi); if (op & ~(MO_AMASK | MO_BSWAP | MO_SSIZE)) { - col += qemu_log(",$0x%x,%u", op, ix); + col += ne_fprintf(f, ",$0x%x,%u", op, ix); } else { const char *s_al, *s_op; s_al = alignment_name[(op & MO_AMASK) >> MO_ASHIFT]; s_op = ldst_name[op & (MO_BSWAP | MO_SSIZE)]; - col += qemu_log(",%s%s,%u", s_al, s_op, ix); + col += ne_fprintf(f, ",%s%s,%u", s_al, s_op, ix); } i = 1; } @@ -1950,9 +1974,9 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) name = bswap_flag_name[flags]; } if (name) { - col += qemu_log(",%s", name); + col += ne_fprintf(f, ",%s", name); } else { - col += qemu_log(",$0x%" TCG_PRIlx, flags); + col += ne_fprintf(f, ",$0x%" TCG_PRIlx, flags); } i = k = 1; } @@ -1967,49 +1991,42 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: case INDEX_op_brcond2_i32: - col += qemu_log("%s$L%d", k ? "," : "", - arg_label(op->args[k])->id); + col += ne_fprintf(f, "%s$L%d", k ? "," : "", + arg_label(op->args[k])->id); i++, k++; break; default: break; } for (; i < nb_cargs; i++, k++) { - col += qemu_log("%s$0x%" TCG_PRIlx, k ? "," : "", op->args[k]); + col += ne_fprintf(f, "%s$0x%" TCG_PRIlx, k ? "," : "", + op->args[k]); } } if (have_prefs || op->life) { - - QemuLogFile *logfile; - - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - for (; col < 40; ++col) { - putc(' ', logfile->fd); - } + for (; col < 40; ++col) { + putc(' ', f); } - rcu_read_unlock(); } if (op->life) { unsigned life = op->life; if (life & (SYNC_ARG * 3)) { - qemu_log(" sync:"); + ne_fprintf(f, " sync:"); for (i = 0; i < 2; ++i) { if (life & (SYNC_ARG << i)) { - qemu_log(" %d", i); + ne_fprintf(f, " %d", i); } } } life /= DEAD_ARG; if (life) { - qemu_log(" dead:"); + ne_fprintf(f, " dead:"); for (i = 0; life; ++i, life >>= 1) { if (life & 1) { - qemu_log(" %d", i); + ne_fprintf(f, " %d", i); } } } @@ -2020,28 +2037,28 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) TCGRegSet set = op->output_pref[i]; if (i == 0) { - qemu_log(" pref="); + ne_fprintf(f, " pref="); } else { - qemu_log(","); + ne_fprintf(f, ","); } if (set == 0) { - qemu_log("none"); + ne_fprintf(f, "none"); } else if (set == MAKE_64BIT_MASK(0, TCG_TARGET_NB_REGS)) { - qemu_log("all"); + ne_fprintf(f, "all"); #ifdef CONFIG_DEBUG_TCG } else if (tcg_regset_single(set)) { TCGReg reg = tcg_regset_first(set); - qemu_log("%s", tcg_target_reg_names[reg]); + ne_fprintf(f, "%s", tcg_target_reg_names[reg]); #endif } else if (TCG_TARGET_NB_REGS <= 32) { - qemu_log("%#x", (uint32_t)set); + ne_fprintf(f, "0x%x", (uint32_t)set); } else { - qemu_log("%#" PRIx64, (uint64_t)set); + ne_fprintf(f, "0x%" PRIx64, (uint64_t)set); } } } - qemu_log("\n"); + putc('\n', f); } } @@ -2382,6 +2399,7 @@ static void la_bb_end(TCGContext *s, int ng, int nt) state = TS_DEAD | TS_MEM; break; case TEMP_NORMAL: + case TEMP_EBB: case TEMP_CONST: state = TS_DEAD; break; @@ -2409,8 +2427,9 @@ static void la_global_sync(TCGContext *s, int ng) } /* - * liveness analysis: conditional branch: all temps are dead, - * globals and local temps should be synced. + * liveness analysis: conditional branch: all temps are dead unless + * explicitly live-across-conditional-branch, globals and local temps + * should be synced. */ static void la_bb_sync(TCGContext *s, int ng, int nt) { @@ -2431,6 +2450,7 @@ static void la_bb_sync(TCGContext *s, int ng, int nt) case TEMP_NORMAL: s->temps[i].state = TS_DEAD; break; + case TEMP_EBB: case TEMP_CONST: continue; default: @@ -2801,6 +2821,7 @@ static bool liveness_pass_2(TCGContext *s) TCGTemp *dts = tcg_temp_alloc(s); dts->type = its->type; dts->base_type = its->base_type; + dts->kind = TEMP_EBB; its->state_ptr = dts; } else { its->state_ptr = NULL; @@ -3111,6 +3132,7 @@ static void temp_free_or_dead(TCGContext *s, TCGTemp *ts, int free_or_dead) new_type = TEMP_VAL_MEM; break; case TEMP_NORMAL: + case TEMP_EBB: new_type = free_or_dead < 0 ? TEMP_VAL_MEM : TEMP_VAL_DEAD; break; case TEMP_CONST: @@ -3357,6 +3379,7 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) temp_save(s, ts, allocated_regs); break; case TEMP_NORMAL: + case TEMP_EBB: /* The liveness analysis already ensures that temps are dead. Keep an tcg_debug_assert for safety. */ tcg_debug_assert(ts->val_type == TEMP_VAL_DEAD); @@ -3374,8 +3397,9 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) } /* - * At a conditional branch, we assume all temporaries are dead and - * all globals and local temps are synced to their location. + * At a conditional branch, we assume all temporaries are dead unless + * explicitly live-across-conditional-branch; all globals and local + * temps are synced to their location. */ static void tcg_reg_alloc_cbranch(TCGContext *s, TCGRegSet allocated_regs) { @@ -3394,6 +3418,7 @@ static void tcg_reg_alloc_cbranch(TCGContext *s, TCGRegSet allocated_regs) case TEMP_NORMAL: tcg_debug_assert(ts->val_type == TEMP_VAL_DEAD); break; + case TEMP_EBB: case TEMP_CONST: break; default: @@ -3602,7 +3627,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) /* fall through */ case TEMP_VAL_MEM: -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN endian_fixup = itype == TCG_TYPE_I32 ? 4 : 8; endian_fixup -= 1 << vece; #else @@ -3883,7 +3908,7 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) if (!itsh->mem_coherent) { temp_sync(s, itsh, s->reserved_regs, 0, 0); } -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN TCGTemp *its = itsh; #else TCGTemp *its = itsl; @@ -4204,11 +4229,13 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP) && qemu_log_in_addr_range(tb->pc))) { - FILE *logfile = qemu_log_lock(); - qemu_log("OP:\n"); - tcg_dump_ops(s, false); - qemu_log("\n"); - qemu_log_unlock(logfile); + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "OP:\n"); + tcg_dump_ops(s, logfile, false); + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); + } } #endif @@ -4249,11 +4276,13 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_IND) && qemu_log_in_addr_range(tb->pc))) { - FILE *logfile = qemu_log_lock(); - qemu_log("OP before indirect lowering:\n"); - tcg_dump_ops(s, false); - qemu_log("\n"); - qemu_log_unlock(logfile); + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "OP before indirect lowering:\n"); + tcg_dump_ops(s, logfile, false); + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); + } } #endif /* Replace indirect temps with direct temps. */ @@ -4270,11 +4299,13 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT) && qemu_log_in_addr_range(tb->pc))) { - FILE *logfile = qemu_log_lock(); - qemu_log("OP after optimization and liveness analysis:\n"); - tcg_dump_ops(s, true); - qemu_log("\n"); - qemu_log_unlock(logfile); + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "OP after optimization and liveness analysis:\n"); + tcg_dump_ops(s, logfile, true); + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); + } } #endif diff --git a/tcg/tci.c b/tcg/tci.c index fe92b5d084..bdfac83492 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "tcg/tcg.h" /* MAX_OPC_PARAM_IARGS */ #include "exec/cpu_ldst.h" #include "tcg/tcg-op.h" diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 0cb16aaa81..98337c567a 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -197,7 +197,7 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R0, }; -#if MAX_OPC_PARAM_IARGS != 6 +#if MAX_OPC_PARAM_IARGS != 7 # error Fix needed, number of supported input arguments changed! #endif @@ -790,14 +790,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset) { - stack_bounds_check(base, offset); switch (type) { case TCG_TYPE_I32: - tcg_out_op_rrs(s, INDEX_op_st_i32, val, base, offset); + tcg_out_ldst(s, INDEX_op_st_i32, val, base, offset); break; #if TCG_TARGET_REG_BITS == 64 case TCG_TYPE_I64: - tcg_out_op_rrs(s, INDEX_op_st_i64, val, base, offset); + tcg_out_ldst(s, INDEX_op_st_i64, val, base, offset); break; #endif default: diff --git a/tests/Makefile.include b/tests/Makefile.include index e7153c8e91..ec84b2ebc0 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -36,37 +36,39 @@ export SRC_PATH SPEED = quick -# Build up our target list from the filtered list of ninja targets -TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets))) +-include tests/tcg/Makefile.prereqs +config-host.mak: $(SRC_PATH)/tests/tcg/configure.sh +tests/tcg/Makefile.prereqs: config-host.mak # Per guest TCG tests -BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(TARGETS)) -CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(TARGETS)) -RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(TARGETS)) +BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(TCG_TESTS_TARGETS)) +CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(TCG_TESTS_TARGETS)) +RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(TCG_TESTS_TARGETS)) -# Probe for the Docker Builds needed for each build -$(foreach PROBE_TARGET,$(TARGET_DIRS), \ - $(eval -include $(SRC_PATH)/tests/tcg/Makefile.prereqs)) +$(foreach TARGET,$(TCG_TESTS_TARGETS), \ + $(eval $(BUILD_DIR)/tests/tcg/config-$(TARGET).mak: config-host.mak)) -$(BUILD_TCG_TARGET_RULES): build-tcg-tests-%: $(if $(CONFIG_PLUGIN),test-plugins) - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) \ - -f $(SRC_PATH)/tests/tcg/Makefile.qemu \ - SRC_PATH=$(SRC_PATH) \ - V="$(V)" TARGET="$*" guest-tests, \ - "BUILD", "TCG tests for $*") +.PHONY: $(TCG_TESTS_TARGETS:%=build-tcg-tests-%) +$(TCG_TESTS_TARGETS:%=build-tcg-tests-%): build-tcg-tests-%: $(BUILD_DIR)/tests/tcg/config-%.mak + $(call quiet-command, \ + $(MAKE) -C tests/tcg/$* -f ../Makefile.target $(SUBDIR_MAKEFLAGS) \ + DOCKER_SCRIPT="$(DOCKER_SCRIPT)" \ + TARGET="$*" SRC_PATH="$(SRC_PATH)", \ + "BUILD","$* guest-tests") -$(RUN_TCG_TARGET_RULES): run-tcg-tests-%: build-tcg-tests-% all - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) \ - -f $(SRC_PATH)/tests/tcg/Makefile.qemu \ - SRC_PATH=$(SRC_PATH) SPEED="$(SPEED)" \ - V="$(V)" TARGET="$*" run-guest-tests, \ - "RUN", "TCG tests for $*") +.PHONY: $(TCG_TESTS_TARGETS:%=run-tcg-tests-%) +$(TCG_TESTS_TARGETS:%=run-tcg-tests-%): run-tcg-tests-%: build-tcg-tests-% $(if $(CONFIG_PLUGIN),test-plugins) + $(call quiet-command, \ + $(MAKE) -C tests/tcg/$* -f ../Makefile.target $(SUBDIR_MAKEFLAGS) \ + TARGET="$*" SRC_PATH="$(SRC_PATH)" SPEED=$(SPEED) run, \ + "RUN", "$* guest-tests") -$(CLEAN_TCG_TARGET_RULES): clean-tcg-tests-%: - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) \ - -f $(SRC_PATH)/tests/tcg/Makefile.qemu \ - SRC_PATH=$(SRC_PATH) TARGET="$*" clean-guest-tests, \ - "CLEAN", "TCG tests for $*") +.PHONY: $(TCG_TESTS_TARGETS:%=clean-tcg-tests-%) +$(TCG_TESTS_TARGETS:%=clean-tcg-tests-%): clean-tcg-tests-%: + $(call quiet-command, \ + $(MAKE) -C tests/tcg/$* -f ../Makefile.target $(SUBDIR_MAKEFLAGS) \ + TARGET="$*" SRC_PATH="$(SRC_PATH)" clean, \ + "CLEAN", "$* guest-tests") .PHONY: build-tcg build-tcg: $(BUILD_TCG_TARGET_RULES) @@ -81,6 +83,9 @@ clean-tcg: $(CLEAN_TCG_TARGET_RULES) .PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning +# Build up our target list from the filtered list of ninja targets +TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets))) + TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results @@ -147,21 +152,14 @@ check-acceptance: check-acceptance-deprecated-warning | check-avocado # Consolidated targets -.PHONY: check-block check check-clean get-vm-images +.PHONY: check check-clean get-vm-images check: -ifneq ($(.check-block.deps),) -check: check-block -check-block: run-ninja - $(if $(MAKE.n),,+)$(MESON) test $(MTESTARGS) $(.mtestargs) --verbose \ - --logbase iotestslog $(call .speed.$(SPEED), block block-slow block-thorough) -endif - check-build: run-ninja check-clean: rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) -clean: check-clean +clean: check-clean clean-tcg endif diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index 75063c0c30..39f15c1d51 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -18,7 +18,7 @@ import time import uuid import avocado -from avocado.utils import cloudinit, datadrainer, network, process, ssh, vmimage +from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage from avocado.utils.path import find_command #: The QEMU build root directory. It may also be the source directory @@ -516,6 +516,8 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): distro = None username = 'root' password = 'password' + smp = '2' + memory = '1024' def _set_distro(self): distro_name = self.params.get( @@ -546,8 +548,8 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): def setUp(self, ssh_pubkey=None, network_device_type='virtio-net'): super().setUp() self._set_distro() - self.vm.add_args('-smp', '2') - self.vm.add_args('-m', '1024') + self.vm.add_args('-smp', self.smp) + self.vm.add_args('-m', self.memory) # The following network device allows for SSH connections self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', '-device', '%s,netdev=vnet' % network_device_type) @@ -602,7 +604,6 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): self.log.info('Preparing cloudinit image') try: cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') - self.phone_home_port = network.find_free_port() pubkey_content = None if ssh_pubkey: with open(ssh_pubkey) as pubkey: @@ -612,7 +613,7 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): password=self.password, # QEMU's hard coded usermode router address phone_home_host='10.0.2.2', - phone_home_port=self.phone_home_port, + phone_home_port=self.phone_server.server_port, authorized_key=pubkey_content) except Exception: self.cancel('Failed to prepare the cloudinit image') @@ -623,6 +624,8 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): self.vm.add_args('-drive', 'file=%s' % path) def set_up_cloudinit(self, ssh_pubkey=None): + self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), + self.name) cloudinit_iso = self.prepare_cloudinit(ssh_pubkey) self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) @@ -633,7 +636,9 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): logger=self.log.getChild('console')) console_drainer.start() self.log.info('VM launched, waiting for boot confirmation from guest') - cloudinit.wait_for_phone_home(('0.0.0.0', self.phone_home_port), self.name) + while not self.phone_server.instance_phoned_back: + self.phone_server.handle_request() + if set_up_ssh_connection: self.log.info('Setting up the SSH connection') self.ssh_connect(self.username, self.ssh_key) diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py index ab19146d1e..ee584d2fdf 100644 --- a/tests/avocado/boot_linux.py +++ b/tests/avocado/boot_linux.py @@ -79,6 +79,7 @@ class BootLinuxAarch64(LinuxTest): """ self.require_accelerator("tcg") self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "max,lpa2=off") self.vm.add_args("-machine", "virt,gic-version=2") self.add_common_args() self.launch_and_wait(set_up_ssh_connection=False) @@ -91,6 +92,7 @@ class BootLinuxAarch64(LinuxTest): """ self.require_accelerator("tcg") self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "max,lpa2=off") self.vm.add_args("-machine", "virt,gic-version=3") self.add_common_args() self.launch_and_wait(set_up_ssh_connection=False) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 9c618d4809..45a2ceda22 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -325,31 +325,6 @@ class BootLinuxConsole(LinuxKernelTest): kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash) - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - self.require_accelerator("tcg") - self.vm.add_args('-cpu', 'cortex-a53', - '-accel', 'tcg', - '-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_aarch64_xlnx_versal_virt(self): """ :avocado: tags=arch:aarch64 @@ -1165,11 +1140,14 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=arch:ppc64 :avocado: tags=machine:ppce500 :avocado: tags=cpu:e5500 + :avocado: tags=accel:tcg """ + self.require_accelerator("tcg") tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' self.do_test_advcal_2018('19', tar_hash, 'uImage') def do_test_ppc64_powernv(self, proc): + self.require_accelerator("tcg") images_url = ('https://github.com/open-power/op-build/releases/download/v2.7/') kernel_url = images_url + 'zImage.epapr' @@ -1194,6 +1172,7 @@ class BootLinuxConsole(LinuxKernelTest): """ :avocado: tags=arch:ppc64 :avocado: tags=machine:powernv8 + :avocado: tags=accel:tcg """ self.do_test_ppc64_powernv('P8') @@ -1201,6 +1180,7 @@ class BootLinuxConsole(LinuxKernelTest): """ :avocado: tags=arch:ppc64 :avocado: tags=machine:powernv9 + :avocado: tags=accel:tcg """ self.do_test_ppc64_powernv('P9') @@ -1208,7 +1188,13 @@ class BootLinuxConsole(LinuxKernelTest): """ :avocado: tags=arch:ppc :avocado: tags=machine:g3beige + :avocado: tags=accel:tcg """ + # TODO: g3beige works with kvm_pr but we don't have a + # reliable way ATM (e.g. looking at /proc/modules) to detect + # whether we're running kvm_hv or kvm_pr. For now let's + # disable this test if we don't have TCG support. + self.require_accelerator("tcg") tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' self.vm.add_args('-M', 'graphics=off') self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') @@ -1217,7 +1203,13 @@ class BootLinuxConsole(LinuxKernelTest): """ :avocado: tags=arch:ppc :avocado: tags=machine:mac99 + :avocado: tags=accel:tcg """ + # TODO: mac99 works with kvm_pr but we don't have a + # reliable way ATM (e.g. looking at /proc/modules) to detect + # whether we're running kvm_hv or kvm_pr. For now let's + # disable this test if we don't have TCG support. + self.require_accelerator("tcg") tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' self.vm.add_args('-M', 'graphics=off') self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py index c0f0be5ade..0179d8a6ca 100644 --- a/tests/avocado/linux_ssh_mips_malta.py +++ b/tests/avocado/linux_ssh_mips_malta.py @@ -23,6 +23,9 @@ from avocado.utils import ssh @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') @skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available') class LinuxSSH(QemuSystemTest, LinuxSSHMixIn): + """ + :avocado: tags=accel:tcg + """ timeout = 150 # Not for 'configure --enable-debug --enable-debug-tcg' diff --git a/tests/avocado/machine_aarch64_virt.py b/tests/avocado/machine_aarch64_virt.py new file mode 100644 index 0000000000..21848cba70 --- /dev/null +++ b/tests/avocado/machine_aarch64_virt.py @@ -0,0 +1,51 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2022 Linaro Ltd. +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import time + +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command + +class Aarch64VirtMachine(QemuSystemTest): + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def test_aarch64_virt(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=accel:tcg + :avocado: tags=cpu:max + """ + kernel_url = ('https://fileserver.linaro.org/s/' + 'z6B2ARM7DQT3HWN/download') + + kernel_hash = 'ed11daab50c151dde0e1e9c9cb8b2d9bd3215347' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + self.require_accelerator("tcg") + self.vm.add_args('-cpu', 'max,pauth-impdef=on', + '-accel', 'tcg', + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + self.wait_for_console_pattern('Welcome to Buildroot') + time.sleep(0.1) + exec_command(self, 'root') + time.sleep(0.1) + exec_command(self, 'cat /proc/self/maps') + time.sleep(0.1) diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py new file mode 100644 index 0000000000..33090af199 --- /dev/null +++ b/tests/avocado/machine_aspeed.py @@ -0,0 +1,36 @@ +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +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.utils import archive + + +class AST1030Machine(QemuSystemTest): + """Boots the zephyr os and checks that the console is operational""" + + timeout = 10 + + def test_ast1030_zephyros(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast1030-evb + """ + tar_url = ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip') + tar_hash = '4c6a8ce3a8ba76ef1a65dae419ae3409343c4b20' + tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(tar_path, self.workdir) + kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.elf" + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, + '-nographic') + self.vm.launch() + wait_for_console_pattern(self, "Booting Zephyr OS") + exec_command_and_wait_for_pattern(self, "help", + "Available commands") diff --git a/tests/avocado/machine_s390_ccw_virtio.py b/tests/avocado/machine_s390_ccw_virtio.py index bd03d7160b..438a6f4321 100644 --- a/tests/avocado/machine_s390_ccw_virtio.py +++ b/tests/avocado/machine_s390_ccw_virtio.py @@ -248,7 +248,7 @@ class S390CCWVirtioMachine(QemuSystemTest): line = ppmfile.readline() self.assertEqual(line, b"P6\n") line = ppmfile.readline() - self.assertEqual(line, b"1024 768\n") + self.assertEqual(line, b"1280 800\n") line = ppmfile.readline() self.assertEqual(line, b"255\n") line = ppmfile.readline(256) diff --git a/tests/avocado/ppc_405.py b/tests/avocado/ppc_405.py index a47f89b934..4e7e01aa76 100644 --- a/tests/avocado/ppc_405.py +++ b/tests/avocado/ppc_405.py @@ -25,18 +25,12 @@ class Ppc405Machine(QemuSystemTest): wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') - def test_ppc_taihu(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:taihu - :avocado: tags=cpu:405ep - """ - self.do_test_ppc405() - 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.do_test_ppc405() diff --git a/tests/avocado/ppc_74xx.py b/tests/avocado/ppc_74xx.py index 556a9a7da9..f54757c243 100644 --- a/tests/avocado/ppc_74xx.py +++ b/tests/avocado/ppc_74xx.py @@ -11,6 +11,7 @@ from avocado_qemu import wait_for_console_pattern class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=arch:ppc + :avocado: tags=accel:tcg """ timeout = 5 @@ -18,6 +19,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7400 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -27,6 +29,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7410 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -36,6 +39,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7441 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -45,6 +49,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7445 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -54,6 +59,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7447 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -63,6 +69,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7447a """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -72,6 +79,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7448 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -81,6 +89,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7450 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -90,6 +99,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7451 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -99,6 +109,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7455 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -108,6 +119,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7457 """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') @@ -117,6 +129,7 @@ class ppc74xxCpu(QemuSystemTest): """ :avocado: tags=cpu:7457a """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') diff --git a/tests/avocado/ppc_bamboo.py b/tests/avocado/ppc_bamboo.py index 40629e3478..102ff252df 100644 --- a/tests/avocado/ppc_bamboo.py +++ b/tests/avocado/ppc_bamboo.py @@ -20,7 +20,9 @@ class BambooMachine(QemuSystemTest): :avocado: tags=machine:bamboo :avocado: tags=cpu:440epb :avocado: tags=device:rtl8139 + :avocado: tags=accel:tcg """ + self.require_accelerator("tcg") tar_url = ('http://landley.net/aboriginal/downloads/binaries/' 'system-image-powerpc-440fp.tar.gz') tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9' diff --git a/tests/avocado/ppc_mpc8544ds.py b/tests/avocado/ppc_mpc8544ds.py index 886f967b15..8d6a749201 100644 --- a/tests/avocado/ppc_mpc8544ds.py +++ b/tests/avocado/ppc_mpc8544ds.py @@ -19,7 +19,9 @@ class Mpc8544dsMachine(QemuSystemTest): """ :avocado: tags=arch:ppc :avocado: tags=machine:mpc8544ds + :avocado: tags=accel:tcg """ + self.require_accelerator("tcg") tar_url = ('https://www.qemu-advent-calendar.org' '/2020/download/day17.tar.gz') tar_hash = '7a5239542a7c4257aa4d3b7f6ddf08fb6775c494' diff --git a/tests/avocado/ppc_prep_40p.py b/tests/avocado/ppc_prep_40p.py index 4bd956584d..d4f1eb7e1d 100644 --- a/tests/avocado/ppc_prep_40p.py +++ b/tests/avocado/ppc_prep_40p.py @@ -28,7 +28,9 @@ class IbmPrep40pMachine(QemuSystemTest): :avocado: tags=machine:40p :avocado: tags=os:netbsd :avocado: tags=slowness:high + :avocado: tags=accel:tcg """ + self.require_accelerator("tcg") bios_url = ('http://ftpmirror.your.org/pub/misc/' 'ftp.software.ibm.com/rs6000/firmware/' '7020-40p/P12H0456.IMG') @@ -51,7 +53,9 @@ class IbmPrep40pMachine(QemuSystemTest): """ :avocado: tags=arch:ppc :avocado: tags=machine:40p + :avocado: tags=accel:tcg """ + self.require_accelerator("tcg") self.vm.set_console() self.vm.add_args('-m', '192') # test fw_cfg @@ -65,7 +69,9 @@ class IbmPrep40pMachine(QemuSystemTest): :avocado: tags=arch:ppc :avocado: tags=machine:40p :avocado: tags=os:netbsd + :avocado: tags=accel:tcg """ + 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' diff --git a/tests/avocado/ppc_virtex_ml507.py b/tests/avocado/ppc_virtex_ml507.py index a6912ee579..6b07686b56 100644 --- a/tests/avocado/ppc_virtex_ml507.py +++ b/tests/avocado/ppc_virtex_ml507.py @@ -19,7 +19,9 @@ class VirtexMl507Machine(QemuSystemTest): """ :avocado: tags=arch:ppc :avocado: tags=machine:virtex-ml507 + :avocado: tags=accel:tcg """ + self.require_accelerator("tcg") tar_url = ('https://www.qemu-advent-calendar.org' '/2020/download/hippo.tar.gz') tar_hash = '306b95bfe7d147f125aa176a877e266db8ef914a' diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index c68a953730..0b2b0dc692 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -36,6 +36,9 @@ class ReplayKernelBase(LinuxKernelTest): def run_vm(self, kernel_path, kernel_command_line, console_pattern, record, shift, args, replay_path): + # icount requires TCG to be available + self.require_accelerator('tcg') + logger = logging.getLogger('replay') start_time = time.time() vm = self.get_vm() @@ -243,6 +246,7 @@ class ReplayKernelNormal(ReplayKernelBase): """ :avocado: tags=arch:ppc64 :avocado: tags=machine:pseries + :avocado: tags=accel:tcg """ kernel_url = ('https://archives.fedoraproject.org/pub/archive' '/fedora-secondary/releases/29/Everything/ppc64le/os' diff --git a/tests/avocado/vnc.py b/tests/avocado/vnc.py index 096432988f..187fd3febc 100644 --- a/tests/avocado/vnc.py +++ b/tests/avocado/vnc.py @@ -8,9 +8,48 @@ # 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 socket +from typing import List + from avocado_qemu import QemuSystemTest +VNC_ADDR = '127.0.0.1' +VNC_PORT_START = 32768 +VNC_PORT_END = VNC_PORT_START + 1024 + + +def check_bind(port: int) -> bool: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + try: + sock.bind((VNC_ADDR, port)) + except OSError: + return False + + return True + + +def check_connect(port: int) -> bool: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + try: + sock.connect((VNC_ADDR, port)) + except ConnectionRefusedError: + return False + + return True + + +def find_free_ports(count: int) -> List[int]: + result = [] + for port in range(VNC_PORT_START, VNC_PORT_END): + if check_bind(port): + result.append(port) + if len(result) >= count: + break + assert len(result) == count + return result + + class Vnc(QemuSystemTest): """ :avocado: tags=vnc,quick @@ -51,3 +90,27 @@ class Vnc(QemuSystemTest): set_password_response = self.vm.qmp('change-vnc-password', password='new_password') self.assertEqual(set_password_response['return'], {}) + + def test_change_listen(self): + a, b, c = find_free_ports(3) + self.assertFalse(check_connect(a)) + self.assertFalse(check_connect(b)) + self.assertFalse(check_connect(c)) + + self.vm.add_args('-nodefaults', '-S', '-vnc', f'{VNC_ADDR}:{a - 5900}') + self.vm.launch() + self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(a)) + self.assertTrue(check_connect(a)) + self.assertFalse(check_connect(b)) + self.assertFalse(check_connect(c)) + + res = self.vm.qmp('display-update', type='vnc', + addresses=[{'type': 'inet', 'host': VNC_ADDR, + 'port': str(b)}, + {'type': 'inet', 'host': VNC_ADDR, + 'port': str(c)}]) + self.assertEqual(res['return'], {}) + self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(b)) + self.assertFalse(check_connect(a)) + self.assertTrue(check_connect(b)) + self.assertTrue(check_connect(c)) diff --git a/tests/bench/atomic_add-bench.c b/tests/bench/atomic_add-bench.c index f05471ab45..8a6faad6ec 100644 --- a/tests/bench/atomic_add-bench.c +++ b/tests/bench/atomic_add-bench.c @@ -2,6 +2,7 @@ #include "qemu/thread.h" #include "qemu/host-utils.h" #include "qemu/processor.h" +#include "qemu/memalign.h" struct thread_info { uint64_t r; diff --git a/tests/bench/qht-bench.c b/tests/bench/qht-bench.c index 2e5b70ccd0..8afe161d10 100644 --- a/tests/bench/qht-bench.c +++ b/tests/bench/qht-bench.c @@ -10,6 +10,7 @@ #include "qemu/qht.h" #include "qemu/rcu.h" #include "qemu/xxhash.h" +#include "qemu/memalign.h" struct thread_stats { size_t rd; diff --git a/tests/check-block.sh b/tests/check-block.sh index 18f7433901..5de2c1ba0b 100755 --- a/tests/check-block.sh +++ b/tests/check-block.sh @@ -18,48 +18,10 @@ skip() { exit 0 } -# Disable tests with any sanitizer except for specific ones -SANITIZE_FLAGS=$( grep "CFLAGS.*-fsanitize" config-host.mak 2>/dev/null ) -ALLOWED_SANITIZE_FLAGS="safe-stack cfi-icall" -#Remove all occurrencies of allowed Sanitize flags -for j in ${ALLOWED_SANITIZE_FLAGS}; do - TMP_FLAGS=${SANITIZE_FLAGS} - SANITIZE_FLAGS="" - for i in ${TMP_FLAGS}; do - if ! echo ${i} | grep -q "${j}" 2>/dev/null; then - SANITIZE_FLAGS="${SANITIZE_FLAGS} ${i}" - fi - done -done -if echo ${SANITIZE_FLAGS} | grep -q "\-fsanitize" 2>/dev/null; then - # Have a sanitize flag that is not allowed, stop - skip "Sanitizers are enabled ==> Not running the qemu-iotests." -fi - if [ -z "$(find . -name 'qemu-system-*' -print)" ]; then skip "No qemu-system binary available ==> Not running the qemu-iotests." fi -if ! command -v bash >/dev/null 2>&1 ; then - skip "bash not available ==> Not running the qemu-iotests." -fi - -if LANG=C bash --version | grep -q 'GNU bash, version [123]' ; then - skip "bash version too old ==> Not running the qemu-iotests." -fi - -if ! (sed --version | grep 'GNU sed') > /dev/null 2>&1 ; then - if ! command -v gsed >/dev/null 2>&1; then - skip "GNU sed not available ==> Not running the qemu-iotests." - fi -else - # Double-check that we're not using BusyBox' sed which says - # that "This is not GNU sed version 4.0" ... - if sed --version | grep -q 'not GNU sed' ; then - skip "BusyBox sed not supported ==> Not running the qemu-iotests." - fi -fi - cd tests/qemu-iotests # QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests diff --git a/tests/data/acpi/q35/FACP b/tests/data/acpi/q35/FACP index f6a864cc86..a8f6a89611 100644 Binary files a/tests/data/acpi/q35/FACP and b/tests/data/acpi/q35/FACP differ diff --git a/tests/data/acpi/q35/FACP.nosmm b/tests/data/acpi/q35/FACP.nosmm index 6a9aa5f370..c4e6d18ee5 100644 Binary files a/tests/data/acpi/q35/FACP.nosmm and b/tests/data/acpi/q35/FACP.nosmm differ diff --git a/tests/data/acpi/q35/FACP.slic b/tests/data/acpi/q35/FACP.slic index 15986e095c..48bbb1cf5a 100644 Binary files a/tests/data/acpi/q35/FACP.slic and b/tests/data/acpi/q35/FACP.slic differ diff --git a/tests/data/acpi/q35/FACP.xapic b/tests/data/acpi/q35/FACP.xapic index 2d3659c9c6..31fa5dd19c 100644 Binary files a/tests/data/acpi/q35/FACP.xapic and b/tests/data/acpi/q35/FACP.xapic differ diff --git a/tests/data/acpi/virt/SSDT.memhp b/tests/data/acpi/virt/SSDT.memhp index 375d7b6fc8..4c363a6d95 100644 Binary files a/tests/data/acpi/virt/SSDT.memhp and b/tests/data/acpi/virt/SSDT.memhp differ diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index f1a0c5db7a..ca2157db46 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -8,31 +8,15 @@ COMMA := , HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m)) -DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles -# we don't run tests on intermediate images (used as base by another image) -DOCKER_PARTIAL_IMAGES := debian10 debian11 -# we don't directly build virtual images (they are used to build other images) -DOCKER_VIRTUAL_IMAGES := debian-bootstrap debian-toolchain empty -DOCKER_IMAGES := $(sort $(filter-out $(DOCKER_VIRTUAL_IMAGES), $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) -DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) -# Use a global constant ccache directory to speed up repetitive builds -DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache ifeq ($(HOST_ARCH),x86_64) DOCKER_DEFAULT_REGISTRY := registry.gitlab.com/qemu-project/qemu endif DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),$(DOCKER_DEFAULT_REGISTRY)) -DOCKER_TESTS := $(notdir $(shell \ - find $(SRC_PATH)/tests/docker/ -name 'test-*' -type f)) - ENGINE := auto - DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --engine $(ENGINE) -TESTS ?= % -IMAGES ?= % - CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$) DOCKER_SRC_COPY := $(BUILD_DIR)/docker-src.$(CUR_TIME) @@ -49,17 +33,7 @@ $(DOCKER_SRC_COPY): docker-qemu-src: $(DOCKER_SRC_COPY) -docker-image: ${DOCKER_TARGETS} - -# General rule for building docker images. If we are a sub-make -# invoked with SKIP_DOCKER_BUILD we still check the image is up to date -# though -ifdef SKIP_DOCKER_BUILD -docker-image-%: $(DOCKER_FILES_DIR)/%.docker - $(call quiet-command, \ - $(DOCKER_SCRIPT) check --quiet qemu/$* $<, \ - "CHECK", "$*") -else +# General rule for building docker images. docker-image-%: $(DOCKER_FILES_DIR)/%.docker $(call quiet-command,\ $(DOCKER_SCRIPT) build -t qemu/$* -f $< \ @@ -95,27 +69,9 @@ docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker { echo "You will need to build $(EXECUTABLE)"; exit 1;},\ "CHECK", "debian-$* exists")) -# These are test targets -USER_TCG_TARGETS=$(patsubst %-linux-user,qemu-%,$(filter %-linux-user,$(TARGET_DIRS))) -EXEC_COPY_TESTS=$(patsubst %,docker-exec-copy-test-%, $(USER_TCG_TARGETS)) - -$(EXEC_COPY_TESTS): docker-exec-copy-test-%: $(DOCKER_FILES_DIR)/empty.docker - $(call quiet-command, \ - $(DOCKER_SCRIPT) build -t qemu/exec-copy-test-$* -f $< \ - $(if $V,,--quiet) --no-cache \ - --include-executable=$* \ - --skip-binfmt, \ - "TEST","copy $* to container") - $(call quiet-command, \ - $(DOCKER_SCRIPT) run qemu/exec-copy-test-$* \ - /$* -version > tests/docker-exec-copy-test-$*.out, \ - "TEST","check $* works in container") - -docker-exec-copy-test: $(EXEC_COPY_TESTS) - -endif - # Enforce dependencies for composite images +# we don't run tests on intermediate images (used as base by another image) +DOCKER_PARTIAL_IMAGES := debian10 debian11 ifeq ($(HOST_ARCH),x86_64) docker-image-debian-amd64: docker-image-debian10 DOCKER_PARTIAL_IMAGES += debian-amd64-cross @@ -133,7 +89,6 @@ DOCKER_PARTIAL_IMAGES += fedora endif docker-image-debian-alpha-cross: docker-image-debian10 -docker-image-debian-arm64-cross: docker-image-debian10 docker-image-debian-armel-cross: docker-image-debian10 docker-image-debian-armhf-cross: docker-image-debian10 docker-image-debian-hppa-cross: docker-image-debian10 @@ -143,7 +98,6 @@ docker-image-debian-mips64-cross: docker-image-debian10 docker-image-debian-mips64el-cross: docker-image-debian10 docker-image-debian-mipsel-cross: docker-image-debian10 docker-image-debian-ppc64el-cross: docker-image-debian10 -docker-image-debian-s390x-cross: docker-image-debian10 docker-image-debian-sh4-cross: docker-image-debian10 docker-image-debian-sparc64-cross: docker-image-debian10 @@ -154,27 +108,8 @@ docker-image-debian-native: DOCKER_REGISTRY= docker-image-debian10: NOUSER=1 docker-image-debian11: NOUSER=1 -# -# The build rule for hexagon-cross is special in so far for most of -# the time we don't want to build it. While dockers caching does avoid -# this most of the time sometimes we want to force the issue. -# -docker-image-debian-hexagon-cross: $(DOCKER_FILES_DIR)/debian-hexagon-cross.docker - $(if $(NOCACHE), \ - $(call quiet-command, \ - $(DOCKER_SCRIPT) build -t qemu/debian-hexagon-cross -f $< \ - $(if $V,,--quiet) --no-cache \ - --registry $(DOCKER_REGISTRY) --extra-files \ - $(DOCKER_FILES_DIR)/debian-hexagon-cross.docker.d/build-toolchain.sh, \ - "BUILD", "debian-hexagon-cross"), \ - $(call quiet-command, \ - $(DOCKER_SCRIPT) fetch $(if $V,,--quiet) \ - qemu/debian-hexagon-cross $(DOCKER_REGISTRY), \ - "FETCH", "debian-hexagon-cross") \ - $(call quiet-command, \ - $(DOCKER_SCRIPT) update $(if $V,,--quiet) \ - qemu/debian-hexagon-cross --add-current-user, \ - "PREPARE", "debian-hexagon-cross")) +# alpine has no adduser +docker-image-alpine: NOUSER=1 debian-toolchain-run = \ $(if $(NOCACHE), \ @@ -195,6 +130,10 @@ debian-toolchain-run = \ "PREPARE", $1)) debian-toolchain = $(call debian-toolchain-run,$(patsubst docker-image-%,%,$1)) +docker-image-debian-hexagon-cross: $(DOCKER_FILES_DIR)/debian-hexagon-cross.docker \ + $(DOCKER_FILES_DIR)/debian-hexagon-cross.d/build-toolchain.sh + $(call debian-toolchain, $@) + docker-image-debian-microblaze-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(DOCKER_FILES_DIR)/debian-microblaze-cross.d/build-toolchain.sh $(call debian-toolchain, $@) @@ -206,35 +145,38 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ # Specialist build images, sometimes very limited tools docker-image-debian-tricore-cross: docker-image-debian10 docker-image-debian-all-test-cross: docker-image-debian10 -docker-image-debian-arm64-test-cross: docker-image-debian11 docker-image-debian-microblaze-cross: docker-image-debian10 docker-image-debian-nios2-cross: docker-image-debian10 docker-image-debian-powerpc-test-cross: docker-image-debian11 +docker-image-debian-riscv64-test-cross: docker-image-debian11 # These images may be good enough for building tests but not for test builds DOCKER_PARTIAL_IMAGES += debian-alpha-cross -DOCKER_PARTIAL_IMAGES += debian-arm64-test-cross DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross +DOCKER_PARTIAL_IMAGES += debian-riscv64-test-cross DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-tricore-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross DOCKER_PARTIAL_IMAGES += fedora-cris-cross -# Rules for building linux-user powered images -# -# These are slower than using native cross compiler setups but can -# work around issues with poorly working multi-arch systems and broken -# packages. +# images that are only used to build other images +DOCKER_VIRTUAL_IMAGES := debian-bootstrap debian-toolchain + +__IMAGES := $(sort $(filter-out $(DOCKER_VIRTUAL_IMAGES), $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) +DOCKER_IMAGES := $(if $(IMAGES), $(filter $(IMAGES), $(__IMAGES)), $(__IMAGES)) + +__TESTS := $(notdir $(shell find $(SRC_PATH)/tests/docker/ -name 'test-*' -type f)) +DOCKER_TESTS := $(if $(TESTS), $(filter $(TESTS), $(__TESTS)), $(__TESTS)) # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES) $(DOCKER_VIRTUAL_IMAGES),$(DOCKER_IMAGES)), \ +$(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES)), \ $(foreach t,$(DOCKER_TESTS), \ $(eval .PHONY: docker-$t@$i) \ - $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ + $(eval docker-$t@$i: docker-image-$i; @$(MAKE) docker-run TEST=$t IMAGE=$i) \ ) \ $(foreach t,$(DOCKER_TESTS), \ $(eval docker-all-tests: docker-$t@$i) \ @@ -261,11 +203,6 @@ docker: @echo @echo 'Available container images:' @echo ' $(DOCKER_IMAGES)' -ifneq ($(DOCKER_USER_IMAGES),) - @echo - @echo 'Available linux-user images (docker-binfmt-image-debian-%):' - @echo ' $(DOCKER_USER_IMAGES)' -endif @echo @echo 'Available tests:' @echo ' $(DOCKER_TESTS)' @@ -274,8 +211,8 @@ endif @echo ' TARGET_LIST=a,b,c Override target list in builds.' @echo ' EXTRA_CONFIGURE_OPTS="..."' @echo ' Extra configure options.' - @echo ' IMAGES="a b c ..": Filters which images to build or run.' - @echo ' TESTS="x y z .." Filters which tests to run (for docker-test).' + @echo ' IMAGES="a b c ..": Restrict available images to subset.' + @echo ' TESTS="x y z .." Restrict available tests to subset.' @echo ' J=[0..9]* Overrides the -jN parameter for make commands' @echo ' (default is 1)' @echo ' DEBUG=1 Stop and drop to shell in the created container' @@ -293,9 +230,12 @@ endif docker-help: docker +# Use a global constant ccache directory to speed up repetitive builds +DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache + # This rule if for directly running against an arbitrary docker target. # It is called by the expanded docker targets (e.g. make -# docker-test-foo@bar) which will do additional verification. +# docker-test-foo@bar) which will also ensure the image is up to date. # # For example: make docker-run TEST="test-quick" IMAGE="debian:arm64" EXECUTABLE=./aarch64-linux-user/qemu-aarch64 # @@ -330,13 +270,7 @@ docker-run: docker-qemu-src $(call quiet-command, rm -r $(DOCKER_SRC_COPY), \ " CLEANUP $(DOCKER_SRC_COPY)") -# Run targets: -# -# Of the form docker-TEST-FOO@IMAGE-BAR which will then be expanded into a call to "make docker-run" -docker-run-%: CMD = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\1/') -docker-run-%: IMAGE = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\2/') -docker-run-%: - @$(MAKE) docker-run TEST=$(CMD) IMAGE=qemu/$(IMAGE) +docker-image: ${DOCKER_IMAGES:%=docker-image-%} docker-clean: $(call quiet-command, $(DOCKER_SCRIPT) clean) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 78dd13171e..d0af2861b8 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -676,63 +676,6 @@ class CcCommand(SubCommand): as_user=True) -class CheckCommand(SubCommand): - """Check if we need to re-build a docker image out of a dockerfile. - Arguments: """ - name = "check" - - def args(self, parser): - parser.add_argument("tag", - help="Image Tag") - parser.add_argument("dockerfile", default=None, - help="Dockerfile name", nargs='?') - parser.add_argument("--checktype", choices=["checksum", "age"], - default="checksum", help="check type") - parser.add_argument("--olderthan", default=60, type=int, - help="number of minutes") - - def run(self, args, argv): - tag = args.tag - - try: - dkr = Docker() - except subprocess.CalledProcessError: - print("Docker not set up") - return 1 - - info = dkr.inspect_tag(tag) - if info is None: - print("Image does not exist") - return 1 - - if args.checktype == "checksum": - if not args.dockerfile: - print("Need a dockerfile for tag:%s" % (tag)) - return 1 - - dockerfile = _read_dockerfile(args.dockerfile) - - if dkr.image_matches_dockerfile(tag, dockerfile): - if not args.quiet: - print("Image is up to date") - return 0 - else: - print("Image needs updating") - return 1 - elif args.checktype == "age": - timestr = dkr.get_image_creation_time(info).split(".")[0] - created = datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S") - past = datetime.now() - timedelta(minutes=args.olderthan) - if created < past: - print ("Image created @ %s more than %d minutes old" % - (timestr, args.olderthan)) - return 1 - else: - if not args.quiet: - print ("Image less than %d minutes old" % (args.olderthan)) - return 0 - - def main(): global USE_ENGINE diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 591af43d60..3f4c0f95cb 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -73,7 +73,6 @@ RUN apk update && \ openssh-client \ pcre-dev \ perl \ - perl-test-harness \ pixman-dev \ pkgconf \ pulseaudio-dev \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index 3ede55d09b..4b20925bbf 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -87,7 +87,6 @@ RUN dnf update -y && \ pam-devel \ pcre-static \ perl \ - perl-Test-Harness \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 166e24df13..b60426834c 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -1,32 +1,165 @@ +# THIS FILE WAS AUTO-GENERATED # -# Docker arm64 cross-compiler target +# $ lcitool dockerfile --layers all --cross aarch64 debian-11 qemu # -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 +# https://gitlab.com/libvirt/libvirt-ci -# Add the foreign architecture we want and install dependencies -RUN dpkg --add-architecture arm64 -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - crossbuild-essential-arm64 -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt build-dep -yy -a arm64 --arch-only qemu +FROM docker.io/library/debian:11-slim -# Specify the cross prefix for this image (see tests/docker/common.rc) +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y \ + bash \ + bc \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + gcovr \ + genisoimage \ + gettext \ + git \ + hostname \ + libpcre2-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + ncat \ + ninja-build \ + openssh-client \ + perl-base \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + sparse \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + texinfo && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ + dpkg-reconfigure locales + +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" + +RUN export DEBIAN_FRONTEND=noninteractive && \ + dpkg --add-architecture arm64 && \ + eatmydata apt-get update && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ + eatmydata apt-get install --no-install-recommends -y \ + g++-aarch64-linux-gnu \ + gcc-aarch64-linux-gnu \ + libaio-dev:arm64 \ + libasan5:arm64 \ + libasound2-dev:arm64 \ + libattr1-dev:arm64 \ + libbpf-dev:arm64 \ + libbrlapi-dev:arm64 \ + libbz2-dev:arm64 \ + libc6-dev:arm64 \ + libcacard-dev:arm64 \ + libcap-ng-dev:arm64 \ + libcapstone-dev:arm64 \ + libcurl4-gnutls-dev:arm64 \ + libdaxctl-dev:arm64 \ + libdrm-dev:arm64 \ + libepoxy-dev:arm64 \ + libfdt-dev:arm64 \ + libffi-dev:arm64 \ + libfuse3-dev:arm64 \ + libgbm-dev:arm64 \ + libgcrypt20-dev:arm64 \ + libglib2.0-dev:arm64 \ + libglusterfs-dev:arm64 \ + libgnutls28-dev:arm64 \ + libgtk-3-dev:arm64 \ + libibumad-dev:arm64 \ + libibverbs-dev:arm64 \ + libiscsi-dev:arm64 \ + libjemalloc-dev:arm64 \ + libjpeg62-turbo-dev:arm64 \ + liblttng-ust-dev:arm64 \ + liblzo2-dev:arm64 \ + libncursesw5-dev:arm64 \ + libnfs-dev:arm64 \ + libnuma-dev:arm64 \ + libpam0g-dev:arm64 \ + libpixman-1-dev:arm64 \ + libpng-dev:arm64 \ + libpulse-dev:arm64 \ + librbd-dev:arm64 \ + librdmacm-dev:arm64 \ + libsasl2-dev:arm64 \ + libsdl2-dev:arm64 \ + libsdl2-image-dev:arm64 \ + libseccomp-dev:arm64 \ + libselinux1-dev:arm64 \ + libslirp-dev:arm64 \ + libsnappy-dev:arm64 \ + libspice-server-dev:arm64 \ + libssh-gcrypt-dev:arm64 \ + libsystemd-dev:arm64 \ + libtasn1-6-dev:arm64 \ + libubsan1:arm64 \ + libudev-dev:arm64 \ + liburing-dev:arm64 \ + libusb-1.0-0-dev:arm64 \ + libusbredirhost-dev:arm64 \ + libvdeplug-dev:arm64 \ + libvirglrenderer-dev:arm64 \ + libvte-2.91-dev:arm64 \ + libxen-dev:arm64 \ + libzstd-dev:arm64 \ + nettle-dev:arm64 \ + systemtap-sdt-dev:arm64 \ + xfslibs-dev:arm64 \ + zlib1g-dev:arm64 && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + mkdir -p /usr/local/share/meson/cross && \ + echo "[binaries]\n\ +c = '/usr/bin/aarch64-linux-gnu-gcc'\n\ +ar = '/usr/bin/aarch64-linux-gnu-gcc-ar'\n\ +strip = '/usr/bin/aarch64-linux-gnu-strip'\n\ +pkgconfig = '/usr/bin/aarch64-linux-gnu-pkg-config'\n\ +\n\ +[host_machine]\n\ +system = 'linux'\n\ +cpu_family = 'aarch64'\n\ +cpu = 'aarch64'\n\ +endian = 'little'" > /usr/local/share/meson/cross/aarch64-linux-gnu && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-gcc + +ENV ABI "aarch64-linux-gnu" +ENV MESON_OPTS "--cross-file=aarch64-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=aarch64-linux-gnu- ENV DEF_TARGET_LIST aarch64-softmmu,aarch64-linux-user - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - libbz2-dev:arm64 \ - liblzo2-dev:arm64 \ - librdmacm-dev:arm64 \ - libsnappy-dev:arm64 \ - libxen-dev:arm64 - -# nettle -ENV QEMU_CONFIGURE_OPTS $QEMU_CONFIGURE_OPTS --enable-nettle diff --git a/tests/docker/dockerfiles/debian-arm64-test-cross.docker b/tests/docker/dockerfiles/debian-arm64-test-cross.docker deleted file mode 100644 index 53a9012beb..0000000000 --- a/tests/docker/dockerfiles/debian-arm64-test-cross.docker +++ /dev/null @@ -1,13 +0,0 @@ -# -# Docker arm64 cross-compiler target (tests only) -# -# This docker target builds on the debian Bullseye base image. -# -FROM qemu/debian11 - -# Add the foreign architecture we want and install dependencies -RUN dpkg --add-architecture arm64 -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - crossbuild-essential-arm64 gcc-10-aarch64-linux-gnu diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-hexagon-cross.d/build-toolchain.sh similarity index 100% rename from tests/docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh rename to tests/docker/dockerfiles/debian-hexagon-cross.d/build-toolchain.sh diff --git a/tests/docker/dockerfiles/debian-riscv64-test-cross.docker b/tests/docker/dockerfiles/debian-riscv64-test-cross.docker new file mode 100644 index 0000000000..1d90901298 --- /dev/null +++ b/tests/docker/dockerfiles/debian-riscv64-test-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the Debian Bullseye base image. +# +FROM qemu/debian11 + +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + gcc-riscv64-linux-gnu \ + libc6-dev-riscv64-cross diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 9f2ab51eb0..ff79a2cc4f 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -1,33 +1,163 @@ +# THIS FILE WAS AUTO-GENERATED # -# Docker s390 cross-compiler target +# $ lcitool dockerfile --layers all --cross s390x debian-11 qemu # -# This docker target builds on the debian Stretch base image. -# -FROM qemu/debian10 +# https://gitlab.com/libvirt/libvirt-ci -# Add the s390x architecture -RUN dpkg --add-architecture s390x +FROM docker.io/library/debian:11-slim -# Grab the updated list of packages -RUN apt update && apt dist-upgrade -yy -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-multilib-s390x-linux-gnu +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y \ + bash \ + bc \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + gcovr \ + genisoimage \ + gettext \ + git \ + hostname \ + libpcre2-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + ncat \ + ninja-build \ + openssh-client \ + perl-base \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + sparse \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + texinfo && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ + dpkg-reconfigure locales -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt build-dep -yy -a s390x --arch-only qemu +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -# Specify the cross prefix for this image (see tests/docker/common.rc) +RUN export DEBIAN_FRONTEND=noninteractive && \ + dpkg --add-architecture s390x && \ + eatmydata apt-get update && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ + eatmydata apt-get install --no-install-recommends -y \ + g++-s390x-linux-gnu \ + gcc-s390x-linux-gnu \ + libaio-dev:s390x \ + libasan5:s390x \ + libasound2-dev:s390x \ + libattr1-dev:s390x \ + libbpf-dev:s390x \ + libbrlapi-dev:s390x \ + libbz2-dev:s390x \ + libc6-dev:s390x \ + libcacard-dev:s390x \ + libcap-ng-dev:s390x \ + libcapstone-dev:s390x \ + libcurl4-gnutls-dev:s390x \ + libdaxctl-dev:s390x \ + libdrm-dev:s390x \ + libepoxy-dev:s390x \ + libfdt-dev:s390x \ + libffi-dev:s390x \ + libfuse3-dev:s390x \ + libgbm-dev:s390x \ + libgcrypt20-dev:s390x \ + libglib2.0-dev:s390x \ + libglusterfs-dev:s390x \ + libgnutls28-dev:s390x \ + libgtk-3-dev:s390x \ + libibumad-dev:s390x \ + libibverbs-dev:s390x \ + libiscsi-dev:s390x \ + libjemalloc-dev:s390x \ + libjpeg62-turbo-dev:s390x \ + liblttng-ust-dev:s390x \ + liblzo2-dev:s390x \ + libncursesw5-dev:s390x \ + libnfs-dev:s390x \ + libnuma-dev:s390x \ + libpam0g-dev:s390x \ + libpixman-1-dev:s390x \ + libpng-dev:s390x \ + libpulse-dev:s390x \ + librbd-dev:s390x \ + librdmacm-dev:s390x \ + libsasl2-dev:s390x \ + libsdl2-dev:s390x \ + libsdl2-image-dev:s390x \ + libseccomp-dev:s390x \ + libselinux1-dev:s390x \ + libslirp-dev:s390x \ + libsnappy-dev:s390x \ + libssh-gcrypt-dev:s390x \ + libsystemd-dev:s390x \ + libtasn1-6-dev:s390x \ + libubsan1:s390x \ + libudev-dev:s390x \ + liburing-dev:s390x \ + libusb-1.0-0-dev:s390x \ + libusbredirhost-dev:s390x \ + libvdeplug-dev:s390x \ + libvirglrenderer-dev:s390x \ + libvte-2.91-dev:s390x \ + libzstd-dev:s390x \ + nettle-dev:s390x \ + systemtap-sdt-dev:s390x \ + xfslibs-dev:s390x \ + zlib1g-dev:s390x && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + mkdir -p /usr/local/share/meson/cross && \ + echo "[binaries]\n\ +c = '/usr/bin/s390x-linux-gnu-gcc'\n\ +ar = '/usr/bin/s390x-linux-gnu-gcc-ar'\n\ +strip = '/usr/bin/s390x-linux-gnu-strip'\n\ +pkgconfig = '/usr/bin/s390x-linux-gnu-pkg-config'\n\ +\n\ +[host_machine]\n\ +system = 'linux'\n\ +cpu_family = 's390x'\n\ +cpu = 's390x'\n\ +endian = 'big'" > /usr/local/share/meson/cross/s390x-linux-gnu && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-gcc + +ENV ABI "s390x-linux-gnu" +ENV MESON_OPTS "--cross-file=s390x-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=s390x-linux-gnu- ENV DEF_TARGET_LIST s390x-softmmu,s390x-linux-user - -# Install extra libraries to increase code coverage -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - libbz2-dev:s390x \ - liblzo2-dev:s390x \ - librdmacm-dev:s390x \ - libsnappy-dev:s390x diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 3f6b55562c..b573b9ded2 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -25,7 +25,6 @@ RUN apt update && \ git \ libglib2.0-dev \ libpixman-1-dev \ - libtest-harness-perl \ locales \ make \ ninja-build \ diff --git a/tests/docker/dockerfiles/empty.docker b/tests/docker/dockerfiles/empty.docker deleted file mode 100644 index 9ba980f1a8..0000000000 --- a/tests/docker/dockerfiles/empty.docker +++ /dev/null @@ -1,8 +0,0 @@ -# -# Empty Dockerfile -# - -FROM scratch - -# Add everything from the context into the container -ADD . / diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker index 13328e6081..0a3ec346e6 100644 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -20,7 +20,6 @@ ENV PACKAGES \ gnutls-devel.i686 \ nettle-devel.i686 \ pcre-devel.i686 \ - perl-Test-Harness \ pixman-devel.i686 \ sysprof-capture-devel.i686 \ zlib-devel.i686 diff --git a/tests/docker/dockerfiles/fedora-win32-cross.docker b/tests/docker/dockerfiles/fedora-win32-cross.docker index d80e66c651..84a8f5524d 100644 --- a/tests/docker/dockerfiles/fedora-win32-cross.docker +++ b/tests/docker/dockerfiles/fedora-win32-cross.docker @@ -31,7 +31,6 @@ ENV PACKAGES \ mingw32-SDL2 \ msitools \ perl \ - perl-Test-Harness \ python3 \ python3-PyYAML \ tar \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 2b12b94ccf..d7ed8eb1cf 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -28,7 +28,6 @@ ENV PACKAGES \ mingw64-pkg-config \ msitools \ perl \ - perl-Test-Harness \ python3 \ python3-PyYAML \ tar \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 1d01cd9440..2e6a84abfd 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -94,7 +94,6 @@ exec "$@"' > /usr/bin/nosync && \ openssh-clients \ pam-devel \ pcre-static \ - perl-Test-Harness \ perl-base \ pixman-devel \ pkgconfig \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 1b78d8369a..6f5993d602 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -83,7 +83,6 @@ RUN zypper update -y && \ openssh \ pam-devel \ pcre-devel-static \ - perl-Test-Harness \ perl-base \ pkgconfig \ python3-Pillow \ @@ -127,8 +126,7 @@ RUN zypper update -y && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN pip3 install \ - meson==0.56.0 +RUN pip3 install meson==0.56.0 ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker index 699f2dfc6a..b3f2156580 100644 --- a/tests/docker/dockerfiles/ubuntu1804.docker +++ b/tests/docker/dockerfiles/ubuntu1804.docker @@ -81,7 +81,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libssh-dev \ libsystemd-dev \ libtasn1-6-dev \ - libtest-harness-perl \ libubsan1 \ libudev-dev \ libusb-1.0-0-dev \ @@ -134,8 +133,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN pip3 install \ - meson==0.56.0 +RUN pip3 install meson==0.56.0 ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker index 87513125b8..a3b38884e3 100644 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ b/tests/docker/dockerfiles/ubuntu2004.docker @@ -83,7 +83,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libssh-dev \ libsystemd-dev \ libtasn1-6-dev \ - libtest-harness-perl \ libubsan1 \ libudev-dev \ libusb-1.0-0-dev \ @@ -136,8 +135,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN pip3 install \ - meson==0.56.0 +RUN pip3 install meson==0.56.0 ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index c24baf8535..8ce0ca1545 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -545,7 +545,8 @@ static int round_name_to_mode(const char *name) return -1; } -static void QEMU_NORETURN die_host_rounding(enum rounding rounding) +static G_NORETURN +void die_host_rounding(enum rounding rounding) { fprintf(stderr, "fatal: '%s' rounding not supported on this host\n", round_names[rounding]); diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 352dd71c44..35829ad5f7 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -921,7 +921,8 @@ static void parse_args(int argc, char *argv[]) } } -static void QEMU_NORETURN run_test(void) +static G_NORETURN +void run_test(void) { unsigned int i; diff --git a/tests/fp/meson.build b/tests/fp/meson.build index 59776a00a7..8bd0979f67 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -37,6 +37,11 @@ tfcflags = [ '-Wno-error', ] +if cc.get_id() == 'clang' + # Clang does not support '#pragma STDC FENV_ACCESS' + tfcflags += [ '-Wno-ignored-pragmas' ] +endif + tfgencases = [ tfdir / 'genCases_ui32.c', tfdir / 'genCases_ui64.c', diff --git a/tests/fp/platform.h b/tests/fp/platform.h index c20ba70baa..6c72ad0cd0 100644 --- a/tests/fp/platform.h +++ b/tests/fp/platform.h @@ -29,9 +29,9 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config-host.h" +#include "qemu/compiler.h" -#ifndef HOST_WORDS_BIGENDIAN +#if !HOST_BIG_ENDIAN #define LITTLEENDIAN 1 /* otherwise do not define it */ #endif diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py index 2e58795a10..d865e46ecd 100755 --- a/tests/guest-debug/run-test.py +++ b/tests/guest-debug/run-test.py @@ -92,17 +92,18 @@ if __name__ == '__main__': result = subprocess.call(gdb_cmd, shell=True, stdout=output) - # A negative result is the result of an internal gdb failure like - # a crash. We force a return of 0 so we don't fail the test on + # A result of greater than 128 indicates a fatal signal (likely a + # crash due to gdb internal failure). That's a problem for GDB and + # not the test so we force a return of 0 so we don't fail the test on # account of broken external tools. - if result < 0: - print("GDB crashed? SKIPPING") + if result > 128: + log(output, "GDB crashed? (%d, %d) SKIPPING" % (result, result - 128)) exit(0) try: inferior.wait(2) except subprocess.TimeoutExpired: - print("GDB never connected? Killed guest") + log(output, "GDB never connected? Killed guest") inferior.kill() exit(result) diff --git a/tests/lcitool/Makefile.include b/tests/lcitool/Makefile.include index 6b215adcd1..3780185c7c 100644 --- a/tests/lcitool/Makefile.include +++ b/tests/lcitool/Makefile.include @@ -14,4 +14,4 @@ lcitool-help: lcitool lcitool-refresh: $(call quiet-command, cd $(SRC_PATH) && git submodule update --init tests/lcitool/libvirt-ci) - $(call quiet-command, $(LCITOOL_REFRESH)) + $(call quiet-command, $(PYTHON) $(LCITOOL_REFRESH)) diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index 6dd9b6fab1..f83b916d5e 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit 6dd9b6fab1fe081b16bc975485d7a02c81ba5fbe +Subproject commit f83b916d5efa4bd33fbf4b7ea41bf6d535cc63fb diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 958868a6ee..d068a7a8de 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -79,7 +79,6 @@ packages: - pam - pcre-static - perl - - perl-Test-Harness - pixman - pkg-config - pulseaudio diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 4ab90a310a..2d198ad281 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # # Re-generate container recipes # @@ -76,6 +76,12 @@ ubuntu2004_tsanhack = [ "RUN sed -i 's/^const/static const/g' /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h\n" ] +def debian_cross_build(prefix, targets): + conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix) + targets = "ENV DEF_TARGET_LIST %s\n" % (targets) + return "".join([conf, targets]) + + try: generate_dockerfile("centos8", "centos-stream-8") generate_dockerfile("fedora", "fedora-35") @@ -86,6 +92,16 @@ try: generate_dockerfile("opensuse-leap", "opensuse-leap-152") generate_dockerfile("alpine", "alpine-edge") + generate_dockerfile("debian-arm64-cross", "debian-11", + cross="aarch64", + trailer=debian_cross_build("aarch64-linux-gnu-", + "aarch64-softmmu,aarch64-linux-user")) + + generate_dockerfile("debian-s390x-cross", "debian-11", + cross="s390x", + trailer=debian_cross_build("s390x-linux-gnu-", + "s390x-softmmu,s390x-linux-user")) + generate_cirrus("freebsd-12") generate_cirrus("freebsd-13") generate_cirrus("macos-11") diff --git a/tests/meson.build b/tests/meson.build index 1d05109eb4..4f691e8465 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,5 +1,3 @@ -py3 = import('python').find_installation() - subdir('bench') subdir('qemu-iotests') diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err index b1aa1f4e8d..e69de29bb2 100644 --- a/tests/qapi-schema/alternate-array.err +++ b/tests/qapi-schema/alternate-array.err @@ -1,2 +0,0 @@ -alternate-array.json: In alternate 'Alt': -alternate-array.json:5: 'data' member 'two' cannot be an array diff --git a/tests/qapi-schema/alternate-array.json b/tests/qapi-schema/alternate-array.json index f241aac122..b878a2db77 100644 --- a/tests/qapi-schema/alternate-array.json +++ b/tests/qapi-schema/alternate-array.json @@ -1,5 +1,3 @@ -# we do not allow array branches in alternates -# TODO: should we support this? { 'struct': 'One', 'data': { 'name': 'str' } } { 'alternate': 'Alt', diff --git a/tests/qapi-schema/alternate-array.out b/tests/qapi-schema/alternate-array.out index e69de29bb2..a657d85738 100644 --- a/tests/qapi-schema/alternate-array.out +++ b/tests/qapi-schema/alternate-array.out @@ -0,0 +1,18 @@ +module ./builtin +object q_empty +enum QType + prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool +module alternate-array.json +object One + member name: str optional=False +alternate Alt + tag type + case one: One + case two: intList diff --git a/tests/qapi-schema/alternate-conflict-lists.err b/tests/qapi-schema/alternate-conflict-lists.err new file mode 100644 index 0000000000..f3374ec1e7 --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-lists.err @@ -0,0 +1,2 @@ +alternate-conflict-lists.json: In alternate 'Alt': +alternate-conflict-lists.json:4: branch 'two' can't be distinguished from 'one' diff --git a/tests/qapi-schema/alternate-conflict-lists.json b/tests/qapi-schema/alternate-conflict-lists.json new file mode 100644 index 0000000000..a3efd6c501 --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-lists.json @@ -0,0 +1,6 @@ +# Two lists conflict even if their inner types would be compatible +{ 'struct': 'One', + 'data': { 'name': 'str' } } +{ 'alternate': 'Alt', + 'data': { 'one': [ 'int' ], + 'two': [ 'str' ] } } diff --git a/python/qemu/aqmp/py.typed b/tests/qapi-schema/alternate-conflict-lists.out similarity index 100% rename from python/qemu/aqmp/py.typed rename to tests/qapi-schema/alternate-conflict-lists.out diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index caf0791ba8..c18dd7d02f 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -11,6 +11,7 @@ schemas = [ 'alternate-conflict-dict.json', 'alternate-conflict-enum-bool.json', 'alternate-conflict-enum-int.json', + 'alternate-conflict-lists.json', 'alternate-conflict-string.json', 'alternate-conflict-bool-string.json', 'alternate-conflict-num-string.json', diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 43b8697002..ba7302f42b 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -119,6 +119,7 @@ { 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } } { 'alternate': 'AltNumEnum', 'data': { 'n': 'number', 'e': 'EnumOne' } } { 'alternate': 'AltEnumInt', 'data': { 'e': 'EnumOne', 'i': 'int' } } +{ 'alternate': 'AltListInt', 'data': { 'l': ['int'], 'i': 'int' } } # for testing use of 'str' within alternates { 'alternate': 'AltStrObj', 'data': { 's': 'str', 'o': 'TestStruct' } } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 1f9585fa9b..043d75c655 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -121,6 +121,10 @@ alternate AltEnumInt tag type case e: EnumOne case i: int +alternate AltListInt + tag type + case l: intList + case i: int alternate AltStrObj tag type case s: str diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 index 80686a30d5..5771ea9200 100755 --- a/tests/qemu-iotests/025 +++ b/tests/qemu-iotests/025 @@ -20,7 +20,7 @@ # # creator -owner=stefanha@linux.vnet.ibm.com +owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/027 b/tests/qemu-iotests/027 index b279c88f33..24c93627bb 100755 --- a/tests/qemu-iotests/027 +++ b/tests/qemu-iotests/027 @@ -20,7 +20,7 @@ # # creator -owner=stefanha@linux.vnet.ibm.com +owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 8c391f2adc..2b232c4614 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -23,7 +23,7 @@ # # creator -owner=stefanha@linux.vnet.ibm.com +owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 567bf1da67..98595d47fe 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -64,16 +64,18 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img), - qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), - 'image file map does not match backing file after streaming') + self.assertEqual( + qemu_io('-f', 'raw', '-c', 'map', backing_img).stdout, + qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout, + 'image file map does not match backing file after streaming') def test_stream_intermediate(self): self.assert_no_active_block_jobs() - self.assertNotEqual(qemu_io('-f', 'raw', '-rU', '-c', 'map', backing_img), - qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img), - 'image file map matches backing file before streaming') + self.assertNotEqual( + qemu_io('-f', 'raw', '-rU', '-c', 'map', backing_img).stdout, + qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img).stdout, + 'image file map matches backing file before streaming') result = self.vm.qmp('block-stream', device='mid', job_id='stream-mid') self.assert_qmp(result, 'return', {}) @@ -83,9 +85,10 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img), - qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img), - 'image file map does not match backing file after streaming') + self.assertEqual( + qemu_io('-f', 'raw', '-c', 'map', backing_img).stdout, + qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img).stdout, + 'image file map does not match backing file after streaming') def test_stream_pause(self): self.assert_no_active_block_jobs() @@ -113,15 +116,17 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img), - qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), - 'image file map does not match backing file after streaming') + self.assertEqual( + qemu_io('-f', 'raw', '-c', 'map', backing_img).stdout, + qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout, + 'image file map does not match backing file after streaming') def test_stream_no_op(self): self.assert_no_active_block_jobs() # The image map is empty before the operation - empty_map = qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', test_img) + empty_map = qemu_io( + '-f', iotests.imgfmt, '-rU', '-c', 'map', test_img).stdout # This is a no-op: no data should ever be copied from the base image result = self.vm.qmp('block-stream', device='drive0', base=mid_img) @@ -132,8 +137,9 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), - empty_map, 'image file map changed after a no-op') + self.assertEqual( + qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout, + empty_map, 'image file map changed after a no-op') def test_stream_partial(self): self.assert_no_active_block_jobs() @@ -146,9 +152,10 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img), - qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), - 'image file map does not match backing file after streaming') + self.assertEqual( + qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img).stdout, + qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout, + 'image file map does not match backing file after streaming') def test_device_not_found(self): result = self.vm.qmp('block-stream', device='nonexistent') @@ -236,9 +243,10 @@ class TestParallelOps(iotests.QMPTestCase): # Check that the maps don't match before the streaming operations for i in range(2, self.num_imgs, 2): - self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]), - qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]), - 'image file map matches backing file before streaming') + self.assertNotEqual( + qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]).stdout, + qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]).stdout, + 'image file map matches backing file before streaming') # Create all streaming jobs pending_jobs = [] @@ -278,9 +286,10 @@ class TestParallelOps(iotests.QMPTestCase): # Check that all maps match now for i in range(2, self.num_imgs, 2): - self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]), - qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]), - 'image file map does not match backing file after streaming') + self.assertEqual( + qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]).stdout, + qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]).stdout, + 'image file map does not match backing file after streaming') # Test that it's not possible to perform two block-stream # operations if there are nodes involved in both. @@ -436,6 +445,11 @@ class TestParallelOps(iotests.QMPTestCase): self.vm.run_job(job='node4', auto_dismiss=True) self.assert_no_active_block_jobs() + # Assert that node0 is now the backing node of node4 + result = self.vm.qmp('query-named-block-nodes') + node4 = next(node for node in result['return'] if node['node-name'] == 'node4') + self.assertEqual(node4['image']['backing-image']['filename'], self.imgs[0]) + # Test a block-stream and a block-commit job in parallel # Here the stream job is supposed to finish quickly in order to reproduce # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507 @@ -509,9 +523,10 @@ class TestParallelOps(iotests.QMPTestCase): def test_stream_base_node_name(self): self.assert_no_active_block_jobs() - self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[4]), - qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[3]), - 'image file map matches backing file before streaming') + self.assertNotEqual( + qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[4]).stdout, + qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[3]).stdout, + 'image file map matches backing file before streaming') # Error: the base node does not exist result = self.vm.qmp('block-stream', device='node4', base_node='none', job_id='stream') @@ -542,9 +557,10 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]), - qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]), - 'image file map matches backing file after streaming') + self.assertEqual( + qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]).stdout, + qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]).stdout, + 'image file map matches backing file after streaming') class TestQuorum(iotests.QMPTestCase): num_children = 3 @@ -583,9 +599,10 @@ class TestQuorum(iotests.QMPTestCase): os.remove(img) def test_stream_quorum(self): - self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.children[0]), - qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.backing[0]), - 'image file map matches backing file before streaming') + self.assertNotEqual( + qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.children[0]).stdout, + qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.backing[0]).stdout, + 'image file map matches backing file before streaming') self.assert_no_active_block_jobs() @@ -597,9 +614,10 @@ class TestQuorum(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]), - qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]), - 'image file map does not match backing file after streaming') + self.assertEqual( + qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]).stdout, + qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]).stdout, + 'image file map does not match backing file after streaming') class TestSmallerBackingFile(iotests.QMPTestCase): backing_len = 1 * 1024 * 1024 # MB @@ -719,7 +737,8 @@ class TestEIO(TestErrors): if result == {'return': []}: # Job finished too quickly continue - self.assert_qmp(result, 'return[0]/paused', False) + self.assertIn(result['return'][0]['status'], + ['running', 'pending', 'aborting', 'concluded']) elif event['event'] == 'BLOCK_JOB_COMPLETED': self.assertTrue(error, 'job completed unexpectedly') self.assert_qmp(event, 'data/type', 'stream') @@ -749,8 +768,14 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') + if self.vm.qmp('query-block-jobs')['return'][0]['status'] != 'paused': + self.vm.events_wait([( + 'JOB_STATUS_CHANGE', + {'data': {'id': 'drive0', 'status': 'paused'}} + )]) + result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', True) + self.assert_qmp(result, 'return[0]/status', 'paused') self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(result, 'return[0]/io-status', 'failed') @@ -761,7 +786,8 @@ class TestEIO(TestErrors): if result == {'return': []}: # Race; likely already finished. Check. continue - self.assert_qmp(result, 'return[0]/paused', False) + self.assertIn(result['return'][0]['status'], + ['running', 'pending', 'aborting', 'concluded']) self.assert_qmp(result, 'return[0]/io-status', 'ok') elif event['event'] == 'BLOCK_JOB_COMPLETED': self.assertTrue(error, 'job completed unexpectedly') @@ -838,8 +864,14 @@ class TestENOSPC(TestErrors): self.assert_qmp(event, 'data/operation', 'read') error = True + if self.vm.qmp('query-block-jobs')['return'][0]['status'] != 'paused': + self.vm.events_wait([( + 'JOB_STATUS_CHANGE', + {'data': {'id': 'drive0', 'status': 'paused'}} + )]) + result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', True) + self.assert_qmp(result, 'return[0]/status', 'paused') self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(result, 'return[0]/io-status', 'nospace') @@ -850,7 +882,8 @@ class TestENOSPC(TestErrors): if result == {'return': []}: # Race; likely already finished. Check. continue - self.assert_qmp(result, 'return[0]/paused', False) + self.assertIn(result['return'][0]['status'], + ['running', 'pending', 'aborting', 'concluded']) self.assert_qmp(result, 'return[0]/io-status', 'ok') elif event['event'] == 'BLOCK_JOB_COMPLETED': self.assertTrue(error, 'job completed unexpectedly') diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index f703605e44..16a401985c 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -23,7 +23,7 @@ # # creator -owner=stefanha@linux.vnet.ibm.com +owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 00d379cde2..e43e7026ce 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -23,7 +23,7 @@ # # creator -owner=stefanha@linux.vnet.ibm.com +owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 6af5ab9e76..30eb97829e 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -86,8 +86,10 @@ class TestSingleDrive(ImageCommitTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, '-F', iotests.imgfmt, test_img) - qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img) - qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) + if self.image_len: + qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', + mid_img) self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none") self.vm.add_device('virtio-scsi') self.vm.add_device("scsi-hd,id=scsi0,drive=drive0") @@ -101,13 +103,17 @@ class TestSingleDrive(ImageCommitTestCase): def test_commit(self): self.run_commit_test(mid_img, backing_img) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + if not self.image_len: + return + qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) + qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) def test_commit_node(self): self.run_commit_test("mid", "base", node_names=True) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + if not self.image_len: + return + qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) + qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) @iotests.skip_if_unsupported(['throttle']) def test_commit_with_filter_and_quit(self): @@ -192,13 +198,17 @@ class TestSingleDrive(ImageCommitTestCase): def test_top_is_active(self): self.run_commit_test(test_img, backing_img, need_ready=True) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + if not self.image_len: + return + qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) + qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) def test_top_is_default_active(self): self.run_default_commit_test() - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + if not self.image_len: + return + qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) + qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) def test_top_and_base_reversed(self): self.assert_no_active_block_jobs() @@ -334,8 +344,8 @@ class TestRelativePaths(ImageCommitTestCase): def test_commit(self): self.run_commit_test(self.mid_img, self.backing_img) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) + qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs) + qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs) def test_device_not_found(self): result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img) @@ -361,8 +371,8 @@ class TestRelativePaths(ImageCommitTestCase): def test_top_is_active(self): self.run_commit_test(self.test_img, self.backing_img) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) - self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) + qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs) + qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs) def test_top_and_base_reversed(self): self.assert_no_active_block_jobs() @@ -738,12 +748,12 @@ class TestCommitWithFilters(iotests.QMPTestCase): def do_test_io(self, read_or_write): for index, pattern_file in enumerate(self.pattern_files): - result = qemu_io('-f', iotests.imgfmt, - '-c', - f'{read_or_write} -P {index + 1} {index}M 1M', - pattern_file) - self.assertFalse('Pattern verification failed' in result) + qemu_io('-f', iotests.imgfmt, + '-c', + f'{read_or_write} -P {index + 1} {index}M 1M', + pattern_file) + @iotests.skip_if_unsupported(['throttle']) def setUp(self): qemu_img('create', '-f', iotests.imgfmt, self.img0, '64M') qemu_img('create', '-f', iotests.imgfmt, self.img1, '64M') @@ -826,7 +836,8 @@ class TestCommitWithFilters(iotests.QMPTestCase): job_id='commit', device='top-filter', top_node='cow-2', - base_node='cow-1') + base_node='cow-1', + backing_file=self.img1) self.assert_qmp(result, 'return', {}) self.wait_until_completed(drive='commit') @@ -842,7 +853,8 @@ class TestCommitWithFilters(iotests.QMPTestCase): job_id='commit', device='top-filter', top_node='cow-1', - base_node='cow-0') + base_node='cow-0', + backing_file=self.img0) self.assert_qmp(result, 'return', {}) self.wait_until_completed(drive='commit') diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index db9f5dc540..8429958bf0 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -24,7 +24,7 @@ import os import re import json import iotests -from iotests import qemu_img, qemu_img_pipe, qemu_io +from iotests import qemu_img, qemu_img_map, qemu_io backing_img = os.path.join(iotests.test_dir, 'backing.img') target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') @@ -529,7 +529,7 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', False) + self.assertIn(result['return'][0]['status'], ['running', 'ready']) self.complete_and_wait() def test_large_cluster(self): @@ -555,7 +555,7 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', False) + self.assertIn(result['return'][0]['status'], ['running', 'ready']) self.complete_and_wait() self.vm.shutdown() @@ -580,8 +580,14 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') + if self.vm.qmp('query-block-jobs')['return'][0]['status'] != 'paused': + self.vm.events_wait([( + 'JOB_STATUS_CHANGE', + {'data': {'id': 'drive0', 'status': 'paused'}} + )]) + result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', True) + self.assert_qmp(result, 'return[0]/status', 'paused') self.assert_qmp(result, 'return[0]/io-status', 'failed') result = self.vm.qmp('block-job-resume', device='drive0') @@ -593,7 +599,7 @@ new_state = "1" ready = True result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', False) + self.assert_qmp(result, 'return[0]/status', 'ready') self.assert_qmp(result, 'return[0]/io-status', 'ok') self.complete_and_wait(wait_ready=False) @@ -686,7 +692,7 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'write') result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', False) + self.assertIn(result['return'][0]['status'], ['running', 'ready']) self.complete_and_wait() def test_stop_write(self): @@ -705,15 +711,21 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'write') + if self.vm.qmp('query-block-jobs')['return'][0]['status'] != 'paused': + self.vm.events_wait([( + 'JOB_STATUS_CHANGE', + {'data': {'id': 'drive0', 'status': 'paused'}} + )]) + result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', True) + self.assert_qmp(result, 'return[0]/status', 'paused') self.assert_qmp(result, 'return[0]/io-status', 'failed') result = self.vm.qmp('block-job-resume', device='drive0') self.assert_qmp(result, 'return', {}) result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return[0]/paused', False) + self.assertIn(result['return'][0]['status'], ['running', 'ready']) self.assert_qmp(result, 'return[0]/io-status', 'ok') error = True elif event['event'] == 'BLOCK_JOB_READY': @@ -1360,8 +1372,7 @@ class TestFilters(iotests.QMPTestCase): self.vm.qmp('blockdev-del', node_name='target') - target_map = qemu_img_pipe('map', '--output=json', target_img) - target_map = json.loads(target_map) + target_map = qemu_img_map(target_img) assert target_map[0]['start'] == 0 assert target_map[0]['length'] == 512 * 1024 diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056 index b459a3f1e8..bef865eec4 100755 --- a/tests/qemu-iotests/056 +++ b/tests/qemu-iotests/056 @@ -102,7 +102,7 @@ class TestSyncModesNoneAndTop(iotests.QMPTestCase): self.vm.shutdown() time.sleep(1) - self.assertEqual(-1, qemu_io('-c', 'read -P0x41 0 512', target_img).find("verification failed")) + qemu_io('-c', 'read -P0x41 0 512', target_img) class TestBeforeWriteNotifier(iotests.QMPTestCase): def setUp(self): diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 65c0c32b26..e8be217e1f 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -20,7 +20,7 @@ # # creator -owner=famz@redhat.com +owner=fam@euphon.net seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index df87d600f7..5cd21a6f68 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index 513fbec14c..509ad247cd 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/062 b/tests/qemu-iotests/062 index 321252298d..6a71bf1477 100755 --- a/tests/qemu-iotests/062 +++ b/tests/qemu-iotests/062 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/064 b/tests/qemu-iotests/064 index 71fc575b21..21e25cf39f 100755 --- a/tests/qemu-iotests/064 +++ b/tests/qemu-iotests/064 @@ -20,7 +20,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index f7c1b68dad..b724c89c7c 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -24,7 +24,7 @@ import os import re import json import iotests -from iotests import qemu_img, qemu_img_pipe +from iotests import qemu_img, qemu_img_info, supports_qcow2_zstd_compression import unittest test_img = os.path.join(iotests.test_dir, 'test.img') @@ -49,13 +49,12 @@ class TestQemuImgInfo(TestImageInfoSpecific): human_compare = None def test_json(self): - data = json.loads(qemu_img_pipe('info', '--output=json', test_img)) - data = data['format-specific'] + data = qemu_img_info(test_img)['format-specific'] self.assertEqual(data['type'], iotests.imgfmt) self.assertEqual(data['data'], self.json_compare) def test_human(self): - data = qemu_img_pipe('info', '--output=human', test_img).split('\n') + data = qemu_img('info', '--output=human', test_img).stdout.split('\n') data = data[(data.index('Format specific information:') + 1) :data.index('')] for field in data: @@ -96,11 +95,17 @@ class TestQCow2(TestQemuImgInfo): class TestQCow3NotLazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts disabled''' - img_options = 'compat=1.1,lazy_refcounts=off,compression_type=zstd' + if supports_qcow2_zstd_compression(): + compression_type = 'zstd' + else: + compression_type = 'zlib' + + img_options = 'compat=1.1,lazy_refcounts=off' + img_options += f',compression_type={compression_type}' json_compare = { 'compat': '1.1', 'lazy-refcounts': False, 'refcount-bits': 16, 'corrupt': False, - 'compression-type': 'zstd', 'extended-l2': False } - human_compare = [ 'compat: 1.1', 'compression type: zstd', + 'compression-type': compression_type, 'extended-l2': False } + human_compare = [ 'compat: 1.1', f'compression type: {compression_type}', 'lazy refcounts: false', 'refcount bits: 16', 'corrupt: false', 'extended l2: false' ] @@ -127,11 +132,17 @@ class TestQCow3NotLazyQMP(TestQMP): class TestQCow3LazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts enabled, opening with lazy refcounts disabled''' - img_options = 'compat=1.1,lazy_refcounts=on,compression_type=zstd' + if supports_qcow2_zstd_compression(): + compression_type = 'zstd' + else: + compression_type = 'zlib' + + img_options = 'compat=1.1,lazy_refcounts=on' + img_options += f',compression_type={compression_type}' qemu_options = 'lazy-refcounts=off' compare = { 'compat': '1.1', 'lazy-refcounts': True, 'refcount-bits': 16, 'corrupt': False, - 'compression-type': 'zstd', 'extended-l2': False } + 'compression-type': compression_type, 'extended-l2': False } TestImageInfoSpecific = None TestQemuImgInfo = None diff --git a/tests/qemu-iotests/066 b/tests/qemu-iotests/066 index a780ed7ab5..cf63144cb9 100755 --- a/tests/qemu-iotests/066 +++ b/tests/qemu-iotests/066 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068 index 54e49c8ffa..7ecd247409 100755 --- a/tests/qemu-iotests/068 +++ b/tests/qemu-iotests/068 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069 index 222dcba741..6647e11861 100755 --- a/tests/qemu-iotests/069 +++ b/tests/qemu-iotests/069 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/070 b/tests/qemu-iotests/070 index b181e00f9b..edb71afbe3 100755 --- a/tests/qemu-iotests/070 +++ b/tests/qemu-iotests/070 @@ -21,7 +21,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index d99cef5a42..27bc7305bf 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072 index c492ab8a78..662ede961c 100755 --- a/tests/qemu-iotests/072 +++ b/tests/qemu-iotests/072 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/074 b/tests/qemu-iotests/074 index c32c94b50d..ee73e636b2 100755 --- a/tests/qemu-iotests/074 +++ b/tests/qemu-iotests/074 @@ -21,7 +21,7 @@ ## # # creator -owner=famz@redhat.com +owner=fam@euphon.net seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/084 b/tests/qemu-iotests/084 index e51e91a7c8..1181cb7cd0 100755 --- a/tests/qemu-iotests/084 +++ b/tests/qemu-iotests/084 @@ -21,7 +21,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 index de74262a26..3fb7b0b5c8 100755 --- a/tests/qemu-iotests/085 +++ b/tests/qemu-iotests/085 @@ -25,7 +25,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index 48bdc42e42..c68c5a66b9 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/090 b/tests/qemu-iotests/090 index 2044c09e9b..8f88eea9aa 100755 --- a/tests/qemu-iotests/090 +++ b/tests/qemu-iotests/090 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091 index 9d144b9439..e396748a91 100755 --- a/tests/qemu-iotests/091 +++ b/tests/qemu-iotests/091 @@ -22,7 +22,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/094 b/tests/qemu-iotests/094 index a295fb20ef..4766e9a458 100755 --- a/tests/qemu-iotests/094 +++ b/tests/qemu-iotests/094 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 index 20b5f9bf61..d1d347eb1f 100755 --- a/tests/qemu-iotests/095 +++ b/tests/qemu-iotests/095 @@ -23,7 +23,7 @@ # along with this program. If not, see . # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/097 b/tests/qemu-iotests/097 index 30313f8867..93857f4fd0 100755 --- a/tests/qemu-iotests/097 +++ b/tests/qemu-iotests/097 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/098 b/tests/qemu-iotests/098 index 4c37eb0cf5..e3eadb3296 100755 --- a/tests/qemu-iotests/098 +++ b/tests/qemu-iotests/098 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099 index 2f1199ce04..a5d2d30931 100755 --- a/tests/qemu-iotests/099 +++ b/tests/qemu-iotests/099 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102 index 8b4c4c905f..141bfe1e90 100755 --- a/tests/qemu-iotests/102 +++ b/tests/qemu-iotests/102 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103 index 726f8313ef..bb9fd6f650 100755 --- a/tests/qemu-iotests/103 +++ b/tests/qemu-iotests/103 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/105 b/tests/qemu-iotests/105 index d804685110..b8f2029f62 100755 --- a/tests/qemu-iotests/105 +++ b/tests/qemu-iotests/105 @@ -20,7 +20,7 @@ # # creator -owner=famz@redhat.com +owner=fam@euphon.net seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106 index 333144502c..9d6adb542d 100755 --- a/tests/qemu-iotests/106 +++ b/tests/qemu-iotests/106 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/107 b/tests/qemu-iotests/107 index e68f1e07c7..3fabff2791 100755 --- a/tests/qemu-iotests/107 +++ b/tests/qemu-iotests/107 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 8eaef0b8bf..9e923d6a59 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" @@ -30,13 +30,20 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img + _cleanup_test_img + if [ -f "$TEST_DIR/qsd.pid" ]; then + qsd_pid=$(cat "$TEST_DIR/qsd.pid") + kill -KILL "$qsd_pid" + fusermount -u "$TEST_DIR/fuse-export" &>/dev/null + fi + rm -f "$TEST_DIR/fuse-export" } trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks . ./common.rc . ./common.filter +. ./common.qemu # This tests qcow2-specific low-level functionality _supported_fmt qcow2 @@ -47,6 +54,22 @@ _supported_os Linux # files _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file +# This test either needs sudo -n losetup or FUSE exports to work +if sudo -n losetup &>/dev/null; then + loopdev=true +else + loopdev=false + + # QSD --export fuse will either yield "Parameter 'id' is missing" + # or "Invalid parameter 'fuse'", depending on whether there is + # FUSE support or not. + error=$($QSD --export fuse 2>&1) + if [[ $error = *"'fuse'"* ]]; then + _notrun 'Passwordless sudo for losetup or FUSE support required, but' \ + 'neither is available' + fi +fi + echo echo '=== Repairing an image without any refcount table ===' echo @@ -138,6 +161,240 @@ _make_test_img 64M poke_file "$TEST_IMG" $((0x10008)) "\xff\xff\xff\xff\xff\xff\x00\x00" _check_test_img -r all +echo +echo '=== Check rebuilt reftable location ===' + +# In an earlier version of the refcount rebuild algorithm, the +# reftable was generally placed at the image end (unless something was +# allocated in the area covered by the refblock right before the image +# file end, then we would try to place the reftable in that refblock). +# This was later changed so the reftable would be placed in the +# earliest possible location. Test this. + +echo +echo '--- Does the image size increase? ---' +echo + +# First test: Just create some image, write some data to it, and +# resize it so there is free space at the end of the image (enough +# that it spans at least one full refblock, which for cluster_size=512 +# images, spans 128k). With the old algorithm, the reftable would +# have then been placed at the end of the image file, but with the new +# one, it will be put in that free space. +# We want to check whether the size of the image file increases due to +# rebuilding the refcount structures (it should not). + +_make_test_img -o 'cluster_size=512' 1M +# Write something +$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io + +# Add free space +file_len=$(stat -c '%s' "$TEST_IMG") +truncate -s $((file_len + 256 * 1024)) "$TEST_IMG" + +# Corrupt the image by saying the image header was not allocated +rt_offset=$(peek_file_be "$TEST_IMG" 48 8) +rb_offset=$(peek_file_be "$TEST_IMG" $rt_offset 8) +poke_file "$TEST_IMG" $rb_offset "\x00\x00" + +# Check whether rebuilding the refcount structures increases the image +# file size +file_len=$(stat -c '%s' "$TEST_IMG") +echo +# The only leaks there can be are the old refcount structures that are +# leaked during rebuilding, no need to clutter the output with them +_check_test_img -r all | grep -v '^Repairing cluster.*refcount=1 reference=0' +echo +post_repair_file_len=$(stat -c '%s' "$TEST_IMG") + +if [[ $file_len -eq $post_repair_file_len ]]; then + echo 'OK: Image size did not change' +else + echo 'ERROR: Image size differs' \ + "($file_len before, $post_repair_file_len after)" +fi + +echo +echo '--- Will the reftable occupy a hole specifically left for it? ---' +echo + +# Note: With cluster_size=512, every refblock covers 128k. +# The reftable covers 8M per reftable cluster. + +# Create an image that requires two reftable clusters (just because +# this is more interesting than a single-clustered reftable). +_make_test_img -o 'cluster_size=512' 9M +$QEMU_IO -c 'write 0 8M' "$TEST_IMG" | _filter_qemu_io + +# Writing 8M will have resized the reftable. Unfortunately, doing so +# will leave holes in the file, so we need to fill them up so we can +# be sure the whole file is allocated. Do that by writing +# consecutively smaller chunks starting from 8 MB, until the file +# length increases even with a chunk size of 512. Then we must have +# filled all holes. +ofs=$((8 * 1024 * 1024)) +block_len=$((16 * 1024)) +while [[ $block_len -ge 512 ]]; do + file_len=$(stat -c '%s' "$TEST_IMG") + while [[ $(stat -c '%s' "$TEST_IMG") -eq $file_len ]]; do + # Do not include this in the reference output, it does not + # really matter which qemu-io calls we do here exactly + $QEMU_IO -c "write $ofs $block_len" "$TEST_IMG" >/dev/null + ofs=$((ofs + block_len)) + done + block_len=$((block_len / 2)) +done + +# Fill up to 9M (do not include this in the reference output either, +# $ofs is random for all we know) +$QEMU_IO -c "write $ofs $((9 * 1024 * 1024 - ofs))" "$TEST_IMG" >/dev/null + +# Make space as follows: +# - For the first refblock: Right at the beginning of the image (this +# refblock is placed in the first place possible), +# - For the reftable somewhere soon afterwards, still near the +# beginning of the image (i.e. covered by the first refblock); the +# reftable too is placed in the first place possible, but only after +# all refblocks have been placed) +# No space is needed for the other refblocks, because no refblock is +# put before the space it covers. In this test case, we do not mind +# if they are placed at the image file's end. + +# Before we make that space, we have to find out the host offset of +# the area that belonged to the two data clusters at guest offset 4k, +# because we expect the reftable to be placed there, and we will have +# to verify that it is. + +l1_offset=$(peek_file_be "$TEST_IMG" 40 8) +l2_offset=$(peek_file_be "$TEST_IMG" $l1_offset 8) +l2_offset=$((l2_offset & 0x00fffffffffffe00)) +data_4k_offset=$(peek_file_be "$TEST_IMG" \ + $((l2_offset + 4096 / 512 * 8)) 8) +data_4k_offset=$((data_4k_offset & 0x00fffffffffffe00)) + +$QEMU_IO -c "discard 0 512" -c "discard 4k 1k" "$TEST_IMG" | _filter_qemu_io + +# Corrupt the image by saying the image header was not allocated +rt_offset=$(peek_file_be "$TEST_IMG" 48 8) +rb_offset=$(peek_file_be "$TEST_IMG" $rt_offset 8) +poke_file "$TEST_IMG" $rb_offset "\x00\x00" + +echo +# The only leaks there can be are the old refcount structures that are +# leaked during rebuilding, no need to clutter the output with them +_check_test_img -r all | grep -v '^Repairing cluster.*refcount=1 reference=0' +echo + +# Check whether the reftable was put where we expected +rt_offset=$(peek_file_be "$TEST_IMG" 48 8) +if [[ $rt_offset -eq $data_4k_offset ]]; then + echo 'OK: Reftable is where we expect it' +else + echo "ERROR: Reftable is at $rt_offset, but was expected at $data_4k_offset" +fi + +echo +echo '--- Rebuilding refcount structures on block devices ---' +echo + +# A block device cannot really grow, at least not during qemu-img +# check. As mentioned in the above cases, rebuilding the refcount +# structure may lead to new refcount structures being written after +# the end of the image, and in the past that happened even if there +# was more than sufficient space in the image. Such post-EOF writes +# will not work on block devices, so test that the new algorithm +# avoids it. + +# If we have passwordless sudo and losetup, we can use those to create +# a block device. Otherwise, we can resort to qemu's FUSE export to +# create a file that isn't growable, which effectively tests the same +# thing. + +_cleanup_test_img +truncate -s $((64 * 1024 * 1024)) "$TEST_IMG" + +if $loopdev; then + export_mp=$(sudo -n losetup --show -f "$TEST_IMG") + export_mp_driver=host_device + sudo -n chmod go+rw "$export_mp" +else + # Create non-growable FUSE export that is a bit like an empty + # block device + export_mp="$TEST_DIR/fuse-export" + export_mp_driver=file + touch "$export_mp" + + $QSD \ + --blockdev file,node-name=export-node,filename="$TEST_IMG" \ + --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=off,allow-other=off \ + --pidfile "$TEST_DIR/qsd.pid" \ + --daemonize +fi + +# Now create a qcow2 image on the device -- unfortunately, qemu-img +# create force-creates the file, so we have to resort to the +# blockdev-create job. +_launch_qemu \ + --blockdev $export_mp_driver,node-name=file,filename="$export_mp" + +_send_qemu_cmd \ + $QEMU_HANDLE \ + '{ "execute": "qmp_capabilities" }' \ + 'return' + +# Small cluster size again, so the image needs multiple refblocks +_send_qemu_cmd \ + $QEMU_HANDLE \ + '{ "execute": "blockdev-create", + "arguments": { + "job-id": "create", + "options": { + "driver": "qcow2", + "file": "file", + "size": '$((64 * 1024 * 1024))', + "cluster-size": 512 + } } }' \ + '"concluded"' + +_send_qemu_cmd \ + $QEMU_HANDLE \ + '{ "execute": "job-dismiss", "arguments": { "id": "create" } }' \ + 'return' + +_send_qemu_cmd \ + $QEMU_HANDLE \ + '{ "execute": "quit" }' \ + 'return' + +wait=y _cleanup_qemu +echo + +# Write some data +$QEMU_IO -c 'write 0 64k' "$export_mp" | _filter_qemu_io + +# Corrupt the image by saying the image header was not allocated +rt_offset=$(peek_file_be "$export_mp" 48 8) +rb_offset=$(peek_file_be "$export_mp" $rt_offset 8) +poke_file "$export_mp" $rb_offset "\x00\x00" + +# Repairing such a simple case should just work +# (We used to put the reftable at the end of the image file, which can +# never work for non-growable devices.) +echo +TEST_IMG="$export_mp" _check_test_img -r all \ + | grep -v '^Repairing cluster.*refcount=1 reference=0' + +if $loopdev; then + sudo -n losetup -d "$export_mp" +else + qsd_pid=$(cat "$TEST_DIR/qsd.pid") + kill -TERM "$qsd_pid" + # Wait for process to exit (cannot `wait` because the QSD is daemonized) + while [ -f "$TEST_DIR/qsd.pid" ]; do + true + done +fi + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out index 75bab8dc84..b5401d788d 100644 --- a/tests/qemu-iotests/108.out +++ b/tests/qemu-iotests/108.out @@ -105,6 +105,87 @@ The following inconsistencies were found and repaired: 0 leaked clusters 1 corruptions +Double checking the fixed image now... +No errors were found on the image. + +=== Check rebuilt reftable location === + +--- Does the image size increase? --- + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +ERROR cluster 0 refcount=0 reference=1 +Rebuilding refcount structure +The following inconsistencies were found and repaired: + + 0 leaked clusters + 1 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +OK: Image size did not change + +--- Will the reftable occupy a hole specifically left for it? --- + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=9437184 +wrote 8388608/8388608 bytes at offset 0 +8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 1024/1024 bytes at offset 4096 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +ERROR cluster 0 refcount=0 reference=1 +Rebuilding refcount structure +The following inconsistencies were found and repaired: + + 0 leaked clusters + 1 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +OK: Reftable is where we expect it + +--- Rebuilding refcount structures on block devices --- + +{ "execute": "qmp_capabilities" } +{"return": {}} +{ "execute": "blockdev-create", + "arguments": { + "job-id": "create", + "options": { + "driver": "IMGFMT", + "file": "file", + "size": 67108864, + "cluster-size": 512 + } } } +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "create"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "create"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "create"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "create"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "create"}} +{ "execute": "job-dismiss", "arguments": { "id": "create" } } +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "create"}} +{"return": {}} +{ "execute": "quit" } +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +ERROR cluster 0 refcount=0 reference=1 +Rebuilding refcount structure +The following inconsistencies were found and repaired: + + 0 leaked clusters + 1 corruptions + Double checking the fixed image now... No errors were found on the image. *** done diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110 index 1fa36ccdb7..91b15f7513 100755 --- a/tests/qemu-iotests/110 +++ b/tests/qemu-iotests/110 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/111 b/tests/qemu-iotests/111 index 3ba25f6161..382dbf0606 100755 --- a/tests/qemu-iotests/111 +++ b/tests/qemu-iotests/111 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/112 b/tests/qemu-iotests/112 index 5333212993..a2ffc96e60 100755 --- a/tests/qemu-iotests/112 +++ b/tests/qemu-iotests/112 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 index ee59b9a4b8..a3ad208fd7 100755 --- a/tests/qemu-iotests/113 +++ b/tests/qemu-iotests/113 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/115 b/tests/qemu-iotests/115 index 26dd37dd6d..7a24070caa 100755 --- a/tests/qemu-iotests/115 +++ b/tests/qemu-iotests/115 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/117 b/tests/qemu-iotests/117 index 48ebc012b1..6081473584 100755 --- a/tests/qemu-iotests/117 +++ b/tests/qemu-iotests/117 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/119 b/tests/qemu-iotests/119 index 5770b50045..6cac8793ba 100755 --- a/tests/qemu-iotests/119 +++ b/tests/qemu-iotests/119 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/120 b/tests/qemu-iotests/120 index 7187731253..ac7bd8c4e3 100755 --- a/tests/qemu-iotests/120 +++ b/tests/qemu-iotests/120 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/121 b/tests/qemu-iotests/121 index ba3d8d9377..f0dd1d1114 100755 --- a/tests/qemu-iotests/121 +++ b/tests/qemu-iotests/121 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/123 b/tests/qemu-iotests/123 index e19111f70d..4d34a2ac49 100755 --- a/tests/qemu-iotests/123 +++ b/tests/qemu-iotests/123 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125 index bd390b3a99..46279d6b38 100755 --- a/tests/qemu-iotests/125 +++ b/tests/qemu-iotests/125 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/126 b/tests/qemu-iotests/126 index 92c0547746..d8d2d654f2 100755 --- a/tests/qemu-iotests/126 +++ b/tests/qemu-iotests/126 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/127 b/tests/qemu-iotests/127 index 32edc3b068..7cc3ce1d78 100755 --- a/tests/qemu-iotests/127 +++ b/tests/qemu-iotests/127 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/135 b/tests/qemu-iotests/135 index 299075b4c9..71125719ee 100755 --- a/tests/qemu-iotests/135 +++ b/tests/qemu-iotests/135 @@ -20,7 +20,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/138 b/tests/qemu-iotests/138 index 951cfa67d4..76628adab7 100755 --- a/tests/qemu-iotests/138 +++ b/tests/qemu-iotests/138 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/140 b/tests/qemu-iotests/140 index 91e08c30d4..d923b777e2 100755 --- a/tests/qemu-iotests/140 +++ b/tests/qemu-iotests/140 @@ -24,7 +24,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 115cc1691e..a37030ee17 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/143 b/tests/qemu-iotests/143 index 72151acf27..92a081b79e 100755 --- a/tests/qemu-iotests/143 +++ b/tests/qemu-iotests/143 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 index 60e9ddd75f..bdcc498fa2 100755 --- a/tests/qemu-iotests/144 +++ b/tests/qemu-iotests/144 @@ -22,7 +22,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/146 b/tests/qemu-iotests/146 index 98aca96732..661a9d2625 100755 --- a/tests/qemu-iotests/146 +++ b/tests/qemu-iotests/146 @@ -20,7 +20,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index d49646ca60..2ae318f16f 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -265,8 +265,11 @@ def qemu_img_create(config, size_mb): "%dM" % size_mb] iotests.log("qemu-img " + " ".join(args), filters=[iotests.filter_test_dir]) - iotests.log(check_cipher_support(config, iotests.qemu_img_pipe(*args)), - filters=[iotests.filter_test_dir]) + try: + iotests.qemu_img(*args) + except subprocess.CalledProcessError as exc: + check_cipher_support(config, exc.output) + raise def qemu_io_image_args(config, dev=False): """Get the args for access an image or device with qemu-io""" @@ -292,7 +295,8 @@ def qemu_io_write_pattern(config, pattern, offset_mb, size_mb, dev=False): args = ["-c", "write -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)] args.extend(qemu_io_image_args(config, dev)) iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir]) - iotests.log(check_cipher_support(config, iotests.qemu_io(*args)), + output = iotests.qemu_io(*args, check=False).stdout + iotests.log(check_cipher_support(config, output), filters=[iotests.filter_test_dir, iotests.filter_qemu_io]) @@ -304,7 +308,8 @@ def qemu_io_read_pattern(config, pattern, offset_mb, size_mb, dev=False): args = ["-c", "read -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)] args.extend(qemu_io_image_args(config, dev)) iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir]) - iotests.log(check_cipher_support(config, iotests.qemu_io(*args)), + output = iotests.qemu_io(*args, check=False).stdout + iotests.log(check_cipher_support(config, output), filters=[iotests.filter_test_dir, iotests.filter_qemu_io]) diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out index ab879596ce..2cc5b82f7c 100644 --- a/tests/qemu-iotests/149.out +++ b/tests/qemu-iotests/149.out @@ -61,7 +61,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha1.img # ================= qemu-img aes-256-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Write test pattern 0xa7 @@ -180,7 +179,6 @@ unlink TEST_DIR/luks-twofish-256-xts-plain64-sha1.img # ================= qemu-img twofish-256-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=twofish-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Write test pattern 0xa7 @@ -299,7 +297,6 @@ unlink TEST_DIR/luks-serpent-256-xts-plain64-sha1.img # ================= qemu-img serpent-256-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Write test pattern 0xa7 @@ -418,7 +415,6 @@ unlink TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img # ================= qemu-img cast5-128-cbc-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=cast5-128,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Write test pattern 0xa7 @@ -538,7 +534,6 @@ unlink TEST_DIR/luks-aes-256-cbc-plain-sha1.img # ================= qemu-img aes-256-cbc-plain-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Write test pattern 0xa7 @@ -657,7 +652,6 @@ unlink TEST_DIR/luks-aes-256-cbc-plain64-sha1.img # ================= qemu-img aes-256-cbc-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Write test pattern 0xa7 @@ -776,7 +770,6 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img # ================= qemu-img aes-256-cbc-essiv-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Write test pattern 0xa7 @@ -895,7 +888,6 @@ unlink TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img # ================= qemu-img aes-256-xts-essiv-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Write test pattern 0xa7 @@ -1014,7 +1006,6 @@ unlink TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img # ================= qemu-img aes-128-xts-plain64-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Write test pattern 0xa7 @@ -1133,7 +1124,6 @@ unlink TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img # ================= qemu-img aes-192-xts-plain64-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Write test pattern 0xa7 @@ -1252,7 +1242,6 @@ unlink TEST_DIR/luks-twofish-128-xts-plain64-sha1.img # ================= qemu-img twofish-128-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=twofish-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Write test pattern 0xa7 @@ -1372,7 +1361,6 @@ unlink TEST_DIR/luks-serpent-128-xts-plain64-sha1.img # ================= qemu-img serpent-128-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Write test pattern 0xa7 @@ -1491,7 +1479,6 @@ unlink TEST_DIR/luks-serpent-192-xts-plain64-sha1.img # ================= qemu-img serpent-192-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Write test pattern 0xa7 @@ -1612,7 +1599,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha224.img # ================= qemu-img aes-256-xts-plain64-sha224 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha224 TEST_DIR/luks-aes-256-xts-plain64-sha224.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Write test pattern 0xa7 @@ -1731,7 +1717,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha256.img # ================= qemu-img aes-256-xts-plain64-sha256 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha256 TEST_DIR/luks-aes-256-xts-plain64-sha256.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Write test pattern 0xa7 @@ -1850,7 +1835,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha384.img # ================= qemu-img aes-256-xts-plain64-sha384 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha384 TEST_DIR/luks-aes-256-xts-plain64-sha384.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Write test pattern 0xa7 @@ -1969,7 +1953,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha512.img # ================= qemu-img aes-256-xts-plain64-sha512 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha512 TEST_DIR/luks-aes-256-xts-plain64-sha512.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Write test pattern 0xa7 @@ -2088,7 +2071,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img # ================= qemu-img aes-256-xts-plain64-ripemd160 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=ripemd160 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Write test pattern 0xa7 @@ -2281,7 +2263,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img # ================= qemu-img aes-256-xts-plain-sha1-pwallslots ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=c2xvdDE=,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Write test pattern 0xa7 @@ -2400,7 +2381,6 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img # ================= qemu-img aes-256-cbc-essiv-auto-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Write test pattern 0xa7 @@ -2519,7 +2499,6 @@ unlink TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img # ================= qemu-img aes-256-cbc-plain64-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img 4194304M - # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Write test pattern 0xa7 diff --git a/tests/qemu-iotests/150 b/tests/qemu-iotests/150 index ac6930ae20..fab0faa389 100755 --- a/tests/qemu-iotests/150 +++ b/tests/qemu-iotests/150 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index 607af59091..9bc3be8f75 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -20,7 +20,7 @@ # # creator -owner=famz@redhat.com +owner=fam@euphon.net seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index 65dcedd493..a9540bd80d 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -28,7 +28,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index cf17f494d8..94dae60d30 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -21,7 +21,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163 index b8bfc95358..c94ad16f4a 100755 --- a/tests/qemu-iotests/163 +++ b/tests/qemu-iotests/163 @@ -107,17 +107,13 @@ class ShrinkBaseClass(iotests.QMPTestCase): if iotests.imgfmt == 'raw': return - self.assertEqual(qemu_img('check', test_img), 0, - "Verifying image corruption") + qemu_img('check', test_img) def test_empty_image(self): qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, self.shrink_size) - self.assertEqual( - qemu_io('-c', 'read -P 0x00 %s'%self.shrink_size, test_img), - qemu_io('-c', 'read -P 0x00 %s'%self.shrink_size, check_img), - "Verifying image content") + qemu_io('-c', f"read -P 0x00 0 {self.shrink_size}", test_img) self.image_verify() @@ -130,8 +126,7 @@ class ShrinkBaseClass(iotests.QMPTestCase): qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, self.shrink_size) - self.assertEqual(qemu_img("compare", test_img, check_img), 0, - "Verifying image content") + qemu_img("compare", test_img, check_img) self.image_verify() @@ -146,8 +141,7 @@ class ShrinkBaseClass(iotests.QMPTestCase): qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, self.shrink_size) - self.assertEqual(qemu_img("compare", test_img, check_img), 0, - "Verifying image content") + qemu_img("compare", test_img, check_img) self.image_verify() diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out index 4cf4d536b4..9479b92185 100644 --- a/tests/qemu-iotests/172.out +++ b/tests/qemu-iotests/172.out @@ -15,7 +15,6 @@ Testing: fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -43,7 +42,6 @@ Testing: -fda TEST_DIR/t.qcow2 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -81,7 +79,6 @@ Testing: -fdb TEST_DIR/t.qcow2 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -135,7 +132,6 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -190,7 +186,6 @@ Testing: -fdb fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -230,7 +225,6 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -268,7 +262,6 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -322,7 +315,6 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -380,7 +372,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -418,7 +409,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -456,7 +446,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -520,7 +509,6 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -575,7 +563,6 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -630,7 +617,6 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -685,7 +671,6 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -749,7 +734,6 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -804,7 +788,6 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -865,7 +848,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global floppy.drive=none0 -device fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -933,7 +915,6 @@ Testing: -device floppy fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -958,7 +939,6 @@ Testing: -device floppy,drive-type=120 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -983,7 +963,6 @@ Testing: -device floppy,drive-type=144 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -1008,7 +987,6 @@ Testing: -device floppy,drive-type=288 fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -1036,7 +1014,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -1074,7 +1051,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -1115,7 +1091,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" @@ -1153,7 +1128,6 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica fdtypeA = "auto" fdtypeB = "auto" fallback = "288" - isa irq 6 bus: floppy-bus.0 type floppy-bus dev: floppy, id "" diff --git a/tests/qemu-iotests/173 b/tests/qemu-iotests/173 index 9594f3c5ea..217e55c168 100755 --- a/tests/qemu-iotests/173 +++ b/tests/qemu-iotests/173 @@ -20,7 +20,7 @@ # along with this program. If not, see . # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 index 27ac25467f..a6a2a4cd44 100755 --- a/tests/qemu-iotests/176 +++ b/tests/qemu-iotests/176 @@ -25,7 +25,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 index fcd1d796eb..bbd1132b05 100755 --- a/tests/qemu-iotests/182 +++ b/tests/qemu-iotests/182 @@ -20,7 +20,7 @@ # # creator -owner=famz@redhat.com +owner=fam@euphon.net seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index f2ec5c5ceb..8b1143dc16 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -33,6 +33,12 @@ _cleanup() _rm_test_img "${TEST_IMG}.copy" _cleanup_test_img _cleanup_qemu + + if [ -f "$TEST_DIR/qsd.pid" ]; then + kill -SIGKILL "$(cat "$TEST_DIR/qsd.pid")" + rm -f "$TEST_DIR/qsd.pid" + fi + rm -f "$SOCK_DIR/qsd.sock" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -45,7 +51,7 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux -size=64M +size=$((64 * 1048576)) TEST_IMG="${TEST_IMG}.base" _make_test_img $size echo @@ -216,6 +222,188 @@ wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' _check_test_img +echo +echo === Start mirror to throttled QSD and exit qemu === +echo + +# Mirror to a throttled QSD instance (so that qemu cannot drain the +# throttling), wait for READY, then write some data to the device, +# and then quit qemu. +# (qemu should force-cancel the job and not wait for the data to be +# written to the target.) + +_make_test_img $size + +# Will be used by this and the next case +set_up_throttled_qsd() { + $QSD \ + --object throttle-group,id=thrgr,limits.bps-total=1048576 \ + --blockdev null-co,node-name=null,size=$size \ + --blockdev throttle,node-name=throttled,throttle-group=thrgr,file=null \ + --nbd-server addr.type=unix,addr.path="$SOCK_DIR/qsd.sock" \ + --export nbd,id=exp,node-name=throttled,name=target,writable=true \ + --pidfile "$TEST_DIR/qsd.pid" \ + --daemonize +} + +set_up_throttled_qsd + +# Need a virtio-blk device so that qemu-io writes will not block the monitor +_launch_qemu \ + --blockdev file,node-name=source-proto,filename="$TEST_IMG" \ + --blockdev qcow2,node-name=source-fmt,file=source-proto \ + --device virtio-blk,id=vblk,drive=source-fmt \ + --blockdev "{\"driver\": \"nbd\", + \"node-name\": \"target\", + \"server\": { + \"type\": \"unix\", + \"path\": \"$SOCK_DIR/qsd.sock\" + }, + \"export\": \"target\"}" + +h=$QEMU_HANDLE +_send_qemu_cmd $h '{"execute": "qmp_capabilities"}' 'return' + +# Use sync=top, so the first pass will not copy the whole image +_send_qemu_cmd $h \ + '{"execute": "blockdev-mirror", + "arguments": { + "job-id": "mirror", + "device": "source-fmt", + "target": "target", + "sync": "top" + }}' \ + 'return' \ + | grep -v JOB_STATUS_CHANGE # Ignore these events during creation + +# This too will be used by this and the next case +# $1: QEMU handle +# $2: Image size +wait_for_job_and_quit() { + h=$1 + size=$2 + + # List of expected events + capture_events='BLOCK_JOB_READY JOB_STATUS_CHANGE' + _wait_event $h 'BLOCK_JOB_READY' + QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before READY + + # Write something to the device for post-READY mirroring. Write it in + # blocks matching the cluster size, each spaced one block apart, so + # that the mirror job will have to spawn one request per cluster. + # Because the number of concurrent requests is limited (to 16), this + # limits the number of bytes concurrently in flight, which speeds up + # cancelling the job (in-flight requests still are waited for). + # To limit the number of bytes in flight, we could alternatively pass + # something for blockdev-mirror's @buf-size parameter, but + # block-commit does not have such a parameter, so we need to figure + # something out that works for both. + + cluster_size=65536 + step=$((cluster_size * 2)) + + echo '--- Writing data to the virtio-blk device ---' + + for ofs in $(seq 0 $step $((size - step))); do + qemu_io_cmd="qemu-io -d vblk/virtio-backend " + qemu_io_cmd+="\\\"aio_write $ofs $cluster_size\\\"" + + # Do not include these requests in the reference output + # (it's just too much) + silent=yes _send_qemu_cmd $h \ + "{\"execute\": \"human-monitor-command\", + \"arguments\": { + \"command-line\": \"$qemu_io_cmd\" + }}" \ + 'return' + done + + # Wait until the job's length is updated to reflect the write requests + + # We have written to half of the device, so this is the expected job length + final_len=$((size / 2)) + timeout=100 # unit: 0.1 seconds + while true; do + len=$( + _send_qemu_cmd $h \ + '{"execute": "query-block-jobs"}' \ + 'return.*"len": [0-9]\+' \ + | grep 'return.*"len": [0-9]\+' \ + | sed -e 's/.*"len": \([0-9]\+\).*/\1/' + ) + if [ "$len" -eq "$final_len" ]; then + break + fi + timeout=$((timeout - 1)) + if [ "$timeout" -eq 0 ]; then + echo "ERROR: Timeout waiting for job to reach len=$final_len" + break + fi + sleep 0.1 + done + + sleep 1 + + _send_qemu_cmd $h \ + '{"execute": "quit"}' \ + 'return' + + # List of expected events + capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN' + _wait_event $h 'SHUTDOWN' + QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before SHUTDOWN + _wait_event $h 'JOB_STATUS_CHANGE' # standby + _wait_event $h 'JOB_STATUS_CHANGE' # ready + _wait_event $h 'JOB_STATUS_CHANGE' # aborting + # Filter the offset (depends on when exactly `quit` was issued) + _wait_event $h 'BLOCK_JOB_CANCELLED' \ + | sed -e 's/"offset": [0-9]\+/"offset": (filtered)/' + _wait_event $h 'JOB_STATUS_CHANGE' # concluded + _wait_event $h 'JOB_STATUS_CHANGE' # null + + wait=yes _cleanup_qemu + + kill -SIGTERM "$(cat "$TEST_DIR/qsd.pid")" +} + +wait_for_job_and_quit $h $size + +echo +echo === Start active commit to throttled QSD and exit qemu === +echo + +# Same as the above, but instead of mirroring, do an active commit + +_make_test_img $size + +set_up_throttled_qsd + +_launch_qemu \ + --blockdev "{\"driver\": \"nbd\", + \"node-name\": \"target\", + \"server\": { + \"type\": \"unix\", + \"path\": \"$SOCK_DIR/qsd.sock\" + }, + \"export\": \"target\"}" \ + --blockdev file,node-name=source-proto,filename="$TEST_IMG" \ + --blockdev qcow2,node-name=source-fmt,file=source-proto,backing=target \ + --device virtio-blk,id=vblk,drive=source-fmt + +h=$QEMU_HANDLE +_send_qemu_cmd $h '{"execute": "qmp_capabilities"}' 'return' + +_send_qemu_cmd $h \ + '{"execute": "block-commit", + "arguments": { + "job-id": "commit", + "device": "source-fmt" + }}' \ + 'return' \ + | grep -v JOB_STATUS_CHANGE # Ignore these events during creation + +wait_for_job_and_quit $h $size + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 754a641258..70e8dd6c87 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -116,4 +116,52 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}} No errors were found on the image. + +=== Start mirror to throttled QSD and exit qemu === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "blockdev-mirror", + "arguments": { + "job-id": "mirror", + "device": "source-fmt", + "target": "target", + "sync": "top" + }} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +--- Writing data to the virtio-blk device --- +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "mirror", "len": 33554432, "offset": (filtered), "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}} + +=== Start active commit to throttled QSD and exit qemu === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "block-commit", + "arguments": { + "job-id": "commit", + "device": "source-fmt" + }} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "commit", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +--- Writing data to the virtio-blk device --- +{"execute": "quit"} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "commit", "len": 33554432, "offset": (filtered), "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "commit"}} *** done diff --git a/tests/qemu-iotests/192 b/tests/qemu-iotests/192 index d809187fca..e66e1a4f06 100755 --- a/tests/qemu-iotests/192 +++ b/tests/qemu-iotests/192 @@ -21,7 +21,7 @@ # # creator -owner=famz@redhat.com +owner=fam@euphon.net seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index e44b8df728..68894371f5 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -33,8 +33,8 @@ with iotests.FilePath('source.img') as source_img_path, \ iotests.VM('dest') as dest_vm: img_size = '1G' - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, source_img_path, img_size) - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, dest_img_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, source_img_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, dest_img_path, img_size) iotests.log('Launching VMs...') (source_vm.add_drive(source_img_path) diff --git a/tests/qemu-iotests/200 b/tests/qemu-iotests/200 index f80517e342..f66c571d24 100755 --- a/tests/qemu-iotests/200 +++ b/tests/qemu-iotests/200 @@ -22,7 +22,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202 index 8eb5f32d15..b784dcd791 100755 --- a/tests/qemu-iotests/202 +++ b/tests/qemu-iotests/202 @@ -35,8 +35,8 @@ with iotests.FilePath('disk0.img') as disk0_img_path, \ iotests.VM() as vm: img_size = '10M' - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size) - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, disk0_img_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, disk1_img_path, img_size) iotests.log('Launching VM...') vm.launch() diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203 index ea30e50497..ab80fd0e44 100755 --- a/tests/qemu-iotests/203 +++ b/tests/qemu-iotests/203 @@ -33,8 +33,8 @@ with iotests.FilePath('disk0.img') as disk0_img_path, \ iotests.VM() as vm: img_size = '10M' - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size) - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, disk0_img_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, disk1_img_path, img_size) iotests.log('Launching VM...') (vm.add_object('iothread,id=iothread0') diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 index c0e107328f..15f798288a 100755 --- a/tests/qemu-iotests/205 +++ b/tests/qemu-iotests/205 @@ -85,13 +85,13 @@ class TestNbdServerRemove(iotests.QMPTestCase): def do_test_connect_after_remove(self, mode=None): args = ('-r', '-f', 'raw', '-c', 'read 0 512', nbd_uri) - self.assertReadOk(qemu_io(*args)) + self.assertReadOk(qemu_io(*args).stdout) result = self.remove_export('exp', mode) self.assert_qmp(result, 'return', {}) self.assertExportNotFound('exp') - self.assertConnectFailed(qemu_io(*args)) + self.assertConnectFailed(qemu_io(*args, check=False).stdout) def test_connect_after_remove_default(self): self.do_test_connect_after_remove() diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index 0f5c4bc8a0..41dcf3ff55 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -35,7 +35,12 @@ def filter_hash(qmsg): if key == 'hash' and re.match('[0-9a-f]+', value): return 'HASH' return value - return iotests.filter_qmp(qmsg, _filter) + if isinstance(qmsg, str): + # Strip key type and fingerprint + p = r"\S+ (key fingerprint) '(md5|sha1|sha256):[0-9a-f]+'" + return re.sub(p, r"\1 '\2:HASH'", qmsg) + else: + return iotests.filter_qmp(qmsg, _filter) def blockdev_create(vm, options): vm.blockdev_create(options, filters=[iotests.filter_qmp_testfiles, filter_hash]) diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out index aeb8569d77..05cf753283 100644 --- a/tests/qemu-iotests/207.out +++ b/tests/qemu-iotests/207.out @@ -42,7 +42,7 @@ virtual size: 4 MiB (4194304 bytes) {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}} {"return": {}} -Job failed: remote host key does not match host_key_check 'wrong' +Job failed: remote host key fingerprint 'md5:HASH' does not match host_key_check 'md5:wrong' {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} @@ -59,7 +59,7 @@ virtual size: 8 MiB (8388608 bytes) {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}} {"return": {}} -Job failed: remote host key does not match host_key_check 'wrong' +Job failed: remote host key fingerprint 'sha1:HASH' does not match host_key_check 'sha1:wrong' {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} @@ -76,7 +76,7 @@ virtual size: 4 MiB (4194304 bytes) {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha256"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}} {"return": {}} -Job failed: remote host key does not match host_key_check 'wrong' +Job failed: remote host key fingerprint 'sha256:HASH' does not match host_key_check 'sha256:wrong' {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index f52cadade1..1a3b4596c8 100755 --- a/tests/qemu-iotests/211 +++ b/tests/qemu-iotests/211 @@ -59,7 +59,7 @@ with iotests.FilePath('t.vdi') as disk_path, \ vm.shutdown() iotests.img_info_log(disk_path) - iotests.log(iotests.qemu_img_pipe('map', '--output=json', disk_path)) + iotests.log(iotests.qemu_img_map(disk_path)) # # Successful image creation (explicit defaults) @@ -83,7 +83,7 @@ with iotests.FilePath('t.vdi') as disk_path, \ vm.shutdown() iotests.img_info_log(disk_path) - iotests.log(iotests.qemu_img_pipe('map', '--output=json', disk_path)) + iotests.log(iotests.qemu_img_map(disk_path)) # # Successful image creation (with non-default options) @@ -107,7 +107,7 @@ with iotests.FilePath('t.vdi') as disk_path, \ vm.shutdown() iotests.img_info_log(disk_path) - iotests.log(iotests.qemu_img_pipe('map', '--output=json', disk_path)) + iotests.log(iotests.qemu_img_map(disk_path)) # # Invalid BlockdevRef diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out index c4425b5982..f02c75409c 100644 --- a/tests/qemu-iotests/211.out +++ b/tests/qemu-iotests/211.out @@ -17,8 +17,7 @@ file format: IMGFMT virtual size: 128 MiB (134217728 bytes) cluster_size: 1048576 -[{ "start": 0, "length": 134217728, "depth": 0, "present": true, "zero": true, "data": false}] - +[{"data": false, "depth": 0, "length": 134217728, "present": true, "start": 0, "zero": true}] === Successful image creation (explicit defaults) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -36,8 +35,7 @@ file format: IMGFMT virtual size: 64 MiB (67108864 bytes) cluster_size: 1048576 -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false}] - +[{"data": false, "depth": 0, "length": 67108864, "present": true, "start": 0, "zero": true}] === Successful image creation (with non-default options) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -55,9 +53,7 @@ file format: IMGFMT virtual size: 32 MiB (33554432 bytes) cluster_size: 1048576 -[{ "start": 0, "length": 3072, "depth": 0, "present": true, "zero": false, "data": true, "offset": 1024}, -{ "start": 3072, "length": 33551360, "depth": 0, "present": true, "zero": true, "data": true, "offset": 4096}] - +[{"data": true, "depth": 0, "length": 3072, "offset": 1024, "present": true, "start": 0, "zero": false}, {"data": true, "depth": 0, "length": 33551360, "offset": 4096, "present": true, "start": 3072, "zero": true}] === Invalid BlockdevRef === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}} diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216 index c02f8d2880..311e02af3a 100755 --- a/tests/qemu-iotests/216 +++ b/tests/qemu-iotests/216 @@ -18,10 +18,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Creator/Owner: Max Reitz +# Creator/Owner: Hanna Reitz import iotests -from iotests import log, qemu_img, qemu_io_silent +from iotests import log, qemu_img, qemu_io # Need backing file support iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'], @@ -51,11 +51,11 @@ with iotests.FilePath('base.img') as base_img_path, \ log('--- Setting up images ---') log('') - assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 - assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0 - assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, - '-F', iotests.imgfmt, top_img_path) == 0 - assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0 + qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') + qemu_io(base_img_path, '-c', 'write -P 1 0M 1M') + qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + '-F', iotests.imgfmt, top_img_path) + qemu_io(top_img_path, '-c', 'write -P 2 1M 1M') log('Done') @@ -110,8 +110,8 @@ with iotests.FilePath('base.img') as base_img_path, \ log('--- Checking COR result ---') log('') - assert qemu_io_silent(base_img_path, '-c', 'discard 0 64M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 1 0M 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0 + qemu_io(base_img_path, '-c', 'discard 0 64M') + qemu_io(top_img_path, '-c', 'read -P 1 0M 1M') + qemu_io(top_img_path, '-c', 'read -P 2 1M 1M') log('Done') diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 index 4922b4d3b6..6320c4cb56 100755 --- a/tests/qemu-iotests/218 +++ b/tests/qemu-iotests/218 @@ -25,10 +25,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Creator/Owner: Max Reitz +# Creator/Owner: Hanna Reitz import iotests -from iotests import log, qemu_img, qemu_io_silent +from iotests import log, qemu_img, qemu_io iotests.script_initialize(supported_fmts=['qcow2', 'raw']) @@ -145,9 +145,8 @@ log('') with iotests.VM() as vm, \ iotests.FilePath('src.img') as src_img_path: - assert qemu_img('create', '-f', iotests.imgfmt, src_img_path, '64M') == 0 - assert qemu_io_silent('-f', iotests.imgfmt, src_img_path, - '-c', 'write -P 42 0M 64M') == 0 + qemu_img('create', '-f', iotests.imgfmt, src_img_path, '64M') + qemu_io('-f', iotests.imgfmt, src_img_path, '-c', 'write -P 42 0M 64M') vm.launch() diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 index da87f2f4a2..0bbb283010 100755 --- a/tests/qemu-iotests/223 +++ b/tests/qemu-iotests/223 @@ -120,6 +120,11 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", "arguments":{"node":"n", "name":"b"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", + "arguments":{"driver":"null-co", "node-name":"null", + "size": 4194304}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-add", + "arguments":{"node":"null", "name":"b3"}}' "return" for attempt in normal iothread; do @@ -155,6 +160,9 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "writable":true, "description":"some text", "bitmap":"b2"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-export-add", + "arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3", + "bitmaps":[{"node":"null","name":"b3"}]}}' "return" $QEMU_NBD_PROG -L -k "$SOCK_DIR/nbd" echo @@ -178,6 +186,14 @@ IMG="driver=nbd,export=n2,server.type=unix,server.path=$SOCK_DIR/nbd" $QEMU_IMG map --output=json --image-opts \ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map +echo +echo "=== Check bitmap taken from another node ===" +echo + +IMG="driver=nbd,export=n3,server.type=unix,server.path=$SOCK_DIR/nbd" +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b3" | _filter_qemu_img_map + echo echo "=== End qemu NBD server ===" echo diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out index e58ea5abbd..0647941531 100644 --- a/tests/qemu-iotests/223.out +++ b/tests/qemu-iotests/223.out @@ -33,6 +33,13 @@ wrote 2097152/2097152 bytes at offset 2097152 {"execute":"block-dirty-bitmap-disable", "arguments":{"node":"n", "name":"b"}} {"return": {}} +{"execute":"blockdev-add", + "arguments":{"driver":"null-co", "node-name":"null", + "size": 4194304}} +{"return": {}} +{"execute":"block-dirty-bitmap-add", + "arguments":{"node":"null", "name":"b3"}} +{"return": {}} === Set up NBD with normal access === @@ -69,7 +76,11 @@ exports available: 0 "arguments":{"device":"n", "name":"n2", "writable":true, "description":"some text", "bitmap":"b2"}} {"return": {}} -exports available: 2 +{"execute":"block-export-add", + "arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3", + "bitmaps":[{"node":"null","name":"b3"}]}} +{"return": {}} +exports available: 3 export: 'n' size: 4194304 flags: 0x58f ( readonly flush fua df multi cache ) @@ -89,6 +100,15 @@ exports available: 2 available meta contexts: 2 base:allocation qemu:dirty-bitmap:b2 + export: 'n3' + size: 4194304 + flags: 0x58f ( readonly flush fua df multi cache ) + min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 2 + base:allocation + qemu:dirty-bitmap:b3 === Contrast normal status to large granularity dirty-bitmap === @@ -114,6 +134,10 @@ read 2097152/2097152 bytes at offset 2097152 { "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, { "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] +=== Check bitmap taken from another node === + +[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] + === End qemu NBD server === {"execute":"nbd-server-remove", @@ -128,6 +152,7 @@ read 2097152/2097152 bytes at offset 2097152 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} {"execute":"nbd-server-stop"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}} {"return": {}} {"execute":"nbd-server-stop"} {"error": {"class": "GenericError", "desc": "NBD server not running"}} @@ -170,7 +195,11 @@ exports available: 0 "arguments":{"device":"n", "name":"n2", "writable":true, "description":"some text", "bitmap":"b2"}} {"return": {}} -exports available: 2 +{"execute":"block-export-add", + "arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3", + "bitmaps":[{"node":"null","name":"b3"}]}} +{"return": {}} +exports available: 3 export: 'n' size: 4194304 flags: 0x58f ( readonly flush fua df multi cache ) @@ -190,6 +219,15 @@ exports available: 2 available meta contexts: 2 base:allocation qemu:dirty-bitmap:b2 + export: 'n3' + size: 4194304 + flags: 0x58f ( readonly flush fua df multi cache ) + min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 2 + base:allocation + qemu:dirty-bitmap:b3 === Contrast normal status to large granularity dirty-bitmap === @@ -215,6 +253,10 @@ read 2097152/2097152 bytes at offset 2097152 { "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, { "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] +=== Check bitmap taken from another node === + +[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] + === End qemu NBD server === {"execute":"nbd-server-remove", @@ -229,6 +271,7 @@ read 2097152/2097152 bytes at offset 2097152 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} {"execute":"nbd-server-stop"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}} {"return": {}} {"execute":"nbd-server-stop"} {"error": {"class": "GenericError", "desc": "NBD server not running"}} diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224 index 38dd153625..542d0eefa6 100755 --- a/tests/qemu-iotests/224 +++ b/tests/qemu-iotests/224 @@ -19,10 +19,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Creator/Owner: Max Reitz +# Creator/Owner: Hanna Reitz import iotests -from iotests import log, qemu_img, qemu_io_silent, filter_qmp_testfiles, \ +from iotests import log, qemu_img, qemu_io, filter_qmp_testfiles, \ filter_qmp_imgfmt import json @@ -47,15 +47,14 @@ for filter_node_name in False, True: iotests.FilePath('top.img') as top_img_path, \ iotests.VM() as vm: - assert qemu_img('create', '-f', iotests.imgfmt, - base_img_path, '64M') == 0 - assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, - '-F', iotests.imgfmt, mid_img_path) == 0 - assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path, - '-F', iotests.imgfmt, top_img_path) == 0 + qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') + qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + '-F', iotests.imgfmt, mid_img_path) + qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path, + '-F', iotests.imgfmt, top_img_path) # Something to commit - assert qemu_io_silent(mid_img_path, '-c', 'write -P 1 0 1M') == 0 + qemu_io(mid_img_path, '-c', 'write -P 1 0 1M') vm.launch() diff --git a/tests/qemu-iotests/225 b/tests/qemu-iotests/225 index c0053790db..b5949fcb58 100755 --- a/tests/qemu-iotests/225 +++ b/tests/qemu-iotests/225 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/228 b/tests/qemu-iotests/228 index a5eda2e149..7341777f9f 100755 --- a/tests/qemu-iotests/228 +++ b/tests/qemu-iotests/228 @@ -19,7 +19,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Creator/Owner: Max Reitz +# Creator/Owner: Hanna Reitz import iotests from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \ @@ -54,11 +54,11 @@ with iotests.FilePath('base.img') as base_img_path, \ iotests.FilePath('top.img') as top_img_path, \ iotests.VM() as vm: - assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 + qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') # Choose a funny way to describe the backing filename - assert qemu_img('create', '-f', iotests.imgfmt, '-b', - 'file:' + base_img_path, '-F', iotests.imgfmt, - top_img_path) == 0 + qemu_img('create', '-f', iotests.imgfmt, '-b', + 'file:' + base_img_path, '-F', iotests.imgfmt, + top_img_path) vm.launch() @@ -172,8 +172,8 @@ with iotests.FilePath('base.img') as base_img_path, \ # (because qemu cannot "canonicalize"/"resolve" the backing # filename unless the backing file is opened implicitly with the # overlay) - assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, - '-F', iotests.imgfmt, top_img_path) == 0 + qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + '-F', iotests.imgfmt, top_img_path) # You can only reliably override backing options by using a node # reference (or by specifying file.filename, but, well...) diff --git a/tests/qemu-iotests/229 b/tests/qemu-iotests/229 index 4bc99390b5..aaa6996ce3 100755 --- a/tests/qemu-iotests/229 +++ b/tests/qemu-iotests/229 @@ -21,7 +21,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq="$(basename $0)" echo "QA output created by $seq" diff --git a/tests/qemu-iotests/231 b/tests/qemu-iotests/231 index 8e6c6447c1..eddc8e9641 100755 --- a/tests/qemu-iotests/231 +++ b/tests/qemu-iotests/231 @@ -22,7 +22,7 @@ # # creator -owner=jcody@redhat.com +owner=codyprime@gmail.com seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 index 9ca7b68f42..55db5b3811 100755 --- a/tests/qemu-iotests/233 +++ b/tests/qemu-iotests/233 @@ -61,11 +61,13 @@ tls_x509_create_server "ca1" "server1" tls_x509_create_client "ca1" "client1" tls_x509_create_client "ca2" "client2" tls_x509_create_client "ca1" "client3" +tls_psk_create_creds "psk1" +tls_psk_create_creds "psk2" echo echo "== preparing image ==" _make_test_img 64M -$QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" 2>&1 | _filter_qemu_io echo echo "== check TLS client to plain server fails ==" @@ -74,9 +76,9 @@ nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" 2> "$TEST_DIR/server.log" obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 $QEMU_IMG info --image-opts --object $obj \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ - 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + 2>&1 | _filter_nbd $QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \ - --tls-creds=tls0 + --tls-creds=tls0 2>&1 | _filter_qemu_nbd_exports nbd_server_stop @@ -88,8 +90,10 @@ nbd_server_start_tcp_socket \ --tls-creds tls0 \ -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log" -$QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g" -$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port +$QEMU_IMG info nbd://localhost:$nbd_tcp_port \ + 2>&1 | _filter_nbd +$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port \ + 2>&1 | _filter_qemu_nbd_exports echo echo "== check TLS works ==" @@ -97,21 +101,39 @@ obj1=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 obj2=tls-creds-x509,dir=${tls_dir}/client3,endpoint=client,id=tls0 $QEMU_IMG info --image-opts --object $obj1 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ - 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + 2>&1 | _filter_nbd $QEMU_IMG info --image-opts --object $obj2 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ - 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + 2>&1 | _filter_nbd $QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj1 \ - --tls-creds=tls0 + --tls-creds=tls0 2>&1 | _filter_qemu_nbd_exports + +echo +echo "== check TLS fail over TCP with mismatched hostname ==" +obj1=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj1 \ + driver=nbd,host=localhost,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | _filter_nbd +$QEMU_NBD_PROG -L -b localhost -p $nbd_tcp_port --object $obj1 \ + --tls-creds=tls0 | _filter_qemu_nbd_exports + +echo +echo "== check TLS works over TCP with mismatched hostname and override ==" +obj1=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj1 \ + driver=nbd,host=localhost,port=$nbd_tcp_port,tls-creds=tls0,tls-hostname=127.0.0.1 \ + 2>&1 | _filter_nbd +$QEMU_NBD_PROG -L -b localhost -p $nbd_tcp_port --object $obj1 \ + --tls-creds=tls0 --tls-hostname=127.0.0.1 | _filter_qemu_nbd_exports echo echo "== check TLS with different CA fails ==" obj=tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 $QEMU_IMG info --image-opts --object $obj \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ - 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + 2>&1 | _filter_nbd $QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \ - --tls-creds=tls0 + --tls-creds=tls0 2>&1 | _filter_qemu_nbd_exports echo echo "== perform I/O over TLS ==" @@ -121,7 +143,8 @@ $QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ 2>&1 | _filter_qemu_io -$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" \ + 2>&1 | _filter_qemu_io echo echo "== check TLS with authorization ==" @@ -139,12 +162,62 @@ nbd_server_start_tcp_socket \ $QEMU_IMG info --image-opts \ --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ - 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + 2>&1 | _filter_nbd $QEMU_IMG info --image-opts \ --object tls-creds-x509,dir=${tls_dir}/client3,endpoint=client,id=tls0 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ - 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + 2>&1 | _filter_nbd + +nbd_server_stop + +nbd_server_start_unix_socket \ + --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=on \ + --tls-creds tls0 \ + -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log" + +echo +echo "== check TLS fail over UNIX with no hostname ==" +obj1=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj1 \ + driver=nbd,path=$nbd_unix_socket,tls-creds=tls0 2>&1 | _filter_nbd +$QEMU_NBD_PROG -L -k $nbd_unix_socket --object $obj1 --tls-creds=tls0 \ + 2>&1 | _filter_qemu_nbd_exports + +echo +echo "== check TLS works over UNIX with hostname override ==" +obj1=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj1 \ + driver=nbd,path=$nbd_unix_socket,tls-creds=tls0,tls-hostname=127.0.0.1 \ + 2>&1 | _filter_nbd +$QEMU_NBD_PROG -L -k $nbd_unix_socket --object $obj1 \ + --tls-creds=tls0 --tls-hostname=127.0.0.1 2>&1 | _filter_qemu_nbd_exports + + +echo +echo "== check TLS works over UNIX with PSK ==" +nbd_server_stop + +nbd_server_start_unix_socket \ + --object tls-creds-psk,dir=${tls_dir}/psk1,endpoint=server,id=tls0,verify-peer=on \ + --tls-creds tls0 \ + -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log" + +obj1=tls-creds-psk,dir=${tls_dir}/psk1,username=psk1,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj1 \ + driver=nbd,path=$nbd_unix_socket,tls-creds=tls0 \ + 2>&1 | _filter_nbd +$QEMU_NBD_PROG -L -k $nbd_unix_socket --object $obj1 \ + --tls-creds=tls0 2>&1 | _filter_qemu_nbd_exports + +echo +echo "== check TLS fails over UNIX with mismatch PSK ==" +obj1=tls-creds-psk,dir=${tls_dir}/psk2,username=psk2,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj1 \ + driver=nbd,path=$nbd_unix_socket,tls-creds=tls0 \ + 2>&1 | _filter_nbd +$QEMU_NBD_PROG -L -k $nbd_unix_socket --object $obj1 \ + --tls-creds=tls0 2>&1 | _filter_qemu_nbd_exports echo echo "== final server log ==" diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out index 4b1f6a0e15..237c82767e 100644 --- a/tests/qemu-iotests/233.out +++ b/tests/qemu-iotests/233.out @@ -7,6 +7,8 @@ Generating a signed certificate... Generating a signed certificate... Generating a signed certificate... Generating a signed certificate... +Generating a random key for user 'psk1' +Generating a random key for user 'psk2' == preparing image == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -17,15 +19,12 @@ wrote 1048576/1048576 bytes at offset 1048576 qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls) server reported: TLS not configured qemu-nbd: Denied by server for option 5 (starttls) -server reported: TLS not configured == check plain client to TLS server fails == qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 7 (go) Did you forget a valid tls-creds? server reported: Option 0x7 not permitted before TLS qemu-nbd: TLS negotiation required before option 3 (list) -Did you forget a valid tls-creds? -server reported: Option 0x3 not permitted before TLS == check TLS works == image: nbd://127.0.0.1:PORT @@ -39,12 +38,21 @@ disk size: unavailable exports available: 1 export: '' size: 67108864 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) min block: 1 - opt block: 4096 - max block: 33554432 - available meta contexts: 1 - base:allocation + +== check TLS fail over TCP with mismatched hostname == +qemu-img: Could not open 'driver=nbd,host=localhost,port=PORT,tls-creds=tls0': Certificate does not match the hostname localhost +qemu-nbd: Certificate does not match the hostname localhost + +== check TLS works over TCP with mismatched hostname and override == +image: nbd://localhost:PORT +file format: nbd +virtual size: 64 MiB (67108864 bytes) +disk size: unavailable +exports available: 1 + export: '' + size: 67108864 + min block: 1 == check TLS with different CA fails == qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer @@ -62,9 +70,43 @@ read 1048576/1048576 bytes at offset 1048576 qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort +== check TLS fail over UNIX with no hostname == +qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0': No hostname for certificate validation +qemu-nbd: No hostname for certificate validation + +== check TLS works over UNIX with hostname override == +image: nbd+unix://?socket=SOCK_DIR/qemu-nbd.sock +file format: nbd +virtual size: 64 MiB (67108864 bytes) +disk size: unavailable +exports available: 1 + export: '' + size: 67108864 + min block: 1 + +== check TLS works over UNIX with PSK == +image: nbd+unix://?socket=SOCK_DIR/qemu-nbd.sock +file format: nbd +virtual size: 64 MiB (67108864 bytes) +disk size: unavailable +exports available: 1 + export: '' + size: 67108864 + min block: 1 + +== check TLS fails over UNIX with mismatch PSK == +qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0': TLS handshake failed: The TLS connection was non-properly terminated. +qemu-nbd: TLS handshake failed: The TLS connection was non-properly terminated. + == final server log == +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. +qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. *** done diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234 index cb5f1753e0..a9f764bb2c 100755 --- a/tests/qemu-iotests/234 +++ b/tests/qemu-iotests/234 @@ -34,8 +34,8 @@ with iotests.FilePath('img') as img_path, \ iotests.VM(path_suffix='a') as vm_a, \ iotests.VM(path_suffix='b') as vm_b: - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, backing_path, '64M') - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, img_path, '64M') + iotests.qemu_img_create('-f', iotests.imgfmt, backing_path, '64M') + iotests.qemu_img_create('-f', iotests.imgfmt, img_path, '64M') os.mkfifo(fifo_a) os.mkfifo(fifo_b) diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237 index 43dfd3bd40..5ea13eb01f 100755 --- a/tests/qemu-iotests/237 +++ b/tests/qemu-iotests/237 @@ -165,8 +165,7 @@ with iotests.FilePath('t.vmdk') as disk_path, \ iotests.log("") for path in [ extent1_path, extent2_path, extent3_path ]: - msg = iotests.qemu_img_pipe('create', '-f', imgfmt, path, '0') - iotests.log(msg, [iotests.filter_testfiles]) + iotests.qemu_img_create('-f', imgfmt, path, '0') vm.add_blockdev('driver=file,filename=%s,node-name=ext1' % (extent1_path)) vm.add_blockdev('driver=file,filename=%s,node-name=ext2' % (extent2_path)) diff --git a/tests/qemu-iotests/237.out b/tests/qemu-iotests/237.out index aeb9724492..62b8865677 100644 --- a/tests/qemu-iotests/237.out +++ b/tests/qemu-iotests/237.out @@ -129,9 +129,6 @@ Job failed: Cannot find device='this doesn't exist' nor node-name='this doesn't === Other subformats === - - - == Missing extent == {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}} diff --git a/tests/qemu-iotests/241 b/tests/qemu-iotests/241 index c962c8b607..f196650afa 100755 --- a/tests/qemu-iotests/241 +++ b/tests/qemu-iotests/241 @@ -58,7 +58,7 @@ echo nbd_server_start_unix_socket -f $IMGFMT "$TEST_IMG_FILE" -$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' +$QEMU_NBD_PROG --list -k $nbd_unix_socket | _filter_qemu_nbd_exports $QEMU_IMG map -f raw --output=json "$TEST_IMG" | _filter_qemu_img_map $QEMU_IO -f raw -c map "$TEST_IMG" nbd_server_stop @@ -71,7 +71,7 @@ echo # sector alignment, here at the server. nbd_server_start_unix_socket "$TEST_IMG_FILE" 2> "$TEST_DIR/server.log" -$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' +$QEMU_NBD_PROG --list -k $nbd_unix_socket | _filter_qemu_nbd_exports $QEMU_IMG map -f raw --output=json "$TEST_IMG" | _filter_qemu_img_map $QEMU_IO -f raw -c map "$TEST_IMG" nbd_server_stop @@ -84,7 +84,7 @@ echo # Now force sector alignment at the client. nbd_server_start_unix_socket -f $IMGFMT "$TEST_IMG_FILE" -$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' +$QEMU_NBD_PROG --list -k $nbd_unix_socket | _filter_qemu_nbd_exports $QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map $QEMU_IO -c map "$TEST_IMG" nbd_server_stop diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out index 56e95b599a..88e8cfcd7e 100644 --- a/tests/qemu-iotests/241.out +++ b/tests/qemu-iotests/241.out @@ -2,6 +2,8 @@ QA output created by 241 === Exporting unaligned raw image, natural alignment === +exports available: 1 + export: '' size: 1024 min block: 1 [{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, @@ -10,6 +12,8 @@ QA output created by 241 === Exporting unaligned raw image, forced server sector alignment === +exports available: 1 + export: '' size: 1024 min block: 512 [{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] @@ -20,6 +24,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed === Exporting unaligned raw image, forced client sector alignment === +exports available: 1 + export: '' size: 1024 min block: 1 [{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242 index 96a30152b0..c89f0c6cb3 100755 --- a/tests/qemu-iotests/242 +++ b/tests/qemu-iotests/242 @@ -22,8 +22,8 @@ import iotests import json import struct -from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \ - file_path, img_info_log, log, filter_qemu_io +from iotests import qemu_img_create, qemu_io_log, qemu_img_info, \ + file_path, img_info_log, log iotests.script_initialize(supported_fmts=['qcow2'], supported_protocols=['file'], @@ -39,8 +39,7 @@ flag_offset = 0x5000f def print_bitmap(extra_args): log('qemu-img info dump:\n') img_info_log(disk, extra_args=extra_args) - result = json.loads(qemu_img_pipe('info', '--force-share', - '--output=json', disk)) + result = qemu_img_info('--force-share', disk) if 'bitmaps' in result['format-specific']['data']: bitmaps = result['format-specific']['data']['bitmaps'] log('The same bitmaps in JSON format:') @@ -62,7 +61,7 @@ def add_bitmap(bitmap_number, persistent, disabled): def write_to_disk(offset, size): write = 'write {} {}'.format(offset, size) - log(qemu_io('-c', write, disk), filters=[filter_qemu_io]) + qemu_io_log('-c', write, disk) def toggle_flag(offset): @@ -101,7 +100,7 @@ add_bitmap(1, True, False) log('Write an unknown bitmap flag \'{}\' into a new QCOW2 image at offset {}' .format(hex(bitmap_flag_unknown), flag_offset)) toggle_flag(flag_offset) -img_info_log(disk) +img_info_log(disk, check=False) toggle_flag(flag_offset) log('Unset the unknown bitmap flag \'{}\' in the bitmap directory entry:\n' .format(hex(bitmap_flag_unknown))) diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index 8cbed7821b..edaf29094b 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -20,11 +20,13 @@ # along with this program. If not, see . # -import os -import re -import iotests import copy import json +import os +import re +from subprocess import CalledProcessError + +import iotests from iotests import qemu_img, qemu_io hd_path = [ @@ -216,11 +218,14 @@ class TestBlockdevReopen(iotests.QMPTestCase): # Reopen an image several times changing some of its options def test_reopen(self): - # Check whether the filesystem supports O_DIRECT - if 'O_DIRECT' in qemu_io('-f', 'raw', '-t', 'none', '-c', 'quit', hd_path[0]): - supports_direct = False - else: + try: + qemu_io('-f', 'raw', '-t', 'none', '-c', 'quit', hd_path[0]) supports_direct = True + except CalledProcessError as exc: + if 'O_DIRECT' in exc.stdout: + supports_direct = False + else: + raise # Open the hd1 image passing all backing options opts = hd_opts(1) diff --git a/tests/qemu-iotests/250 b/tests/qemu-iotests/250 index f069ca1759..af48f83aba 100755 --- a/tests/qemu-iotests/250 +++ b/tests/qemu-iotests/250 @@ -20,7 +20,7 @@ # # creator -owner=vsementsov@virtuozzo.com +owner=v.sementsov-og@mail.ru seq=`basename $0` echo "QA output created by $seq" diff --git a/tests/qemu-iotests/251 b/tests/qemu-iotests/251 index 8bdec37d32..794cad58b2 100755 --- a/tests/qemu-iotests/251 +++ b/tests/qemu-iotests/251 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/252 b/tests/qemu-iotests/252 index 2134b9993a..522333cf1d 100755 --- a/tests/qemu-iotests/252 +++ b/tests/qemu-iotests/252 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255 index 3d6d0e80cb..88b29d64b4 100755 --- a/tests/qemu-iotests/255 +++ b/tests/qemu-iotests/255 @@ -42,8 +42,8 @@ with iotests.FilePath('t.qcow2') as disk_path, \ size_str = str(size) iotests.create_image(base_path, size) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, mid_path, size_str) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, disk_path, size_str) + iotests.qemu_img_create('-f', iotests.imgfmt, mid_path, size_str) + iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, size_str) # Create a backing chain like this: # base <- [throttled: bps-read=4096] <- mid <- overlay @@ -92,12 +92,10 @@ with iotests.FilePath('src.qcow2') as src_path, \ size = 128 * 1024 * 1024 size_str = str(size) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, src_path, size_str) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, dst_path, size_str) + iotests.qemu_img_create('-f', iotests.imgfmt, src_path, size_str) + iotests.qemu_img_create('-f', iotests.imgfmt, dst_path, size_str) - iotests.log(iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 0 1M', - src_path), - filters=[iotests.filter_test_dir, iotests.filter_qemu_io]) + iotests.qemu_io_log('-f', iotests.imgfmt, '-c', 'write 0 1M', src_path), vm.add_object('throttle-group,x-bps-read=4096,id=throttle0') diff --git a/tests/qemu-iotests/255.out b/tests/qemu-iotests/255.out index 11a05a5213..2e837cbb5f 100644 --- a/tests/qemu-iotests/255.out +++ b/tests/qemu-iotests/255.out @@ -3,8 +3,6 @@ Finishing a commit job with background reads === Create backing chain and start VM === - - === Start background read requests === === Run a commit job === @@ -21,8 +19,6 @@ Closing the VM while a job is being cancelled === Create images and start VM === - - wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index c72c82a171..e7e7a2317e 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -240,13 +240,14 @@ def compare_images(image, reference, baseimg=None, expected_match=True): """ expected_ret = 0 if expected_match else 1 if baseimg: - assert qemu_img("rebase", "-u", "-b", baseimg, '-F', iotests.imgfmt, - image) == 0 - ret = qemu_img("compare", image, reference) + qemu_img("rebase", "-u", "-b", baseimg, '-F', iotests.imgfmt, image) + + sub = qemu_img("compare", image, reference, check=False) + log('qemu_img compare "{:s}" "{:s}" ==> {:s}, {:s}'.format( image, reference, - "Identical" if ret == 0 else "Mismatch", - "OK!" if ret == expected_ret else "ERROR!"), + "Identical" if sub.returncode == 0 else "Mismatch", + "OK!" if sub.returncode == expected_ret else "ERROR!"), filters=[iotests.filter_testfiles]) def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out index 50cbd8e882..aa76131ca9 100644 --- a/tests/qemu-iotests/257.out +++ b/tests/qemu-iotests/257.out @@ -106,6 +106,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -566,6 +582,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -819,6 +851,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -1279,6 +1327,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -1532,6 +1596,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -1992,6 +2072,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -2245,6 +2341,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -2705,6 +2817,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -2958,6 +3086,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -3418,6 +3562,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -3671,6 +3831,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -4131,6 +4307,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -4384,6 +4576,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, @@ -4844,6 +5052,22 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { + "backup-top": [ + { + "busy": false, + "count": 67108864, + "granularity": 65536, + "persistent": false, + "recording": false + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "persistent": false, + "recording": false + } + ], "drive0": [ { "busy": false, diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258 index a6618208a8..73d4af645f 100755 --- a/tests/qemu-iotests/258 +++ b/tests/qemu-iotests/258 @@ -18,10 +18,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Creator/Owner: Max Reitz +# Creator/Owner: Hanna Reitz import iotests -from iotests import log, qemu_img, qemu_io_silent, \ +from iotests import log, qemu_img, qemu_io, \ filter_qmp_testfiles, filter_qmp_imgfmt # Returns a node for blockdev-add @@ -75,26 +75,25 @@ def test_concurrent_finish(write_to_stream_node): # It is important to use raw for the base layer (so that # permissions are just handed through to the protocol layer) - assert qemu_img('create', '-f', 'raw', node0_path, '64M') == 0 + qemu_img('create', '-f', 'raw', node0_path, '64M') stream_throttle=None commit_throttle=None for path in [node1_path, node2_path, node3_path, node4_path]: - assert qemu_img('create', '-f', iotests.imgfmt, path, '64M') == 0 + qemu_img('create', '-f', iotests.imgfmt, path, '64M') if write_to_stream_node: # This is what (most of the time) makes commit finish # earlier and then pull in stream - assert qemu_io_silent(node2_path, - '-c', 'write %iK 64K' % (65536 - 192), - '-c', 'write %iK 64K' % (65536 - 64)) == 0 + qemu_io(node2_path, + '-c', 'write %iK 64K' % (65536 - 192), + '-c', 'write %iK 64K' % (65536 - 64)) stream_throttle='tg' else: # And this makes stream finish earlier - assert qemu_io_silent(node1_path, - '-c', 'write %iK 64K' % (65536 - 64)) == 0 + qemu_io(node1_path, '-c', 'write %iK 64K' % (65536 - 64)) commit_throttle='tg' diff --git a/tests/qemu-iotests/259 b/tests/qemu-iotests/259 index 1b15e8fb48..82f5de4b34 100755 --- a/tests/qemu-iotests/259 +++ b/tests/qemu-iotests/259 @@ -20,7 +20,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/261 b/tests/qemu-iotests/261 index d1c8037ab1..b73da565da 100755 --- a/tests/qemu-iotests/261 +++ b/tests/qemu-iotests/261 @@ -22,7 +22,7 @@ # # creator -owner=mreitz@redhat.com +owner=hreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262 index 32d69988ef..2294fd5ecb 100755 --- a/tests/qemu-iotests/262 +++ b/tests/qemu-iotests/262 @@ -51,7 +51,7 @@ with iotests.FilePath('img') as img_path, \ vm.add_device('virtio-blk,drive=%s,iothread=iothread0' % root) - iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, img_path, '64M') + iotests.qemu_img_create('-f', iotests.imgfmt, img_path, '64M') os.mkfifo(fifo) diff --git a/tests/qemu-iotests/266 b/tests/qemu-iotests/266 index 71ce81d0df..8fc3807ac5 100755 --- a/tests/qemu-iotests/266 +++ b/tests/qemu-iotests/266 @@ -137,7 +137,7 @@ def main(): iotests.log('') vm.shutdown() - iotests.img_info_log(file_path) + iotests.img_info_log(file_path, check=False) iotests.script_main(main, diff --git a/tests/qemu-iotests/271 b/tests/qemu-iotests/271 index 2775b4d130..c7c2cadda0 100755 --- a/tests/qemu-iotests/271 +++ b/tests/qemu-iotests/271 @@ -896,7 +896,7 @@ _make_test_img -o extended_l2=on 1M # Second and third writes in _concurrent_io() are independent and may finish in # different order. So, filter offset out to match both possible variants. _concurrent_io | $QEMU_IO | _filter_qemu_io | \ - $SED -e 's/\(20480\|40960\)/OFFSET/' + sed -e 's/\(20480\|40960\)/OFFSET/' _concurrent_verify | $QEMU_IO | _filter_qemu_io # success, all done diff --git a/tests/qemu-iotests/274 b/tests/qemu-iotests/274 index 080a90f10f..2495e051a2 100755 --- a/tests/qemu-iotests/274 +++ b/tests/qemu-iotests/274 @@ -31,12 +31,11 @@ size_long = 2 * 1024 * 1024 size_diff = size_long - size_short def create_chain() -> None: - iotests.qemu_img_log('create', '-f', iotests.imgfmt, base, - str(size_long)) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base, - '-F', iotests.imgfmt, mid, str(size_short)) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', mid, - '-F', iotests.imgfmt, top, str(size_long)) + iotests.qemu_img_create('-f', iotests.imgfmt, base, str(size_long)) + iotests.qemu_img_create('-f', iotests.imgfmt, '-b', base, + '-F', iotests.imgfmt, mid, str(size_short)) + iotests.qemu_img_create('-f', iotests.imgfmt, '-b', mid, + '-F', iotests.imgfmt, top, str(size_long)) iotests.qemu_io_log('-c', 'write -P 1 0 %d' % size_long, base) @@ -160,9 +159,9 @@ with iotests.FilePath('base') as base, \ ('off', '512k', '256k', '500k', '436k')]: iotests.log('=== preallocation=%s ===' % prealloc) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, base, base_size) - iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base, - '-F', iotests.imgfmt, top, top_size_old) + iotests.qemu_img_create('-f', iotests.imgfmt, base, base_size) + iotests.qemu_img_create('-f', iotests.imgfmt, '-b', base, + '-F', iotests.imgfmt, top, top_size_old) iotests.qemu_io_log('-c', 'write -P 1 %s 64k' % off, base) # After this, top_size_old to base_size should be allocated/zeroed. diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out index 1ce40d839a..acd8b166a6 100644 --- a/tests/qemu-iotests/274.out +++ b/tests/qemu-iotests/274.out @@ -1,7 +1,4 @@ == Commit tests == - - - wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -63,9 +60,6 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing HMP commit (top -> mid) === - - - wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -92,9 +86,6 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing QMP active commit (top -> mid) === - - - wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -127,9 +118,6 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing qemu-img commit (top -> base) === - - - wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -154,9 +142,6 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing QMP active commit (top -> base) === - - - wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -190,8 +175,6 @@ read 1048576/1048576 bytes at offset 1048576 == Resize tests == === preallocation=off === - - wrote 65536/65536 bytes at offset 5368709120 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -207,8 +190,6 @@ read 65536/65536 bytes at offset 5368709120 { "start": 1073741824, "length": 7516192768, "depth": 0, "present": true, "zero": true, "data": false}] === preallocation=metadata === - - wrote 65536/65536 bytes at offset 33285996544 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -229,8 +210,6 @@ read 65536/65536 bytes at offset 33285996544 { "start": 34896609280, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 2685075456}] === preallocation=falloc === - - wrote 65536/65536 bytes at offset 9437184 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -246,8 +225,6 @@ read 65536/65536 bytes at offset 9437184 { "start": 5242880, "length": 10485760, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] === preallocation=full === - - wrote 65536/65536 bytes at offset 11534336 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -263,8 +240,6 @@ read 65536/65536 bytes at offset 11534336 { "start": 8388608, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] === preallocation=off === - - wrote 65536/65536 bytes at offset 259072 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -281,8 +256,6 @@ read 65536/65536 bytes at offset 259072 { "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] === preallocation=off === - - wrote 65536/65536 bytes at offset 344064 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -298,8 +271,6 @@ read 65536/65536 bytes at offset 344064 { "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] === preallocation=off === - - wrote 65536/65536 bytes at offset 446464 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/280 b/tests/qemu-iotests/280 index 628f3c33ca..5f50500fdb 100755 --- a/tests/qemu-iotests/280 +++ b/tests/qemu-iotests/280 @@ -33,7 +33,7 @@ with iotests.FilePath('base') as base_path , \ iotests.FilePath('top') as top_path, \ iotests.VM() as vm: - iotests.qemu_img_log('create', '-f', iotests.imgfmt, base_path, '64M') + iotests.qemu_img_create('-f', iotests.imgfmt, base_path, '64M') iotests.log('=== Launch VM ===') vm.add_object('iothread,id=iothread0') diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out index e39164c579..c75f437c00 100644 --- a/tests/qemu-iotests/280.out +++ b/tests/qemu-iotests/280.out @@ -1,4 +1,3 @@ - === Launch VM === Enabling migration QMP events on VM... {"return": {}} diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296 index 099a3eeaa5..0d21b740a7 100755 --- a/tests/qemu-iotests/296 +++ b/tests/qemu-iotests/296 @@ -76,7 +76,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): # create the encrypted block device using qemu-img def createImg(self, file, secret): - output = iotests.qemu_img_pipe( + iotests.qemu_img( 'create', '--object', *secret.to_cmdline_object(), '-f', iotests.imgfmt, @@ -84,8 +84,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): '-o', 'iter-time=10', file, '1M') - - iotests.log(output, filters=[iotests.filter_test_dir]) + iotests.log('') # attempts to add a key using qemu-img def addKey(self, file, secret, new_secret): @@ -99,7 +98,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): } } - output = iotests.qemu_img_pipe( + output = iotests.qemu_img( 'amend', '--object', *secret.to_cmdline_object(), '--object', *new_secret.to_cmdline_object(), @@ -108,8 +107,9 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): '-o', 'new-secret=' + new_secret.id(), '-o', 'iter-time=10', - "json:" + json.dumps(image_options) - ) + "json:" + json.dumps(image_options), + check=False # Expected to fail. Log output. + ).stdout iotests.log(output, filters=[iotests.filter_test_dir]) @@ -174,8 +174,12 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): } result = vm.qmp('x-blockdev-amend', **args) - assert result['return'] == {} - vm.run_job('job0') + iotests.log(result) + # Run the job only if it was created + event = ('JOB_STATUS_CHANGE', + {'data': {'id': 'job0', 'status': 'created'}}) + if vm.events_wait([event], timeout=0.0) is not None: + vm.run_job('job0') # test that when the image opened by two qemu processes, # neither of them can update the encryption keys diff --git a/tests/qemu-iotests/296.out b/tests/qemu-iotests/296.out index 42205cc981..609826eaa0 100644 --- a/tests/qemu-iotests/296.out +++ b/tests/qemu-iotests/296.out @@ -1,11 +1,9 @@ +{"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -Job failed: Failed to get shared "consistent read" lock -{"execute": "job-dismiss", "arguments": {"id": "job0"}} -{"return": {}} -Job failed: Failed to get shared "consistent read" lock -{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}} +{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}} {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} @@ -13,14 +11,9 @@ qemu-img: Failed to get shared "consistent read" lock Is another process using the image [TEST_DIR/test.img]? . -Job failed: Block node is read-only -{"execute": "job-dismiss", "arguments": {"id": "job0"}} -{"return": {}} -Job failed: Failed to get shared "consistent read" lock -{"execute": "job-dismiss", "arguments": {"id": "job0"}} -{"return": {}} -Job failed: Failed to get shared "consistent read" lock -{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"error": {"class": "GenericError", "desc": "Block node is read-only"}} +{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}} +{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}} {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298 index fae72211b1..ad560e2941 100755 --- a/tests/qemu-iotests/298 +++ b/tests/qemu-iotests/298 @@ -129,16 +129,13 @@ class TestTruncate(iotests.QMPTestCase): os.remove(refdisk) def do_test(self, prealloc_mode, new_size): - ret = iotests.qemu_io_silent('--image-opts', '-c', 'write 0 10M', '-c', - f'truncate -m {prealloc_mode} {new_size}', - drive_opts) - self.assertEqual(ret, 0) + iotests.qemu_io('--image-opts', '-c', 'write 0 10M', '-c', + f'truncate -m {prealloc_mode} {new_size}', + drive_opts) - ret = iotests.qemu_io_silent('-f', iotests.imgfmt, '-c', 'write 0 10M', - '-c', - f'truncate -m {prealloc_mode} {new_size}', - refdisk) - self.assertEqual(ret, 0) + iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 0 10M', + '-c', f'truncate -m {prealloc_mode} {new_size}', + refdisk) stat = os.stat(disk) refstat = os.stat(refdisk) diff --git a/tests/qemu-iotests/303 b/tests/qemu-iotests/303 index 16c2e10827..a8cc6a23df 100755 --- a/tests/qemu-iotests/303 +++ b/tests/qemu-iotests/303 @@ -21,10 +21,12 @@ import iotests import subprocess -from iotests import qemu_img_create, qemu_io, file_path, log, filter_qemu_io +from iotests import qemu_img_create, qemu_io_log, file_path, log, \ + verify_qcow2_zstd_compression iotests.script_initialize(supported_fmts=['qcow2'], unsupported_imgopts=['refcount_bits', 'compat']) +verify_qcow2_zstd_compression() disk = file_path('disk') chunk = 1024 * 1024 @@ -38,12 +40,12 @@ def create_bitmap(bitmap_number, disabled): if disabled: args.append('--disable') - iotests.qemu_img_pipe(*args) + iotests.qemu_img(*args) def write_to_disk(offset, size): write = f'write {offset} {size}' - log(qemu_io('-c', write, disk), filters=[filter_qemu_io]) + qemu_io_log('-c', write, disk) def add_bitmap(num, begin, end, disabled): diff --git a/tests/qemu-iotests/310 b/tests/qemu-iotests/310 index 33c3411869..650d2cb6fb 100755 --- a/tests/qemu-iotests/310 +++ b/tests/qemu-iotests/310 @@ -21,7 +21,7 @@ # import iotests -from iotests import log, qemu_img, qemu_io_silent +from iotests import log, qemu_img, qemu_io # Need backing file support iotests.script_initialize(supported_fmts=['qcow2'], @@ -31,7 +31,7 @@ log('') log('=== Copy-on-read across nodes ===') log('') -# This test is similar to the 216 one by Max Reitz +# This test is similar to the 216 one by Hanna Reitz # The difference is that this test case involves a bottom node to the # COR filter driver. @@ -43,16 +43,16 @@ with iotests.FilePath('base.img') as base_img_path, \ log('--- Setting up images ---') log('') - assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 - assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0 - assert qemu_io_silent(base_img_path, '-c', 'write -P 1 3M 1M') == 0 - assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, - '-F', iotests.imgfmt, mid_img_path) == 0 - assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 2M 1M') == 0 - assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 4M 1M') == 0 - assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path, - '-F', iotests.imgfmt, top_img_path) == 0 - assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0 + qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') + qemu_io(base_img_path, '-c', 'write -P 1 0M 1M') + qemu_io(base_img_path, '-c', 'write -P 1 3M 1M') + qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + '-F', iotests.imgfmt, mid_img_path) + qemu_io(mid_img_path, '-c', 'write -P 3 2M 1M') + qemu_io(mid_img_path, '-c', 'write -P 3 4M 1M') + qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path, + '-F', iotests.imgfmt, top_img_path) + qemu_io(top_img_path, '-c', 'write -P 2 1M 1M') # 0 1 2 3 4 # top 2 @@ -105,13 +105,12 @@ with iotests.FilePath('base.img') as base_img_path, \ log('') # Detach backing to check that we can read the data from the top level now - assert qemu_img('rebase', '-u', '-b', '', '-f', iotests.imgfmt, - top_img_path) == 0 + qemu_img('rebase', '-u', '-b', '', '-f', iotests.imgfmt, top_img_path) - assert qemu_io_silent(top_img_path, '-c', 'read -P 0 0 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 3 2M 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 0 3M 1M') == 0 - assert qemu_io_silent(top_img_path, '-c', 'read -P 3 4M 1M') == 0 + qemu_io(top_img_path, '-c', 'read -P 0 0 1M') + qemu_io(top_img_path, '-c', 'read -P 2 1M 1M') + qemu_io(top_img_path, '-c', 'read -P 3 2M 1M') + qemu_io(top_img_path, '-c', 'read -P 0 3M 1M') + qemu_io(top_img_path, '-c', 'read -P 3 4M 1M') log('Done') diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 75cc241580..cc9f1a5891 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -21,44 +21,44 @@ _filter_date() { - $SED -re 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/yyyy-mm-dd hh:mm:ss/' + sed -Ee 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/yyyy-mm-dd hh:mm:ss/' } _filter_vmstate_size() { - $SED -r -e 's/[0-9. ]{5} [KMGT]iB/ SIZE/' \ - -e 's/[0-9. ]{5} B/ SIZE/' + sed -E -e 's/[0-9. ]{5} [KMGT]iB/ SIZE/' \ + -e 's/[0-9. ]{5} B/ SIZE/' } _filter_generated_node_ids() { - $SED -re 's/\#block[0-9]{3,}/NODE_NAME/' + sed -Ee 's/\#block[0-9]{3,}/NODE_NAME/' } _filter_qom_path() { - $SED -e '/Attached to:/s/\device[[0-9]\+\]/device[N]/g' + gsed -e '/Attached to:/s/\device[[0-9]\+\]/device[N]/g' } # replace occurrences of the actual TEST_DIR value with TEST_DIR _filter_testdir() { - $SED -e "s#$TEST_DIR/#TEST_DIR/#g" \ - -e "s#$SOCK_DIR/#SOCK_DIR/#g" \ - -e "s#SOCK_DIR/fuse-#TEST_DIR/#g" + sed -e "s#$TEST_DIR/#TEST_DIR/#g" \ + -e "s#$SOCK_DIR/#SOCK_DIR/#g" \ + -e "s#SOCK_DIR/fuse-#TEST_DIR/#g" } # replace occurrences of the actual IMGFMT value with IMGFMT _filter_imgfmt() { - $SED -e "s#$IMGFMT#IMGFMT#g" + sed -e "s#$IMGFMT#IMGFMT#g" } # Replace error message when the format is not supported and delete # the output lines after the first one _filter_qemu_img_check() { - $SED -e '/allocated.*fragmented.*compressed clusters/d' \ + gsed -e '/allocated.*fragmented.*compressed clusters/d' \ -e 's/qemu-img: This image format does not support checks/No errors were found on the image./' \ -e '/Image end offset: [0-9]\+/d' } @@ -66,13 +66,14 @@ _filter_qemu_img_check() # Removes \r from messages _filter_win32() { - $SED -e 's/\r//g' + gsed -e 's/\r//g' } # sanitize qemu-io output _filter_qemu_io() { - _filter_win32 | $SED -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ + _filter_win32 | \ + gsed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\)/:\1/" \ -e "s/qemu-io> //g" } @@ -80,7 +81,7 @@ _filter_qemu_io() # replace occurrences of QEMU_PROG with "qemu" _filter_qemu() { - $SED -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ + gsed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ -e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \ -e $'s#\r##' # QEMU monitor uses \r\n line endings } @@ -89,7 +90,7 @@ _filter_qemu() _filter_qmp() { _filter_win32 | \ - $SED -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \ + gsed -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \ -e 's#^{"QMP":.*}$#QMP_VERSION#' \ -e '/^ "QMP": {\s*$/, /^ }\s*$/ c\' \ -e ' QMP_VERSION' @@ -98,32 +99,32 @@ _filter_qmp() # readline makes HMP command strings so long that git complains _filter_hmp() { - $SED -e $'s/^\\((qemu) \\)\\?.*\e\\[D/\\1/g' \ + gsed -e $'s/^\\((qemu) \\)\\?.*\e\\[D/\\1/g' \ -e $'s/\e\\[K//g' } # replace block job offset _filter_block_job_offset() { - $SED -e 's/, "offset": [0-9]\+,/, "offset": OFFSET,/' + gsed -e 's/, "offset": [0-9]\+,/, "offset": OFFSET,/' } # replace block job len _filter_block_job_len() { - $SED -e 's/, "len": [0-9]\+,/, "len": LEN,/g' + gsed -e 's/, "len": [0-9]\+,/, "len": LEN,/g' } # replace actual image size (depends on the host filesystem) _filter_actual_image_size() { - $SED -s 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' + gsed -s 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' } # Filename filters for qemu-img create _filter_img_create_filenames() { - $SED \ + sed \ -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ @@ -141,7 +142,7 @@ _do_filter_img_create() # precedes ", fmt=") and the options part ($options, which starts # with "fmt=") # (And just echo everything before the first "^Formatting") - readarray formatting_line < <($SED -e 's/, fmt=/\n/') + readarray formatting_line < <(gsed -e 's/, fmt=/\n/') filename_part=${formatting_line[0]} unset formatting_line[0] @@ -168,11 +169,11 @@ _do_filter_img_create() options=$( echo "$options" \ | tr '\n' '\0' \ - | $SED -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \ + | gsed -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \ | grep -a -e '^fmt' -e '^size' -e '^backing' -e '^preallocation' \ -e '^encryption' "${grep_data_file[@]}" \ | _filter_img_create_filenames \ - | $SED \ + | sed \ -e 's/^\(fmt\)/0-\1/' \ -e 's/^\(size\)/1-\1/' \ -e 's/^\(backing\)/2-\1/' \ @@ -180,9 +181,9 @@ _do_filter_img_create() -e 's/^\(encryption\)/4-\1/' \ -e 's/^\(preallocation\)/8-\1/' \ | LC_ALL=C sort \ - | $SED -e 's/^[0-9]-//' \ + | sed -e 's/^[0-9]-//' \ | tr '\n\0' ' \n' \ - | $SED -e 's/^ *$//' -e 's/ *$//' + | sed -e 's/^ *$//' -e 's/ *$//' ) if [ -n "$options" ]; then @@ -208,7 +209,7 @@ _filter_img_create() _filter_img_create_size() { - $SED -e "s# size=[0-9]\\+# size=SIZE#g" + gsed -e "s# size=[0-9]\\+# size=SIZE#g" } _filter_img_info() @@ -222,7 +223,7 @@ _filter_img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' - $SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + gsed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR#SOCK_DIR#g" \ @@ -284,7 +285,7 @@ _filter_qemu_img_map() data_file_filter=(-e "s#$data_file_pattern#\\1#") fi - $SED -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \ + sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \ -e 's/"offset": [0-9]\+/"offset": OFFSET/g' \ -e 's/Mapped to *//' \ "${data_file_filter[@]}" \ @@ -298,12 +299,21 @@ _filter_nbd() # receive callbacks sometimes, making them unreliable. # # Filter out the TCP port number since this changes between runs. - $SED -e '/nbd\/.*\.c:/d' \ + sed -e '/nbd\/.*\.c:/d' \ -e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \ + -e 's#localhost:[0-9]*#localhost:PORT#g' \ + -e 's#host=127\.0\.0\.1,port=[0-9]*#host=127.0.0.1,port=PORT#g' \ + -e 's#host=localhost,port=[0-9]*#host=localhost,port=PORT#g' \ + -e "s#path=$SOCK_DIR#path=SOCK_DIR#g" \ -e "s#?socket=$SOCK_DIR#?socket=SOCK_DIR#g" \ -e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#' } +_filter_qemu_nbd_exports() +{ + grep '\(exports available\|export\|size\|min block\|qemu-nbd\):' +} + _filter_qmp_empty_return() { grep -v '{"return": {}}' @@ -335,14 +345,14 @@ sys.stdout.write(result)' _filter_authz_check_tls() { - $SED -e 's/TLS x509 authz check for .* is denied/TLS x509 authz check for DISTINGUISHED-NAME is denied/' + sed -e 's/TLS x509 authz check for .* is denied/TLS x509 authz check for DISTINGUISHED-NAME is denied/' } _filter_qcow2_compression_type_bit() { - $SED -e 's/\(incompatible_features\s\+\)\[3\(, \)\?/\1[/' \ - -e 's/\(incompatible_features.*\), 3\]/\1]/' \ - -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/' + gsed -e 's/\(incompatible_features\s\+\)\[3\(, \)\?/\1[/' \ + -e 's/\(incompatible_features.*\), 3\]/\1]/' \ + -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/' } # make sure this script returns success diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 9885030b43..227e0a5be9 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -17,17 +17,28 @@ # along with this program. If not, see . # -SED= -for sed in sed gsed; do - ($sed --version | grep 'GNU sed') > /dev/null 2>&1 - if [ "$?" -eq 0 ]; then - SED=$sed - break +# bail out, setting up .notrun file +_notrun() +{ + echo "$*" >"$TEST_DIR/$seq.notrun" + echo "$seq not run: $*" + status=0 + exit +} + +if ! command -v gsed >/dev/null 2>&1; then + if sed --version 2>&1 | grep -v 'not GNU sed' | grep 'GNU sed' > /dev/null; + then + gsed() + { + sed "$@" + } + else + gsed() + { + _notrun "GNU sed not available" + } fi -done -if [ -z "$SED" ]; then - echo "$0: GNU sed not found" - exit 1 fi dd() @@ -722,30 +733,20 @@ _img_info() done } -# bail out, setting up .notrun file -# -_notrun() -{ - echo "$*" >"$OUTPUT_DIR/$seq.notrun" - echo "$seq not run: $*" - status=0 - exit -} - # bail out, setting up .casenotrun file # The function _casenotrun() is used as a notifier. It is the # caller's responsibility to make skipped a particular test. # _casenotrun() { - echo " [case not run] $*" >>"$OUTPUT_DIR/$seq.casenotrun" + echo " [case not run] $*" >>"$TEST_DIR/$seq.casenotrun" } # just plain bail out # _fail() { - echo "$*" | tee -a "$OUTPUT_DIR/$seq.full" + echo "$*" | tee -a "$TEST_DIR/$seq.full" echo "(see $seq.full for details)" status=1 exit 1 @@ -920,7 +921,7 @@ _require_working_luks() IMGFMT='luks' _rm_test_img "$file" if [ $status != 0 ]; then - reason=$(echo "$output" | grep "$file:" | $SED -e "s#.*$file: *##") + reason=$(echo "$output" | grep "$file:" | sed -e "s#.*$file: *##") if [ -z "$reason" ]; then reason="Failed to create a LUKS image" fi diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls index 6ba28a78d3..b9c5462986 100644 --- a/tests/qemu-iotests/common.tls +++ b/tests/qemu-iotests/common.tls @@ -24,6 +24,7 @@ tls_x509_cleanup() { rm -f "${tls_dir}"/*.pem rm -f "${tls_dir}"/*/*.pem + rm -f "${tls_dir}"/*/*.psk rmdir "${tls_dir}"/* rmdir "${tls_dir}" } @@ -40,6 +41,18 @@ tls_certtool() rm -f "${tls_dir}"/certtool.log } +tls_psktool() +{ + psktool "$@" 1>"${tls_dir}"/psktool.log 2>&1 + if test "$?" = 0; then + head -1 "${tls_dir}"/psktool.log + else + cat "${tls_dir}"/psktool.log + fi + rm -f "${tls_dir}"/psktool.log +} + + tls_x509_init() { (certtool --help) >/dev/null 2>&1 || \ @@ -118,12 +131,13 @@ tls_x509_create_server() caname=$1 name=$2 + # We don't include 'localhost' in the cert, as + # we want to keep it unlisted to let tests + # validate hostname override mkdir -p "${tls_dir}/$name" cat > "${tls_dir}/cert.info" < List[str]: return result -def qemu_img_pipe_and_status(*args: str) -> Tuple[str, int]: - """ - Run qemu-img and return both its output and its exit code - """ - is_create = bool(args and args[0] == 'create') - full_args = qemu_img_args + qemu_img_create_prepare_args(list(args)) - return qemu_tool_pipe_and_status('qemu-img', full_args, - drop_successful_output=is_create) -def qemu_img(*args: str) -> int: - '''Run qemu-img and return the exit code''' - return qemu_img_pipe_and_status(*args)[1] +def qemu_tool(*args: str, check: bool = True, combine_stdio: bool = True + ) -> 'subprocess.CompletedProcess[str]': + """ + Run a qemu tool and return its status code and console output. + + :param args: full command line to run. + :param check: Enforce a return code of zero. + :param combine_stdio: set to False to keep stdout/stderr separated. + + :raise VerboseProcessError: + When the return code is negative, or on any non-zero exit code + when 'check=True' was provided (the default). This exception has + 'stdout', 'stderr', and 'returncode' properties that may be + inspected to show greater detail. If this exception is not + handled, the command-line, return code, and all console output + will be included at the bottom of the stack trace. + + :return: + a CompletedProcess. This object has args, returncode, and stdout + properties. If streams are not combined, it will also have a + stderr property. + """ + subp = subprocess.run( + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT if combine_stdio else subprocess.PIPE, + universal_newlines=True, + check=False + ) + + if check and subp.returncode or (subp.returncode < 0): + raise VerboseProcessError( + subp.returncode, args, + output=subp.stdout, + stderr=subp.stderr, + ) + + return subp + + +def qemu_img(*args: str, check: bool = True, combine_stdio: bool = True + ) -> 'subprocess.CompletedProcess[str]': + """ + Run QEMU_IMG_PROG and return its status code and console output. + + This function always prepends QEMU_IMG_OPTIONS and may further alter + the args for 'create' commands. + + See `qemu_tool()` for greater detail. + """ + full_args = qemu_img_args + qemu_img_create_prepare_args(list(args)) + return qemu_tool(*full_args, check=check, combine_stdio=combine_stdio) + def ordered_qmp(qmsg, conv_keys=True): # Dictionaries are not ordered prior to 3.6, therefore: @@ -232,26 +274,63 @@ def ordered_qmp(qmsg, conv_keys=True): return od return qmsg -def qemu_img_create(*args): +def qemu_img_create(*args: str) -> 'subprocess.CompletedProcess[str]': return qemu_img('create', *args) -def qemu_img_measure(*args): - return json.loads(qemu_img_pipe("measure", "--output", "json", *args)) +def qemu_img_json(*args: str) -> Any: + """ + Run qemu-img and return its output as deserialized JSON. -def qemu_img_check(*args): - return json.loads(qemu_img_pipe("check", "--output", "json", *args)) + :raise CalledProcessError: + When qemu-img crashes, or returns a non-zero exit code without + producing a valid JSON document to stdout. + :raise JSONDecoderError: + When qemu-img returns 0, but failed to produce a valid JSON document. -def qemu_img_pipe(*args: str) -> str: - '''Run qemu-img and return its output''' - return qemu_img_pipe_and_status(*args)[0] + :return: A deserialized JSON object; probably a dict[str, Any]. + """ + try: + res = qemu_img(*args, combine_stdio=False) + except subprocess.CalledProcessError as exc: + # Terminated due to signal. Don't bother. + if exc.returncode < 0: + raise -def qemu_img_log(*args): - result = qemu_img_pipe(*args) - log(result, filters=[filter_testfiles]) + # Commands like 'check' can return failure (exit codes 2 and 3) + # to indicate command completion, but with errors found. For + # multi-command flexibility, ignore the exact error codes and + # *try* to load JSON. + try: + return json.loads(exc.stdout) + except json.JSONDecodeError: + # Nope. This thing is toast. Raise the /process/ error. + pass + raise + + return json.loads(res.stdout) + +def qemu_img_measure(*args: str) -> Any: + return qemu_img_json("measure", "--output", "json", *args) + +def qemu_img_check(*args: str) -> Any: + return qemu_img_json("check", "--output", "json", *args) + +def qemu_img_info(*args: str) -> Any: + return qemu_img_json('info', "--output", "json", *args) + +def qemu_img_map(*args: str) -> Any: + return qemu_img_json('map', "--output", "json", *args) + +def qemu_img_log(*args: str, check: bool = True + ) -> 'subprocess.CompletedProcess[str]': + result = qemu_img(*args, check=check) + log(result.stdout, filters=[filter_testfiles]) return result -def img_info_log(filename, filter_path=None, use_image_opts=False, - extra_args=()): +def img_info_log(filename: str, filter_path: Optional[str] = None, + use_image_opts: bool = False, extra_args: Sequence[str] = (), + check: bool = True, + ) -> None: args = ['info'] if use_image_opts: args.append('--image-opts') @@ -260,7 +339,7 @@ def img_info_log(filename, filter_path=None, use_image_opts=False, args += extra_args args.append(filename) - output = qemu_img_pipe(*args) + output = qemu_img(*args, check=check).stdout if not filter_path: filter_path = filename log(filter_img_info(output, filter_path)) @@ -274,31 +353,23 @@ def qemu_io_wrap_args(args: Sequence[str]) -> List[str]: def qemu_io_popen(*args): return qemu_tool_popen(qemu_io_wrap_args(args)) -def qemu_io(*args): - '''Run qemu-io and return the stdout data''' - return qemu_tool_pipe_and_status('qemu-io', qemu_io_wrap_args(args))[0] +def qemu_io(*args: str, check: bool = True, combine_stdio: bool = True + ) -> 'subprocess.CompletedProcess[str]': + """ + Run QEMU_IO_PROG and return the status code and console output. -def qemu_io_log(*args): - result = qemu_io(*args) - log(result, filters=[filter_testfiles, filter_qemu_io]) + This function always prepends either QEMU_IO_OPTIONS or + QEMU_IO_OPTIONS_NO_FMT. + """ + return qemu_tool(*qemu_io_wrap_args(args), + check=check, combine_stdio=combine_stdio) + +def qemu_io_log(*args: str, check: bool = True + ) -> 'subprocess.CompletedProcess[str]': + result = qemu_io(*args, check=check) + log(result.stdout, filters=[filter_testfiles, filter_qemu_io]) return result -def qemu_io_silent(*args): - '''Run qemu-io and return the exit code, suppressing stdout''' - args = qemu_io_wrap_args(args) - result = subprocess.run(args, stdout=subprocess.DEVNULL, check=False) - if result.returncode < 0: - sys.stderr.write('qemu-io received signal %i: %s\n' % - (-result.returncode, ' '.join(args))) - return result.returncode - -def qemu_io_silent_check(*args): - '''Run qemu-io and return the true if subprocess returned 0''' - args = qemu_io_wrap_args(args) - result = subprocess.run(args, stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT, check=False) - return result.returncode == 0 - class QemuIoInteractive: def __init__(self, *args): self.args = qemu_io_wrap_args(args) @@ -348,14 +419,30 @@ class QemuIoInteractive: class QemuStorageDaemon: - def __init__(self, *args: str, instance_id: str = 'a'): + _qmp: Optional[QEMUMonitorProtocol] = None + _qmpsock: Optional[str] = None + # Python < 3.8 would complain if this type were not a string literal + # (importing `annotations` from `__future__` would work; but not on <= 3.6) + _p: 'Optional[subprocess.Popen[bytes]]' = None + + def __init__(self, *args: str, instance_id: str = 'a', qmp: bool = False): assert '--pidfile' not in args self.pidfile = os.path.join(test_dir, f'qsd-{instance_id}-pid') all_args = [qsd_prog] + list(args) + ['--pidfile', self.pidfile] + if qmp: + self._qmpsock = os.path.join(sock_dir, f'qsd-{instance_id}.sock') + all_args += ['--chardev', + f'socket,id=qmp-sock,path={self._qmpsock}', + '--monitor', 'qmp-sock'] + + self._qmp = QEMUMonitorProtocol(self._qmpsock, server=True) + # Cannot use with here, we want the subprocess to stay around # pylint: disable=consider-using-with self._p = subprocess.Popen(all_args) + if self._qmp is not None: + self._qmp.accept() while not os.path.exists(self.pidfile): if self._p.poll() is not None: cmd = ' '.join(all_args) @@ -370,11 +457,24 @@ class QemuStorageDaemon: assert self._pid == self._p.pid + def qmp(self, cmd: str, args: Optional[Dict[str, object]] = None) \ + -> QMPMessage: + assert self._qmp is not None + return self._qmp.cmd(cmd, args) + def stop(self, kill_signal=15): self._p.send_signal(kill_signal) self._p.wait() self._p = None + if self._qmp: + self._qmp.close() + + if self._qmpsock is not None: + try: + os.remove(self._qmpsock) + except OSError: + pass try: os.remove(self.pidfile) except OSError: @@ -433,10 +533,22 @@ def qemu_nbd_popen(*args): p.kill() p.wait() -def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): - '''Return True if two image files are identical''' - return qemu_img('compare', '-f', fmt1, - '-F', fmt2, img1, img2) == 0 +def compare_images(img1: str, img2: str, + fmt1: str = imgfmt, fmt2: str = imgfmt) -> bool: + """ + Compare two images with QEMU_IMG; return True if they are identical. + + :raise CalledProcessError: + when qemu-img crashes or returns a status code of anything other + than 0 (identical) or 1 (different). + """ + try: + qemu_img('compare', '-f', fmt1, '-F', fmt2, img1, img2) + return True + except subprocess.CalledProcessError as exc: + if exc.returncode == 1: + return False + raise def create_image(name, size): '''Create a fully-allocated raw image with sector markers''' @@ -447,10 +559,14 @@ def create_image(name, size): file.write(sector) i = i + 512 -def image_size(img): - '''Return image's virtual size''' - r = qemu_img_pipe('info', '--output=json', '-f', imgfmt, img) - return json.loads(r)['virtual-size'] +def image_size(img: str) -> int: + """Return image's virtual size""" + value = qemu_img_info('-f', imgfmt, img)['virtual-size'] + if not isinstance(value, int): + type_name = type(value).__name__ + raise TypeError("Expected 'int' for 'virtual-size', " + f"got '{value}' of type '{type_name}'") + return value def is_str(val): return isinstance(val, str) @@ -489,8 +605,10 @@ def filter_qmp(qmsg, filter_fn): # Iterate through either lists or dicts; if isinstance(qmsg, list): items = enumerate(qmsg) - else: + elif isinstance(qmsg, dict): items = qmsg.items() + else: + return filter_fn(None, qmsg) for k, v in items: if isinstance(v, (dict, list)): @@ -826,8 +944,12 @@ class VM(qtest.QEMUQtestMachine): return result # Returns None on success, and an error string on failure - def run_job(self, job, auto_finalize=True, auto_dismiss=False, - pre_finalize=None, cancel=False, wait=60.0): + def run_job(self, job: str, auto_finalize: bool = True, + auto_dismiss: bool = False, + pre_finalize: Optional[Callable[[], None]] = None, + cancel: bool = False, wait: float = 60.0, + filters: Iterable[Callable[[Any], Any]] = (), + ) -> Optional[str]: """ run_job moves a job from creation through to dismissal. @@ -857,7 +979,7 @@ class VM(qtest.QEMUQtestMachine): while True: ev = filter_qmp_event(self.events_wait(events, timeout=wait)) if ev['event'] != 'JOB_STATUS_CHANGE': - log(ev) + log(ev, filters=filters) continue status = ev['data']['status'] if status == 'aborting': @@ -865,18 +987,18 @@ class VM(qtest.QEMUQtestMachine): for j in result['return']: if j['id'] == job: error = j['error'] - log('Job failed: %s' % (j['error'])) + log('Job failed: %s' % (j['error']), filters=filters) elif status == 'ready': - self.qmp_log('job-complete', id=job) + self.qmp_log('job-complete', id=job, filters=filters) elif status == 'pending' and not auto_finalize: if pre_finalize: pre_finalize() if cancel: - self.qmp_log('job-cancel', id=job) + self.qmp_log('job-cancel', id=job, filters=filters) else: - self.qmp_log('job-finalize', id=job) + self.qmp_log('job-finalize', id=job, filters=filters) elif status == 'concluded' and not auto_dismiss: - self.qmp_log('job-dismiss', id=job) + self.qmp_log('job-dismiss', id=job, filters=filters) elif status == 'null': return error @@ -889,7 +1011,7 @@ class VM(qtest.QEMUQtestMachine): if 'return' in result: assert result['return'] == {} - job_result = self.run_job(job_id) + job_result = self.run_job(job_id, filters=filters) else: job_result = result['error'] @@ -1209,7 +1331,7 @@ def notrun(reason): # Each test in qemu-iotests has a number ("seq") seq = os.path.basename(sys.argv[0]) - with open('%s/%s.notrun' % (output_dir, seq), 'w', encoding='utf-8') \ + with open('%s/%s.notrun' % (test_dir, seq), 'w', encoding='utf-8') \ as outfile: outfile.write(reason + '\n') logger.warning("%s not run: %s", seq, reason) @@ -1224,7 +1346,7 @@ def case_notrun(reason): # Each test in qemu-iotests has a number ("seq") seq = os.path.basename(sys.argv[0]) - with open('%s/%s.casenotrun' % (output_dir, seq), 'a', encoding='utf-8') \ + with open('%s/%s.casenotrun' % (test_dir, seq), 'a', encoding='utf-8') \ as outfile: outfile.write(' [case not run] ' + reason + '\n') @@ -1300,8 +1422,8 @@ def _verify_imgopts(unsupported: Sequence[str] = ()) -> None: notrun(f'not suitable for this imgopts: {imgopts}') -def supports_quorum(): - return 'quorum' in qemu_img_pipe('--help') +def supports_quorum() -> bool: + return 'quorum' in qemu_img('--help').stdout def verify_quorum(): '''Skip test suite if quorum support is not available''' @@ -1317,20 +1439,20 @@ def has_working_luks() -> Tuple[bool, str]: """ img_file = f'{test_dir}/luks-test.luks' - (output, status) = \ - qemu_img_pipe_and_status('create', '-f', 'luks', - '--object', luks_default_secret_object, - '-o', luks_default_key_secret_opt, - '-o', 'iter-time=10', - img_file, '1G') + res = qemu_img('create', '-f', 'luks', + '--object', luks_default_secret_object, + '-o', luks_default_key_secret_opt, + '-o', 'iter-time=10', + img_file, '1G', + check=False) try: os.remove(img_file) except OSError: pass - if status != 0: - reason = output - for line in output.splitlines(): + if res.returncode: + reason = res.stdout + for line in res.stdout.splitlines(): if img_file + ':' in line: reason = line.split(img_file + ':', 1)[1].strip() break @@ -1347,6 +1469,26 @@ def verify_working_luks(): if not working: notrun(reason) +def supports_qcow2_zstd_compression() -> bool: + img_file = f'{test_dir}/qcow2-zstd-test.qcow2' + res = qemu_img('create', '-f', 'qcow2', '-o', 'compression_type=zstd', + img_file, '0', + check=False) + try: + os.remove(img_file) + except OSError: + pass + + if res.returncode == 1 and \ + "'compression-type' does not accept value 'zstd'" in res.stdout: + return False + else: + return True + +def verify_qcow2_zstd_compression(): + if not supports_qcow2_zstd_compression(): + notrun('zstd compression not supported') + def qemu_pipe(*args: str) -> str: """ Run qemu with an option to print something and exit (e.g. a help option). diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build index 9747bb68a5..323a4acb6a 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build @@ -1,30 +1,47 @@ -if have_tools and targetos != 'windows' and not get_option('gprof') - qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd] - qemu_iotests_env = {'PYTHON': python.full_path()} - qemu_iotests_formats = { - 'qcow2': 'quick', - 'raw': 'slow', - 'qed': 'thorough', - 'vmdk': 'thorough', - 'vpc': 'thorough' - } - - foreach k, v : emulators - if k.startswith('qemu-system-') - qemu_iotests_binaries += v - endif - endforeach - foreach format, speed: qemu_iotests_formats - if speed == 'quick' - suites = 'block' - else - suites = ['block-' + speed, speed] - endif - test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format], - depends: qemu_iotests_binaries, env: qemu_iotests_env, - protocol: 'tap', - suite: suites, - timeout: 0, - is_parallel: false) - endforeach +if not have_tools or targetos == 'windows' or get_option('gprof') + subdir_done() endif + +foreach cflag: config_host['QEMU_CFLAGS'].split() + if cflag.startswith('-fsanitize') and \ + not cflag.contains('safe-stack') and not cflag.contains('cfi-icall') + message('Sanitizers are enabled ==> Disabled the qemu-iotests.') + subdir_done() + endif +endforeach + +bash = find_program('bash', required: false, version: '>= 4.0') +if not bash.found() + message('bash >= v4.0 not available ==> Disabled the qemu-iotests.') + subdir_done() +endif + +qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd] +qemu_iotests_env = {'PYTHON': python.full_path()} +qemu_iotests_formats = { + 'qcow2': 'quick', + 'raw': 'slow', + 'qed': 'thorough', + 'vmdk': 'thorough', + 'vpc': 'thorough' +} + +foreach k, v : emulators + if k.startswith('qemu-system-') + qemu_iotests_binaries += v + endif +endforeach + +foreach format, speed: qemu_iotests_formats + if speed == 'quick' + suites = 'block' + else + suites = ['block-' + speed, speed] + endif + test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format], + depends: qemu_iotests_binaries, env: qemu_iotests_env, + protocol: 'tap', + suite: suites, + timeout: 0, + is_parallel: false) +endforeach diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 0f32897fe8..a864c74b12 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -66,7 +66,7 @@ class TestEnv(ContextManager['TestEnv']): # pylint: disable=too-many-instance-attributes env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR', - 'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG', + 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG', 'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG', 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS', 'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT', @@ -106,7 +106,6 @@ class TestEnv(ContextManager['TestEnv']): TEST_DIR SOCK_DIR SAMPLE_IMG_DIR - OUTPUT_DIR """ # Path where qemu goodies live in this source tree. @@ -134,8 +133,6 @@ class TestEnv(ContextManager['TestEnv']): os.path.join(self.source_iotests, 'sample_images')) - self.output_dir = os.getcwd() # OUTPUT_DIR - def init_binaries(self) -> None: """Init binary path variables: PYTHON (for bash tests) @@ -238,6 +235,8 @@ class TestEnv(ContextManager['TestEnv']): ('aarch64', 'virt'), ('avr', 'mega2560'), ('m68k', 'virt'), + ('riscv32', 'virt'), + ('riscv64', 'virt'), ('rx', 'gdbsim-r5f562n8'), ('tricore', 'tricore_testboard') ) diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 9a94273975..aae70a8341 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -25,6 +25,7 @@ import subprocess import contextlib import json import termios +import shutil import sys from multiprocessing import Pool from contextlib import contextmanager @@ -259,9 +260,6 @@ class TestRunner(ContextManager['TestRunner']): """ f_test = Path(test) - f_bad = Path(f_test.name + '.out.bad') - f_notrun = Path(f_test.name + '.notrun') - f_casenotrun = Path(f_test.name + '.casenotrun') f_reference = Path(self.find_reference(test)) if not f_test.exists(): @@ -276,9 +274,6 @@ class TestRunner(ContextManager['TestRunner']): description='No qualified output ' f'(expected {f_reference})') - for p in (f_bad, f_notrun, f_casenotrun): - silent_unlink(p) - args = [str(f_test.resolve())] env = self.env.prepare_subprocess(args) if mp: @@ -288,6 +283,14 @@ class TestRunner(ContextManager['TestRunner']): env[d] = os.path.join(env[d], f_test.name) Path(env[d]).mkdir(parents=True, exist_ok=True) + test_dir = env['TEST_DIR'] + f_bad = Path(test_dir, f_test.name + '.out.bad') + f_notrun = Path(test_dir, f_test.name + '.notrun') + f_casenotrun = Path(test_dir, f_test.name + '.casenotrun') + + for p in (f_notrun, f_casenotrun): + silent_unlink(p) + t0 = time.time() with f_bad.open('w', encoding="utf-8") as f: with subprocess.Popen(args, cwd=str(f_test.parent), env=env, @@ -320,6 +323,11 @@ class TestRunner(ContextManager['TestRunner']): diff = file_diff(str(f_reference), str(f_bad)) if diff: + if os.environ.get("QEMU_IOTESTS_REGEN", None) is not None: + shutil.copyfile(str(f_bad), str(f_reference)) + print("########################################") + print("##### REFERENCE FILE UPDATED #####") + print("########################################") return TestResult(status='fail', elapsed=elapsed, description=f'output mismatch (see {f_bad})', diff=diff, casenotrun=casenotrun) @@ -365,7 +373,10 @@ class TestRunner(ContextManager['TestRunner']): description=res.description) if res.casenotrun: - print(res.casenotrun) + if self.tap: + print('#' + res.casenotrun.replace('\n', '\n#')) + else: + print(res.casenotrun) return res @@ -377,6 +388,7 @@ class TestRunner(ContextManager['TestRunner']): if self.tap: self.env.print_env('# ') + print('1..%d' % len(tests)) else: self.env.print_env() diff --git a/tests/qemu-iotests/tests/block-status-cache b/tests/qemu-iotests/tests/block-status-cache index 6fa10bb8f8..5a7bc2c149 100755 --- a/tests/qemu-iotests/tests/block-status-cache +++ b/tests/qemu-iotests/tests/block-status-cache @@ -22,7 +22,7 @@ import os import signal import iotests -from iotests import qemu_img_create, qemu_img_pipe, qemu_nbd +from iotests import qemu_img_create, qemu_img_map, qemu_nbd image_size = 1 * 1024 * 1024 @@ -35,8 +35,7 @@ nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') class TestBscWithNbd(iotests.QMPTestCase): def setUp(self) -> None: """Just create an empty image with a read-only NBD server on it""" - assert qemu_img_create('-f', iotests.imgfmt, test_img, - str(image_size)) == 0 + qemu_img_create('-f', iotests.imgfmt, test_img, str(image_size)) # Pass --allocation-depth to enable the qemu:allocation-depth context, # which we are going to query to provoke a block-status inquiry with @@ -77,8 +76,7 @@ class TestBscWithNbd(iotests.QMPTestCase): # to allocate the first sector to facilitate alignment probing), and # then the rest to be zero. The BSC will thus contain (if anything) # one range covering the first sector. - map_pre = qemu_img_pipe('map', '--output=json', '--image-opts', - nbd_img_opts) + map_pre = qemu_img_map('--image-opts', nbd_img_opts) # qemu:allocation-depth maps for want_zero=false. # want_zero=false should (with the file driver, which the server is @@ -112,14 +110,12 @@ class TestBscWithNbd(iotests.QMPTestCase): # never loop too many times here. for _ in range(2): # (Ignore the result, this is just to contaminate the cache) - qemu_img_pipe('map', '--output=json', '--image-opts', - nbd_img_opts_alloc_depth) + qemu_img_map('--image-opts', nbd_img_opts_alloc_depth) # Now let's see whether the cache reports everything as data, or # whether we get correct information (i.e. the same as we got on our # first attempt). - map_post = qemu_img_pipe('map', '--output=json', '--image-opts', - nbd_img_opts) + map_post = qemu_img_map('--image-opts', nbd_img_opts) if map_pre != map_post: print('ERROR: Map information differs before and after querying ' + diff --git a/tests/qemu-iotests/tests/graph-changes-while-io b/tests/qemu-iotests/tests/graph-changes-while-io new file mode 100755 index 0000000000..7664f33689 --- /dev/null +++ b/tests/qemu-iotests/tests/graph-changes-while-io @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# group: rw +# +# Test graph changes while I/O is happening +# +# Copyright (C) 2022 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +from threading import Thread +import iotests +from iotests import imgfmt, qemu_img, qemu_img_create, QMPTestCase, \ + QemuStorageDaemon + + +top = os.path.join(iotests.test_dir, 'top.img') +nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') + + +def do_qemu_img_bench() -> None: + """ + Do some I/O requests on `nbd_sock`. + """ + qemu_img('bench', '-f', 'raw', '-c', '2000000', + f'nbd+unix:///node0?socket={nbd_sock}') + + +class TestGraphChangesWhileIO(QMPTestCase): + def setUp(self) -> None: + # Create an overlay that can be added at runtime on top of the + # null-co block node that will receive I/O + qemu_img_create('-f', imgfmt, '-F', 'raw', '-b', 'null-co://', top) + + # QSD instance with a null-co block node in an I/O thread, + # exported over NBD (on `nbd_sock`, export name "node0") + self.qsd = QemuStorageDaemon( + '--object', 'iothread,id=iothread0', + '--blockdev', 'null-co,node-name=node0,read-zeroes=true', + '--nbd-server', f'addr.type=unix,addr.path={nbd_sock}', + '--export', 'nbd,id=exp0,node-name=node0,iothread=iothread0,' + + 'fixed-iothread=true,writable=true', + qmp=True + ) + + def tearDown(self) -> None: + self.qsd.stop() + + def test_blockdev_add_while_io(self) -> None: + # Run qemu-img bench in the background + bench_thr = Thread(target=do_qemu_img_bench) + bench_thr.start() + + # While qemu-img bench is running, repeatedly add and remove an + # overlay to/from node0 + while bench_thr.is_alive(): + result = self.qsd.qmp('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'overlay', + 'backing': 'node0', + 'file': { + 'driver': 'file', + 'filename': top + } + }) + self.assert_qmp(result, 'return', {}) + + result = self.qsd.qmp('blockdev-del', { + 'node-name': 'overlay' + }) + self.assert_qmp(result, 'return', {}) + + bench_thr.join() + +if __name__ == '__main__': + # Format must support raw backing files + iotests.main(supported_fmts=['qcow', 'qcow2', 'qed'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/graph-changes-while-io.out b/tests/qemu-iotests/tests/graph-changes-while-io.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/graph-changes-while-io.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/tests/image-fleecing b/tests/qemu-iotests/tests/image-fleecing index a58b5a1781..f6e449d071 100755 --- a/tests/qemu-iotests/tests/image-fleecing +++ b/tests/qemu-iotests/tests/image-fleecing @@ -22,13 +22,16 @@ # # Creator/Owner: John Snow +from subprocess import CalledProcessError + import iotests -from iotests import log, qemu_img, qemu_io, qemu_io_silent +from iotests import log, qemu_img, qemu_io iotests.script_initialize( - supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'], + supported_fmts=['qcow2'], supported_platforms=['linux'], required_fmts=['copy-before-write'], + unsupported_imgopts=['compat'] ) patterns = [('0x5d', '0', '64k'), @@ -49,12 +52,30 @@ remainder = [('0xd5', '0x108000', '32k'), # Right-end of partial-left [1] ('0xdc', '32M', '32k'), # Left-end of partial-right [2] ('0xcd', '0x3ff0000', '64k')] # patterns[3] -def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): +def do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path, + fleece_img_path, nbd_sock_path=None, + target_img_path=None, + bitmap=False): + push_backup = target_img_path is not None + assert (nbd_sock_path is not None) != push_backup + if push_backup: + assert use_cbw + log('--- Setting up images ---') log('') - assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 - assert qemu_img('create', '-f', 'qcow2', fleece_img_path, '64M') == 0 + qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') + if bitmap: + qemu_img('bitmap', '--add', base_img_path, 'bitmap0') + + if use_snapshot_access_filter: + assert use_cbw + qemu_img('create', '-f', 'raw', fleece_img_path, '64M') + else: + qemu_img('create', '-f', 'qcow2', fleece_img_path, '64M') + + if push_backup: + qemu_img('create', '-f', 'qcow2', target_img_path, '64M') for p in patterns: qemu_io('-f', iotests.imgfmt, @@ -81,27 +102,46 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): log('') - # create tmp_node backed by src_node - log(vm.qmp('blockdev-add', { - 'driver': 'qcow2', - 'node-name': tmp_node, - 'file': { + if use_snapshot_access_filter: + log(vm.qmp('blockdev-add', { + 'node-name': tmp_node, 'driver': 'file', 'filename': fleece_img_path, - }, - 'backing': src_node, - })) + })) + else: + # create tmp_node backed by src_node + log(vm.qmp('blockdev-add', { + 'driver': 'qcow2', + 'node-name': tmp_node, + 'file': { + 'driver': 'file', + 'filename': fleece_img_path, + }, + 'backing': src_node, + })) # Establish CBW from source to fleecing node if use_cbw: - log(vm.qmp('blockdev-add', { + fl_cbw = { 'driver': 'copy-before-write', 'node-name': 'fl-cbw', 'file': src_node, 'target': tmp_node - })) + } + + if bitmap: + fl_cbw['bitmap'] = {'node': src_node, 'name': 'bitmap0'} + + log(vm.qmp('blockdev-add', fl_cbw)) log(vm.qmp('qom-set', path=qom_path, property='drive', value='fl-cbw')) + + if use_snapshot_access_filter: + log(vm.qmp('blockdev-add', { + 'driver': 'snapshot-access', + 'node-name': 'fl-access', + 'file': 'fl-cbw', + })) else: log(vm.qmp('blockdev-backup', job_id='fleecing', @@ -109,25 +149,51 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): target=tmp_node, sync='none')) - log('') - log('--- Setting up NBD Export ---') - log('') + export_node = 'fl-access' if use_snapshot_access_filter else tmp_node - nbd_uri = 'nbd+unix:///%s?socket=%s' % (tmp_node, nbd_sock_path) - log(vm.qmp('nbd-server-start', - {'addr': {'type': 'unix', - 'data': {'path': nbd_sock_path}}})) + if push_backup: + log('') + log('--- Starting actual backup ---') + log('') - log(vm.qmp('nbd-server-add', device=tmp_node)) + log(vm.qmp('blockdev-add', **{ + 'driver': iotests.imgfmt, + 'node-name': 'target', + 'file': { + 'driver': 'file', + 'filename': target_img_path + } + })) + log(vm.qmp('blockdev-backup', device=export_node, + sync='full', target='target', + job_id='push-backup', speed=1)) + else: + log('') + log('--- Setting up NBD Export ---') + log('') - log('') - log('--- Sanity Check ---') - log('') + nbd_uri = 'nbd+unix:///%s?socket=%s' % (export_node, nbd_sock_path) + log(vm.qmp('nbd-server-start', + {'addr': { 'type': 'unix', + 'data': { 'path': nbd_sock_path } } })) - for p in patterns + zeroes: - cmd = 'read -P%s %s %s' % p - log(cmd) - assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 + log(vm.qmp('nbd-server-add', device=export_node)) + + log('') + log('--- Sanity Check ---') + log('') + + for p in patterns + zeroes: + cmd = 'read -P%s %s %s' % p + log(cmd) + + try: + qemu_io('-r', '-f', 'raw', '-c', cmd, nbd_uri) + except CalledProcessError as exc: + if bitmap and p in zeroes: + log(exc.stdout) + else: + raise log('') log('--- Testing COW ---') @@ -138,6 +204,23 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): log(cmd) log(vm.hmp_qemu_io(qom_path, cmd, qdev=True)) + if push_backup: + # Check that previous operations were done during backup, not after + # If backup is already finished, it's possible that it was finished + # even before hmp qemu_io write, and we didn't actually test + # copy-before-write operation. This should not happen, as we use + # speed=1. But worth checking. + result = vm.qmp('query-block-jobs') + assert len(result['return']) == 1 + + result = vm.qmp('block-job-set-speed', device='push-backup', speed=0) + assert result == {'return': {}} + + log(vm.event_wait(name='BLOCK_JOB_COMPLETED', + match={'data': {'device': 'push-backup'}}), + filters=[iotests.filter_qmp_event]) + log(vm.qmp('blockdev-del', node_name='target')) + log('') log('--- Verifying Data ---') log('') @@ -145,13 +228,30 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): for p in patterns + zeroes: cmd = 'read -P%s %s %s' % p log(cmd) - assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 + args = ['-r', '-c', cmd] + if push_backup: + args += [target_img_path] + else: + args += ['-f', 'raw', nbd_uri] + + try: + qemu_io(*args) + except CalledProcessError as exc: + if bitmap and p in zeroes: + log(exc.stdout) + else: + raise log('') log('--- Cleanup ---') log('') + if not push_backup: + log(vm.qmp('nbd-server-stop')) + if use_cbw: + if use_snapshot_access_filter: + log(vm.qmp('blockdev-del', node_name='fl-access')) log(vm.qmp('qom-set', path=qom_path, property='drive', value=src_node)) log(vm.qmp('blockdev-del', node_name='fl-cbw')) else: @@ -160,7 +260,6 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): assert e is not None log(e, filters=[iotests.filter_qmp_event]) - log(vm.qmp('nbd-server-stop')) log(vm.qmp('blockdev-del', node_name=tmp_node)) vm.shutdown() @@ -171,23 +270,43 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): for p in overwrite + remainder: cmd = 'read -P%s %s %s' % p log(cmd) - assert qemu_io_silent(base_img_path, '-c', cmd) == 0 + qemu_io(base_img_path, '-c', cmd) log('') log('Done') -def test(use_cbw): +def test(use_cbw, use_snapshot_access_filter, + nbd_sock_path=None, target_img_path=None, bitmap=False): with iotests.FilePath('base.img') as base_img_path, \ iotests.FilePath('fleece.img') as fleece_img_path, \ - iotests.FilePath('nbd.sock', - base_dir=iotests.sock_dir) as nbd_sock_path, \ iotests.VM() as vm: - do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm) + do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path, + fleece_img_path, nbd_sock_path, target_img_path, + bitmap=bitmap) + +def test_pull(use_cbw, use_snapshot_access_filter, bitmap=False): + with iotests.FilePath('nbd.sock', + base_dir=iotests.sock_dir) as nbd_sock_path: + test(use_cbw, use_snapshot_access_filter, nbd_sock_path, None, + bitmap=bitmap) + +def test_push(): + with iotests.FilePath('target.img') as target_img_path: + test(True, True, None, target_img_path) log('=== Test backup(sync=none) based fleecing ===\n') -test(False) +test_pull(False, False) -log('=== Test filter based fleecing ===\n') -test(True) +log('=== Test cbw-filter based fleecing ===\n') +test_pull(True, False) + +log('=== Test fleecing-format based fleecing ===\n') +test_pull(True, True) + +log('=== Test fleecing-format based fleecing with bitmap ===\n') +test_pull(True, True, bitmap=True) + +log('=== Test push backup with fleecing ===\n') +test_push() diff --git a/tests/qemu-iotests/tests/image-fleecing.out b/tests/qemu-iotests/tests/image-fleecing.out index e96d122a8b..acfc89ff0e 100644 --- a/tests/qemu-iotests/tests/image-fleecing.out +++ b/tests/qemu-iotests/tests/image-fleecing.out @@ -52,8 +52,8 @@ read -P0 0x3fe0000 64k --- Cleanup --- {"return": {}} -{"data": {"device": "fleecing", "len": 67108864, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"return": {}} +{"data": {"device": "fleecing", "len": 67108864, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"return": {}} --- Confirming writes --- @@ -67,7 +67,7 @@ read -P0xdc 32M 32k read -P0xcd 0x3ff0000 64k Done -=== Test filter based fleecing === +=== Test cbw-filter based fleecing === --- Setting up images --- @@ -137,3 +137,222 @@ read -P0xdc 32M 32k read -P0xcd 0x3ff0000 64k Done +=== Test fleecing-format based fleecing === + +--- Setting up images --- + +Done + +--- Launching VM --- + +Done + +--- Setting up Fleecing Graph --- + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +--- Setting up NBD Export --- + +{"return": {}} +{"return": {}} + +--- Sanity Check --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read -P0 0x2010000 32k +read -P0 0x3fe0000 64k + +--- Testing COW --- + +write -P0xab 0 64k +{"return": ""} +write -P0xad 0x00f8000 64k +{"return": ""} +write -P0x1d 0x2008000 64k +{"return": ""} +write -P0xea 0x3fe0000 64k +{"return": ""} + +--- Verifying Data --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read -P0 0x2010000 32k +read -P0 0x3fe0000 64k + +--- Cleanup --- + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +--- Confirming writes --- + +read -P0xab 0 64k +read -P0xad 0x00f8000 64k +read -P0x1d 0x2008000 64k +read -P0xea 0x3fe0000 64k +read -P0xd5 0x108000 32k +read -P0xdc 32M 32k +read -P0xcd 0x3ff0000 64k + +Done +=== Test fleecing-format based fleecing with bitmap === + +--- Setting up images --- + +Done + +--- Launching VM --- + +Done + +--- Setting up Fleecing Graph --- + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +--- Setting up NBD Export --- + +{"return": {}} +{"return": {}} + +--- Sanity Check --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read failed: Invalid argument + +read -P0 0x2010000 32k +read failed: Invalid argument + +read -P0 0x3fe0000 64k +read failed: Invalid argument + + +--- Testing COW --- + +write -P0xab 0 64k +{"return": ""} +write -P0xad 0x00f8000 64k +{"return": ""} +write -P0x1d 0x2008000 64k +{"return": ""} +write -P0xea 0x3fe0000 64k +{"return": ""} + +--- Verifying Data --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read failed: Invalid argument + +read -P0 0x2010000 32k +read failed: Invalid argument + +read -P0 0x3fe0000 64k +read failed: Invalid argument + + +--- Cleanup --- + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +--- Confirming writes --- + +read -P0xab 0 64k +read -P0xad 0x00f8000 64k +read -P0x1d 0x2008000 64k +read -P0xea 0x3fe0000 64k +read -P0xd5 0x108000 32k +read -P0xdc 32M 32k +read -P0xcd 0x3ff0000 64k + +Done +=== Test push backup with fleecing === + +--- Setting up images --- + +Done + +--- Launching VM --- + +Done + +--- Setting up Fleecing Graph --- + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +--- Starting actual backup --- + +{"return": {}} +{"return": {}} + +--- Testing COW --- + +write -P0xab 0 64k +{"return": ""} +write -P0xad 0x00f8000 64k +{"return": ""} +write -P0x1d 0x2008000 64k +{"return": ""} +write -P0xea 0x3fe0000 64k +{"return": ""} +{"data": {"device": "push-backup", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": {}} + +--- Verifying Data --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read -P0 0x2010000 32k +read -P0 0x3fe0000 64k + +--- Cleanup --- + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +--- Confirming writes --- + +read -P0xab 0 64k +read -P0xad 0x00f8000 64k +read -P0x1d 0x2008000 64k +read -P0xea 0x3fe0000 64k +read -P0xd5 0x108000 32k +read -P0xdc 32M 32k +read -P0xcd 0x3ff0000 64k + +Done diff --git a/tests/qemu-iotests/tests/migration-permissions b/tests/qemu-iotests/tests/migration-permissions index 6be02581c7..4e1da369c9 100755 --- a/tests/qemu-iotests/tests/migration-permissions +++ b/tests/qemu-iotests/tests/migration-permissions @@ -18,6 +18,8 @@ # import os +from subprocess import CalledProcessError + import iotests from iotests import imgfmt, qemu_img_create, qemu_io @@ -69,13 +71,12 @@ class TestMigrationPermissions(iotests.QMPTestCase): def test_post_migration_permissions(self): # Try to access the image R/W, which should fail because virtio-blk # has not been configured with share-rw=on - log = qemu_io('-f', imgfmt, '-c', 'quit', test_img) - if not log.strip(): - print('ERROR (pre-migration): qemu-io should not be able to ' - 'access this image, but it reported no error') - else: - # This is the expected output - assert 'Is another process using the image' in log + emsg = ('ERROR (pre-migration): qemu-io should not be able to ' + 'access this image, but it reported no error') + with self.assertRaises(CalledProcessError, msg=emsg) as ctx: + qemu_io('-f', imgfmt, '-c', 'quit', test_img) + if 'Is another process using the image' not in ctx.exception.stdout: + raise ctx.exception # Now migrate the VM self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}') @@ -84,13 +85,12 @@ class TestMigrationPermissions(iotests.QMPTestCase): # Try the same qemu-io access again, verifying that the WRITE # permission remains unshared - log = qemu_io('-f', imgfmt, '-c', 'quit', test_img) - if not log.strip(): - print('ERROR (post-migration): qemu-io should not be able to ' - 'access this image, but it reported no error') - else: - # This is the expected output - assert 'Is another process using the image' in log + emsg = ('ERROR (post-migration): qemu-io should not be able to ' + 'access this image, but it reported no error') + with self.assertRaises(CalledProcessError, msg=emsg) as ctx: + qemu_io('-f', imgfmt, '-c', 'quit', test_img) + if 'Is another process using the image' not in ctx.exception.stdout: + raise ctx.exception if __name__ == '__main__': diff --git a/tests/qemu-iotests/tests/mirror-ready-cancel-error b/tests/qemu-iotests/tests/mirror-ready-cancel-error index 770ffca379..01217459b9 100755 --- a/tests/qemu-iotests/tests/mirror-ready-cancel-error +++ b/tests/qemu-iotests/tests/mirror-ready-cancel-error @@ -31,15 +31,13 @@ target = os.path.join(iotests.test_dir, 'target.img') class TestMirrorReadyCancelError(iotests.QMPTestCase): def setUp(self) -> None: - assert iotests.qemu_img_create('-f', iotests.imgfmt, source, - str(image_size)) == 0 - assert iotests.qemu_img_create('-f', iotests.imgfmt, target, - str(image_size)) == 0 + iotests.qemu_img_create('-f', iotests.imgfmt, source, str(image_size)) + iotests.qemu_img_create('-f', iotests.imgfmt, target, str(image_size)) # Ensure that mirror will copy something before READY so the # target format layer will forward the pre-READY flush to its # file child - assert iotests.qemu_io_silent('-c', 'write -P 1 0 64k', source) == 0 + iotests.qemu_io('-c', 'write -P 1 0 64k', source) self.vm = iotests.VM() self.vm.launch() diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index b5849978c4..8bca592708 100755 --- a/tests/qemu-iotests/tests/mirror-top-perms +++ b/tests/qemu-iotests/tests/mirror-top-perms @@ -22,7 +22,6 @@ import os from qemu.machine import machine -from qemu.qmp import QMPConnectError import iotests from iotests import change_log_level, qemu_img @@ -34,8 +33,7 @@ source = os.path.join(iotests.test_dir, 'source.img') class TestMirrorTopPerms(iotests.QMPTestCase): def setUp(self): - assert qemu_img('create', '-f', iotests.imgfmt, source, - str(image_size)) == 0 + qemu_img('create', '-f', iotests.imgfmt, source, str(image_size)) self.vm = iotests.VM() self.vm.add_drive(source) self.vm.add_blockdev(f'null-co,node-name=null,size={image_size}') @@ -99,15 +97,13 @@ class TestMirrorTopPerms(iotests.QMPTestCase): self.vm_b.add_blockdev(f'file,node-name=drive0,filename={source}') self.vm_b.add_device('virtio-blk,drive=drive0,share-rw=on') try: - # Silence AQMP errors temporarily. - # TODO: Remove this and just allow the errors to be logged when - # AQMP fully replaces QMP. - with change_log_level('qemu.aqmp'): + # Silence QMP logging errors temporarily. + with change_log_level('qemu.qmp'): self.vm_b.launch() print('ERROR: VM B launched successfully, ' 'this should not have happened') - except (QMPConnectError, machine.VMLaunchFailure): - assert 'Is another process using the image' in self.vm_b.get_log() + except machine.VMLaunchFailure as exc: + assert 'Is another process using the image' in exc.output result = self.vm.qmp('block-job-cancel', device='mirror') diff --git a/tests/qemu-iotests/tests/nbd-reconnect-on-open b/tests/qemu-iotests/tests/nbd-reconnect-on-open index 8be721a24f..d0b401b060 100755 --- a/tests/qemu-iotests/tests/nbd-reconnect-on-open +++ b/tests/qemu-iotests/tests/nbd-reconnect-on-open @@ -39,7 +39,7 @@ def check_fail_to_connect(open_timeout): log(f'Check fail to connect with {open_timeout} seconds of timeout') start_t = time.time() - qemu_io_log(*create_args(open_timeout)) + qemu_io_log(*create_args(open_timeout), check=False) delta_t = time.time() - start_t max_delta = open_timeout + 0.2 diff --git a/tests/qemu-iotests/tests/parallels-read-bitmap b/tests/qemu-iotests/tests/parallels-read-bitmap index af6b9c5db3..38ab5fa5b2 100755 --- a/tests/qemu-iotests/tests/parallels-read-bitmap +++ b/tests/qemu-iotests/tests/parallels-read-bitmap @@ -18,9 +18,8 @@ # along with this program. If not, see . # -import json import iotests -from iotests import qemu_nbd_popen, qemu_img_pipe, log, file_path +from iotests import qemu_nbd_popen, qemu_img_map, log, file_path iotests.script_initialize(supported_fmts=['parallels']) @@ -36,8 +35,7 @@ iotests.unarchive_sample_image('parallels-with-bitmap', disk) with qemu_nbd_popen('--read-only', f'--socket={nbd_sock}', f'--bitmap={bitmap}', '-f', iotests.imgfmt, disk): - out = qemu_img_pipe('map', '--output=json', '--image-opts', nbd_opts) - chunks = json.loads(out) + chunks = qemu_img_map('--image-opts', nbd_opts) cluster = 64 * 1024 log('dirty clusters (cluster size is 64K):') diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing index 3c397b08ea..15be32dcb9 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -19,7 +19,7 @@ # import iotests -from iotests import log, qemu_img_create, qemu_img, qemu_img_pipe +from iotests import log, qemu_img_create, qemu_img, qemu_img_info iotests.script_initialize(supported_fmts=['qcow2'], unsupported_imgopts=['compat']) @@ -27,13 +27,13 @@ iotests.script_initialize(supported_fmts=['qcow2'], top, base = iotests.file_path('top', 'base') size = '1M' -assert qemu_img_create('-f', iotests.imgfmt, base, size) == 0 -assert qemu_img_create('-f', iotests.imgfmt, '-b', base, - '-F', iotests.imgfmt, top, size) == 0 +qemu_img_create('-f', iotests.imgfmt, base, size) +qemu_img_create('-f', iotests.imgfmt, '-b', base, + '-F', iotests.imgfmt, top, size) -assert qemu_img('bitmap', '--add', base, 'bitmap0') == 0 +qemu_img('bitmap', '--add', base, 'bitmap0') # Just assert that our method of checking bitmaps in the image works. -assert 'bitmaps' in qemu_img_pipe('info', base) +assert 'bitmaps' in qemu_img_info(base)['format-specific']['data'] vm = iotests.VM().add_drive(top, 'backing.node-name=base') vm.launch() @@ -68,5 +68,5 @@ if result != {'return': {}}: vm.shutdown() -if 'bitmaps' in qemu_img_pipe('info', base): +if 'bitmaps' in qemu_img_info(base)['format-specific']['data']: log('ERROR: Bitmap is still in the base image') diff --git a/tests/qemu-iotests/tests/stream-error-on-reset b/tests/qemu-iotests/tests/stream-error-on-reset index 7eaedb24d7..5a8c3a9e8d 100755 --- a/tests/qemu-iotests/tests/stream-error-on-reset +++ b/tests/qemu-iotests/tests/stream-error-on-reset @@ -21,7 +21,7 @@ import os import iotests -from iotests import imgfmt, qemu_img_create, qemu_io_silent, QMPTestCase +from iotests import imgfmt, qemu_img_create, qemu_io, QMPTestCase image_size = 1 * 1024 * 1024 @@ -54,9 +54,9 @@ class TestStreamErrorOnReset(QMPTestCase): to it will result in an error - top image is attached to a virtio-scsi device """ - assert qemu_img_create('-f', imgfmt, base, str(image_size)) == 0 - assert qemu_io_silent('-c', f'write 0 {data_size}', base) == 0 - assert qemu_img_create('-f', imgfmt, top, str(image_size)) == 0 + qemu_img_create('-f', imgfmt, base, str(image_size)) + qemu_io('-c', f'write 0 {data_size}', base) + qemu_img_create('-f', imgfmt, top, str(image_size)) self.vm = iotests.VM() self.vm.add_args('-accel', 'tcg') # Make throttling work properly diff --git a/tests/qtest/ac97-test.c b/tests/qtest/ac97-test.c index e09f2495d2..b084e31bff 100644 --- a/tests/qtest/ac97-test.c +++ b/tests/qtest/ac97-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/acpi-utils.c b/tests/qtest/acpi-utils.c index 766c48e3a6..673fc97586 100644 --- a/tests/qtest/acpi-utils.c +++ b/tests/qtest/acpi-utils.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" #include "qemu/bitmap.h" #include "acpi-utils.h" #include "boot-sector.h" diff --git a/tests/qtest/acpi-utils.h b/tests/qtest/acpi-utils.h index 261784d251..0c86780689 100644 --- a/tests/qtest/acpi-utils.h +++ b/tests/qtest/acpi-utils.h @@ -13,7 +13,7 @@ #ifndef TEST_ACPI_UTILS_H #define TEST_ACPI_UTILS_H -#include "libqos/libqtest.h" +#include "libqtest.h" /* DSDT and SSDTs format */ typedef struct { diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 8073ccc205..f1e510b0ac 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -25,12 +25,11 @@ #include "qemu/osdep.h" #include -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/libqos-pc.h" #include "libqos/ahci.h" #include "libqos/pci-pc.h" -#include "qemu-common.h" #include "qapi/qmp/qdict.h" #include "qemu/host-utils.h" diff --git a/tests/qtest/am53c974-test.c b/tests/qtest/am53c974-test.c index d214a912b3..ed3ac7db20 100644 --- a/tests/qtest/am53c974-test.c +++ b/tests/qtest/am53c974-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" static void test_cmdfifo_underflow_ok(void) diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index f76652143a..5a14527386 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" #include "qemu/bitops.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c new file mode 100644 index 0000000000..c1003f2d1b --- /dev/null +++ b/tests/qtest/aspeed_gpio-test.c @@ -0,0 +1,87 @@ +/* + * QTest testcase for the Aspeed GPIO Controller. + * + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/timer.h" +#include "qapi/qmp/qdict.h" +#include "libqtest-single.h" + +static bool qom_get_bool(QTestState *s, const char *path, const char *property) +{ + QDict *r; + bool b; + + r = qtest_qmp(s, "{ 'execute': 'qom-get', 'arguments': " + "{ 'path': %s, 'property': %s } }", path, property); + b = qdict_get_bool(r, "return"); + qobject_unref(r); + + return b; +} + +static void qom_set_bool(QTestState *s, const char *path, const char *property, + bool value) +{ + QDict *r; + + r = qtest_qmp(s, "{ 'execute': 'qom-set', 'arguments': " + "{ 'path': %s, 'property': %s, 'value': %i } }", + path, property, value); + qobject_unref(r); +} + +static void test_set_colocated_pins(const void *data) +{ + QTestState *s = (QTestState *)data; + + /* + * gpioV4-7 occupy bits within a single 32-bit value, so we want to make + * sure that modifying one doesn't affect the other. + */ + qom_set_bool(s, "/machine/soc/gpio", "gpioV4", true); + qom_set_bool(s, "/machine/soc/gpio", "gpioV5", false); + qom_set_bool(s, "/machine/soc/gpio", "gpioV6", true); + qom_set_bool(s, "/machine/soc/gpio", "gpioV7", false); + g_assert(qom_get_bool(s, "/machine/soc/gpio", "gpioV4")); + g_assert(!qom_get_bool(s, "/machine/soc/gpio", "gpioV5")); + g_assert(qom_get_bool(s, "/machine/soc/gpio", "gpioV6")); + g_assert(!qom_get_bool(s, "/machine/soc/gpio", "gpioV7")); +} + +int main(int argc, char **argv) +{ + QTestState *s; + int r; + + g_test_init(&argc, &argv, NULL); + + s = qtest_init("-machine ast2600-evb"); + qtest_add_data_func("/ast2600/gpio/set_colocated_pins", s, + test_set_colocated_pins); + r = g_test_run(); + qtest_quit(s); + + return r; +} diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index 09ee31545e..ce86a44672 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -7,8 +7,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" -#include "qemu-common.h" +#include "libqtest.h" #include "qemu/bitops.h" #define HACE_CMD 0x10 @@ -21,6 +20,7 @@ #define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) #define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) #define HACE_SG_EN BIT(18) +#define HACE_ACCUM_EN BIT(8) #define HACE_STS 0x1c #define HACE_RSA_ISR BIT(13) @@ -96,6 +96,57 @@ static const uint8_t test_result_sg_sha256[] = { 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; +/* + * The accumulative mode requires firmware to provide internal initial state + * and message padding (including length L at the end of padding). + * + * This test vector is a ascii text "abc" with padding message. + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abc' | dd of=/tmp/test + * for hash in sha512sum sha256sum; do $hash /tmp/test; done + */ +static const uint8_t test_vector_accum_512[] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_vector_accum_256[] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_result_accum_sha512[] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, + 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, + 0xa5, 0x4c, 0xa4, 0x9f}; + +static const uint8_t test_result_accum_sha256[] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; static void write_regs(QTestState *s, uint32_t base, uint32_t src, uint32_t length, uint32_t out, uint32_t method) @@ -308,6 +359,88 @@ static void test_sha512_sg(const char *machine, const uint32_t base, qtest_quit(s); } +static void test_sha256_accum(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t buffer_addr = src_addr + 0x1000000; + const uint32_t digest_addr = src_addr + 0x4000000; + uint8_t digest[32] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_256, + sizeof(test_vector_accum_256)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_256), + digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha256, sizeof(digest)); + + qtest_quit(s); +} + +static void test_sha512_accum(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t buffer_addr = src_addr + 0x1000000; + const uint32_t digest_addr = src_addr + 0x4000000; + uint8_t digest[64] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_512, + sizeof(test_vector_accum_512)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_512), + digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha512, sizeof(digest)); + + qtest_quit(s); +} + struct masks { uint32_t src; uint32_t dest; @@ -396,6 +529,16 @@ static void test_sha512_sg_ast2600(void) test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } +static void test_sha256_accum_ast2600(void) +{ + test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha512_accum_ast2600(void) +{ + test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + static void test_addresses_ast2600(void) { test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks); @@ -455,6 +598,9 @@ int main(int argc, char **argv) qtest_add_func("ast2600/hace/sha512_sg", test_sha512_sg_ast2600); qtest_add_func("ast2600/hace/sha256_sg", test_sha256_sg_ast2600); + qtest_add_func("ast2600/hace/sha512_accum", test_sha512_accum_ast2600); + qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600); + qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500); qtest_add_func("ast2500/hace/sha512", test_sha512_ast2500); qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500); diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index c4a2d1e166..5dddedabcd 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -57,7 +57,6 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" #include "hw/firmware/smbios.h" #include "qemu/bitmap.h" #include "acpi-utils.h" diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index f1f59b1261..0680d79d6d 100644 --- a/tests/qtest/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "libqos/fw_cfg.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "standard-headers/linux/qemu_fw_cfg.h" diff --git a/tests/qtest/boot-sector.c b/tests/qtest/boot-sector.c index ea8f264661..44a109abd8 100644 --- a/tests/qtest/boot-sector.c +++ b/tests/qtest/boot-sector.c @@ -12,8 +12,7 @@ */ #include "qemu/osdep.h" #include "boot-sector.h" -#include "qemu-common.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #define LOW(x) ((x) & 0xff) #define HIGH(x) ((x) >> 8) diff --git a/tests/qtest/boot-sector.h b/tests/qtest/boot-sector.h index b339fdee4c..6ee6bb4d97 100644 --- a/tests/qtest/boot-sector.h +++ b/tests/qtest/boot-sector.h @@ -14,7 +14,7 @@ #ifndef TEST_BOOT_SECTOR_H #define TEST_BOOT_SECTOR_H -#include "libqos/libqtest.h" +#include "libqtest.h" /* Create boot disk file. fname must be a suitable string for mkstemp() */ int boot_sector_init(char *fname); diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index d72a82d629..2f99d71cab 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -14,7 +14,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/libqos-spapr.h" static const uint8_t bios_avr[] = { diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index fdd889a487..a7766a9e65 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "boot-sector.h" #include "qapi/qmp/qdict.h" diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c index a1c689414b..7f5dd5f85a 100644 --- a/tests/qtest/cpu-plug-test.c +++ b/tests/qtest/cpu-plug-test.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "libqtest-single.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c index 43c77aff04..8be5974763 100644 --- a/tests/qtest/dbus-display-test.c +++ b/tests/qtest/dbus-display-test.c @@ -2,8 +2,7 @@ #include "qemu/dbus.h" #include #include -#include "libqos/libqtest.h" -#include "qemu-common.h" +#include "libqtest.h" #include "dbus-display1.h" static GDBusConnection* diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c index aca9b98b7a..74ede651f6 100644 --- a/tests/qtest/dbus-vmstate-test.c +++ b/tests/qtest/dbus-vmstate-test.c @@ -1,8 +1,7 @@ #include "qemu/osdep.h" #include #include -#include "libqos/libqtest.h" -#include "qemu-common.h" +#include "libqtest.h" #include "dbus-vmstate1.h" #include "migration-helpers.h" diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c index bbec166dbc..5b0ffe43f5 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -18,11 +18,10 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" -#include "libqos/libqtest.h" +#include "libqtest.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index 404a92e132..2e3137843e 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 0cc18dfa4a..5e6d58b4dd 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/virtio.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" diff --git a/tests/qtest/ds1338-test.c b/tests/qtest/ds1338-test.c index c5d46bcc64..f6ade9a050 100644 --- a/tests/qtest/ds1338-test.c +++ b/tests/qtest/ds1338-test.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/i2c.h" #define DS1338_ADDR 0x68 diff --git a/tests/qtest/e1000-test.c b/tests/qtest/e1000-test.c index ea286d1793..c387984ef6 100644 --- a/tests/qtest/e1000-test.c +++ b/tests/qtest/e1000-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/e1000e-test.c b/tests/qtest/e1000e-test.c index 0273fe4c15..ddd6983ede 100644 --- a/tests/qtest/e1000e-test.c +++ b/tests/qtest/e1000e-test.c @@ -25,9 +25,7 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "libqtest-single.h" -#include "qemu-common.h" #include "libqos/pci-pc.h" #include "qemu/sockets.h" #include "qemu/iov.h" @@ -91,9 +89,9 @@ static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *a g_assert_cmphex(le32_to_cpu(descr.upper.data) & dsta_dd, ==, dsta_dd); /* Check data sent to the backend */ - ret = qemu_recv(test_sockets[0], &recv_len, sizeof(recv_len), 0); + ret = recv(test_sockets[0], &recv_len, sizeof(recv_len), 0); g_assert_cmpint(ret, == , sizeof(recv_len)); - ret = qemu_recv(test_sockets[0], buffer, 64, 0); + ret = recv(test_sockets[0], buffer, 64, 0); g_assert_cmpint(ret, >=, 5); g_assert_cmpstr(buffer, == , "TEST"); diff --git a/tests/qtest/eepro100-test.c b/tests/qtest/eepro100-test.c index d72ad099f1..8dbffff0b8 100644 --- a/tests/qtest/eepro100-test.c +++ b/tests/qtest/eepro100-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/endianness-test.c b/tests/qtest/endianness-test.c index 9c03b72dc9..222d116fae 100644 --- a/tests/qtest/endianness-test.c +++ b/tests/qtest/endianness-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/bswap.h" typedef struct TestCase TestCase; @@ -28,6 +28,7 @@ struct TestCase { static const TestCase test_cases[] = { { "i386", "pc", -1 }, { "mips", "malta", 0x10000000, .bswap = true }, + { "mipsel", "malta", 0x10000000 }, { "mips64", "magnum", 0x90000000, .bswap = true }, { "mips64", "pica61", 0x90000000, .bswap = true }, { "mips64", "malta", 0x10000000, .bswap = true }, diff --git a/tests/qtest/erst-test.c b/tests/qtest/erst-test.c index f94cd8dd8e..4e768a126f 100644 --- a/tests/qtest/erst-test.c +++ b/tests/qtest/erst-test.c @@ -10,8 +10,7 @@ #include "qemu/osdep.h" #include #include "libqos/libqos-pc.h" -#include "libqos/libqtest.h" -#include "qemu-common.h" +#include "libqtest.h" #include "hw/pci/pci.h" diff --git a/tests/qtest/es1370-test.c b/tests/qtest/es1370-test.c index 2fd7fd2d3d..adccdac1be 100644 --- a/tests/qtest/es1370-test.c +++ b/tests/qtest/es1370-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c index b0d40012e6..0b3c2c0d52 100644 --- a/tests/qtest/fdc-test.c +++ b/tests/qtest/fdc-test.c @@ -27,7 +27,6 @@ #include "libqtest-single.h" #include "qapi/qmp/qdict.h" -#include "qemu-common.h" /* TODO actually test the results and get rid of this */ #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) @@ -551,7 +550,7 @@ static void fuzz_registers(void) static bool qtest_check_clang_sanitizer(void) { -#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) +#ifdef QEMU_SANITIZE_ADDRESS return true; #else g_test_skip("QEMU not configured using --enable-sanitizers"); diff --git a/tests/qtest/fuzz-e1000e-test.c b/tests/qtest/fuzz-e1000e-test.c index 66229e6096..5052883fb6 100644 --- a/tests/qtest/fuzz-e1000e-test.c +++ b/tests/qtest/fuzz-e1000e-test.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * https://bugs.launchpad.net/qemu/+bug/1879531 diff --git a/tests/qtest/fuzz-lsi53c895a-test.c b/tests/qtest/fuzz-lsi53c895a-test.c index ba5d468970..2e8e67859e 100644 --- a/tests/qtest/fuzz-lsi53c895a-test.c +++ b/tests/qtest/fuzz-lsi53c895a-test.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * This used to trigger the assert in lsi_do_dma() @@ -39,14 +39,10 @@ static void test_lsi_do_dma_empty_queue(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); - g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("fuzz/lsi53c895a/lsi_do_dma_empty_queue", - test_lsi_do_dma_empty_queue); - } + qtest_add_func("fuzz/lsi53c895a/lsi_do_dma_empty_queue", + test_lsi_do_dma_empty_queue); return g_test_run(); } diff --git a/tests/qtest/fuzz-megasas-test.c b/tests/qtest/fuzz-megasas-test.c index e1141c58a4..287fe19fc7 100644 --- a/tests/qtest/fuzz-megasas-test.c +++ b/tests/qtest/fuzz-megasas-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * This used to trigger the assert in scsi_dma_complete @@ -64,16 +64,12 @@ static void test_gitlab_issue521_megasas_sgl_ovf(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); - g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("fuzz/test_lp1878263_megasas_zero_iov_cnt", - test_lp1878263_megasas_zero_iov_cnt); - qtest_add_func("fuzz/gitlab_issue521_megasas_sgl_ovf", - test_gitlab_issue521_megasas_sgl_ovf); - } + qtest_add_func("fuzz/test_lp1878263_megasas_zero_iov_cnt", + test_lp1878263_megasas_zero_iov_cnt); + qtest_add_func("fuzz/gitlab_issue521_megasas_sgl_ovf", + test_gitlab_issue521_megasas_sgl_ovf); return g_test_run(); } diff --git a/tests/qtest/fuzz-sb16-test.c b/tests/qtest/fuzz-sb16-test.c index f47a8bcdbd..a65826b943 100644 --- a/tests/qtest/fuzz-sb16-test.c +++ b/tests/qtest/fuzz-sb16-test.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * This used to trigger the assert in audio_calloc @@ -55,15 +55,11 @@ static void test_fuzz_sb16_0xd4(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); - g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "i386") == 0) { - qtest_add_func("fuzz/test_fuzz_sb16/1c", test_fuzz_sb16_0x1c); - qtest_add_func("fuzz/test_fuzz_sb16/91", test_fuzz_sb16_0x91); - qtest_add_func("fuzz/test_fuzz_sb16/d4", test_fuzz_sb16_0xd4); - } + qtest_add_func("fuzz/test_fuzz_sb16/1c", test_fuzz_sb16_0x1c); + qtest_add_func("fuzz/test_fuzz_sb16/91", test_fuzz_sb16_0x91); + qtest_add_func("fuzz/test_fuzz_sb16/d4", test_fuzz_sb16_0xd4); - return g_test_run(); + return g_test_run(); } diff --git a/tests/qtest/fuzz-sdcard-test.c b/tests/qtest/fuzz-sdcard-test.c index ae14305344..e7fd818148 100644 --- a/tests/qtest/fuzz-sdcard-test.c +++ b/tests/qtest/fuzz-sdcard-test.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * https://gitlab.com/qemu-project/qemu/-/issues/450 @@ -87,16 +87,88 @@ static void oss_fuzz_36217(void) qtest_quit(s); } +/* + * https://gitlab.com/qemu-project/qemu/-/issues/451 + * Used to trigger a heap buffer overflow. + */ +static void oss_fuzz_36391(void) +{ + QTestState *s; + + s = qtest_init(" -display none -m 512M -nodefaults -nographic" + " -device sdhci-pci,sd-spec-version=3" + " -device sd-card,drive=drv" + " -drive if=none,index=0,file=null-co://,format=raw,id=drv"); + qtest_outl(s, 0xcf8, 0x80001010); + qtest_outl(s, 0xcfc, 0xe0000000); + qtest_outl(s, 0xcf8, 0x80001004); + qtest_outw(s, 0xcfc, 0x7); + qtest_bufwrite(s, 0xe0000005, "\x73", 0x1); + qtest_bufwrite(s, 0xe0000028, "\x55", 0x1); + qtest_bufwrite(s, 0xe000002c, "\x55", 0x1); + qtest_bufwrite(s, 0x0, "\x65", 0x1); + qtest_bufwrite(s, 0x7, "\x69", 0x1); + qtest_bufwrite(s, 0x8, "\x65", 0x1); + qtest_bufwrite(s, 0xf, "\x69", 0x1); + qtest_bufwrite(s, 0x10, "\x65", 0x1); + qtest_bufwrite(s, 0x17, "\x69", 0x1); + qtest_bufwrite(s, 0x18, "\x65", 0x1); + qtest_bufwrite(s, 0x1f, "\x69", 0x1); + qtest_bufwrite(s, 0x20, "\x65", 0x1); + qtest_bufwrite(s, 0x27, "\x69", 0x1); + qtest_bufwrite(s, 0x28, "\x65", 0x1); + qtest_bufwrite(s, 0x2f, "\x69", 0x1); + qtest_bufwrite(s, 0x30, "\x65", 0x1); + qtest_bufwrite(s, 0x37, "\x69", 0x1); + qtest_bufwrite(s, 0x38, "\x65", 0x1); + qtest_bufwrite(s, 0x3f, "\x69", 0x1); + qtest_bufwrite(s, 0x40, "\x65", 0x1); + qtest_bufwrite(s, 0x47, "\x69", 0x1); + qtest_bufwrite(s, 0x48, "\x65", 0x1); + qtest_bufwrite(s, 0xe000000c, "\x55", 0x1); + qtest_bufwrite(s, 0xe000000e, "\x2c", 0x1); + qtest_bufwrite(s, 0xe000000f, "\x5b", 0x1); + qtest_bufwrite(s, 0xe0000010, "\x06\x46", 0x2); + qtest_bufwrite(s, 0x50, "\x65", 0x1); + qtest_bufwrite(s, 0x57, "\x69", 0x1); + qtest_bufwrite(s, 0x58, "\x65", 0x1); + qtest_bufwrite(s, 0x5f, "\x69", 0x1); + qtest_bufwrite(s, 0x60, "\x65", 0x1); + qtest_bufwrite(s, 0x67, "\x69", 0x1); + qtest_bufwrite(s, 0x68, "\x65", 0x1); + qtest_bufwrite(s, 0x6f, "\x69", 0x1); + qtest_bufwrite(s, 0x70, "\x65", 0x1); + qtest_bufwrite(s, 0x77, "\x69", 0x1); + qtest_bufwrite(s, 0x78, "\x65", 0x1); + qtest_bufwrite(s, 0x7f, "\x69", 0x1); + qtest_bufwrite(s, 0x80, "\x65", 0x1); + qtest_bufwrite(s, 0x87, "\x69", 0x1); + qtest_bufwrite(s, 0x88, "\x65", 0x1); + qtest_bufwrite(s, 0x8f, "\x69", 0x1); + qtest_bufwrite(s, 0x90, "\x65", 0x1); + qtest_bufwrite(s, 0x97, "\x69", 0x1); + qtest_bufwrite(s, 0x98, "\x65", 0x1); + qtest_bufwrite(s, 0xe0000026, "\x5a\x06", 0x2); + qtest_bufwrite(s, 0xe0000028, "\x46\xc0\xc9\xc9", 0x4); + qtest_bufwrite(s, 0xe0000028, "\x55", 0x1); + qtest_bufwrite(s, 0xe000002a, "\x5a", 0x1); + qtest_bufwrite(s, 0xa0, "\x65", 0x1); + qtest_bufwrite(s, 0xa5, "\xff", 0x1); + qtest_bufwrite(s, 0xa6, "\xff", 0x1); + qtest_bufwrite(s, 0xa7, "\xdf", 0x1); + qtest_bufwrite(s, 0xe000000c, "\x27", 0x1); + qtest_bufwrite(s, 0xe000000f, "\x55", 0x1); + + qtest_quit(s); +} + int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); - g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "i386") == 0) { - qtest_add_func("fuzz/sdcard/oss_fuzz_29225", oss_fuzz_29225); - qtest_add_func("fuzz/sdcard/oss_fuzz_36217", oss_fuzz_36217); - } + qtest_add_func("fuzz/sdcard/oss_fuzz_29225", oss_fuzz_29225); + qtest_add_func("fuzz/sdcard/oss_fuzz_36217", oss_fuzz_36217); + qtest_add_func("fuzz/sdcard/oss_fuzz_36391", oss_fuzz_36391); - return g_test_run(); + return g_test_run(); } diff --git a/tests/qtest/fuzz-virtio-scsi-test.c b/tests/qtest/fuzz-virtio-scsi-test.c index aaf6d10e18..71c91b0356 100644 --- a/tests/qtest/fuzz-virtio-scsi-test.c +++ b/tests/qtest/fuzz-virtio-scsi-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * Here a MemoryRegionCache pointed to an MMIO region but had a @@ -62,14 +62,10 @@ static void test_mmio_oob_from_memory_region_cache(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); - g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("fuzz/test_mmio_oob_from_memory_region_cache", - test_mmio_oob_from_memory_region_cache); - } + qtest_add_func("fuzz/test_mmio_oob_from_memory_region_cache", + test_mmio_oob_from_memory_region_cache); return g_test_run(); } diff --git a/tests/qtest/fuzz-xlnx-dp-test.c b/tests/qtest/fuzz-xlnx-dp-test.c index 69eb6c0eb1..51e9a37300 100644 --- a/tests/qtest/fuzz-xlnx-dp-test.c +++ b/tests/qtest/fuzz-xlnx-dp-test.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * This used to trigger the out-of-bounds read in xlnx_dp_read diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index 5f77c84983..a7a5e14fa3 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -21,7 +21,7 @@ #include "sysemu/runstate.h" #include "qemu/main-loop.h" #include "qemu/rcu.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/qgraph.h" #include "fuzz.h" diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h index 3a8570e84c..c5f0b7227a 100644 --- a/tests/qtest/fuzz/fuzz.h +++ b/tests/qtest/fuzz/fuzz.h @@ -17,7 +17,7 @@ #include "qemu/units.h" #include "qapi/error.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" /** * A libfuzzer fuzzing target diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index dd7e25851c..25df19fd5a 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -15,7 +15,7 @@ #include #include "hw/core/cpu.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" #include "fork_fuzz.h" @@ -743,14 +743,12 @@ static void usage(void) static int locate_fuzz_memory_regions(Object *child, void *opaque) { - const char *name; MemoryRegion *mr; if (object_dynamic_cast(child, TYPE_MEMORY_REGION)) { mr = MEMORY_REGION(child); if ((memory_region_is_ram(mr) || memory_region_is_ram_device(mr) || memory_region_is_rom(mr)) == false) { - name = object_get_canonical_path_component(child); /* * We don't want duplicate pointers to the same MemoryRegion, so * try to remove copies of the pointer, before adding it. diff --git a/tests/qtest/fuzz/i440fx_fuzz.c b/tests/qtest/fuzz/i440fx_fuzz.c index 86796bff2b..b17fc725df 100644 --- a/tests/qtest/fuzz/i440fx_fuzz.c +++ b/tests/qtest/fuzz/i440fx_fuzz.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/pci.h" #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index 7a244c951e..c856d3d500 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -19,11 +19,10 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qemu-common.h" #include "exec/memory.h" #include "qemu/main-loop.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/malloc.h" #include "tests/qtest/libqos/qgraph.h" #include "tests/qtest/libqos/qgraph_internal.h" diff --git a/tests/qtest/fuzz/virtio_blk_fuzz.c b/tests/qtest/fuzz/virtio_blk_fuzz.c index 623a756fd4..236d078cc8 100644 --- a/tests/qtest/fuzz/virtio_blk_fuzz.c +++ b/tests/qtest/fuzz/virtio_blk_fuzz.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/virtio-blk.h" #include "tests/qtest/libqos/virtio.h" #include "tests/qtest/libqos/virtio-pci.h" diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c index 0e873ab8e2..c2c15f07f0 100644 --- a/tests/qtest/fuzz/virtio_net_fuzz.c +++ b/tests/qtest/fuzz/virtio_net_fuzz.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "standard-headers/linux/virtio_config.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/virtio-net.h" #include "fuzz.h" #include "fork_fuzz.h" @@ -151,7 +151,7 @@ static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg) { int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds); g_assert_cmpint(ret, !=, -1); - fcntl(sockfds[0], F_SETFL, O_NONBLOCK); + g_unix_set_fd_nonblocking(sockfds[0], true, NULL); sockfds_initialized = true; g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sockfds[1]); diff --git a/tests/qtest/fuzz/virtio_scsi_fuzz.c b/tests/qtest/fuzz/virtio_scsi_fuzz.c index 6ff6fabe4a..b3220ef6cb 100644 --- a/tests/qtest/fuzz/virtio_scsi_fuzz.c +++ b/tests/qtest/fuzz/virtio_scsi_fuzz.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" -#include "tests/qtest/libqos/libqtest.h" +#include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/virtio-scsi.h" #include "tests/qtest/libqos/virtio.h" #include "tests/qtest/libqos/virtio-pci.h" diff --git a/tests/qtest/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c index 95b3907c18..5dc807ba23 100644 --- a/tests/qtest/fw_cfg-test.c +++ b/tests/qtest/fw_cfg-test.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "standard-headers/linux/qemu_fw_cfg.h" #include "libqos/fw_cfg.h" #include "qemu/bswap.h" diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index 64023c0574..413cf964c0 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -16,10 +16,9 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/bswap.h" #include "qapi/qmp/qlist.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/fw_cfg.h" #include "libqos/libqos.h" #include "standard-headers/linux/qemu_fw_cfg.h" diff --git a/tests/qtest/hexloader-test.c b/tests/qtest/hexloader-test.c index 561502052a..8b7aa2d72d 100644 --- a/tests/qtest/hexloader-test.c +++ b/tests/qtest/hexloader-test.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* Load 'test.hex' and verify that the in-memory contents are as expected. * 'test.hex' is a memory test pattern stored in Hexadecimal Object diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 19de3b4104..5bcb75a7e5 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -25,12 +25,11 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/malloc-pc.h" #include "qapi/qmp/qdict.h" -#include "qemu-common.h" #include "qemu/bswap.h" #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" diff --git a/tests/qtest/intel-hda-test.c b/tests/qtest/intel-hda-test.c index fc25ccc33c..a58c98e4d1 100644 --- a/tests/qtest/intel-hda-test.c +++ b/tests/qtest/intel-hda-test.c @@ -29,11 +29,45 @@ static void ich9_test(void) qtest_end(); } +/* + * https://gitlab.com/qemu-project/qemu/-/issues/542 + * Used to trigger: + * AddressSanitizer: stack-overflow + */ +static void test_issue542_ich6(void) +{ + QTestState *s; + + s = qtest_init("-nographic -nodefaults -M pc-q35-6.2 " + "-device intel-hda,id=" HDA_ID CODEC_DEVICES); + + qtest_outl(s, 0xcf8, 0x80000804); + qtest_outw(s, 0xcfc, 0x06); + qtest_bufwrite(s, 0xff0d060f, "\x03", 1); + qtest_bufwrite(s, 0x0, "\x12", 1); + qtest_bufwrite(s, 0x2, "\x2a", 1); + qtest_writeb(s, 0x0, 0x12); + qtest_writeb(s, 0x2, 0x2a); + qtest_outl(s, 0xcf8, 0x80000811); + qtest_outl(s, 0xcfc, 0x006a4400); + qtest_bufwrite(s, 0x6a44005a, "\x01", 1); + qtest_bufwrite(s, 0x6a44005c, "\x02", 1); + qtest_bufwrite(s, 0x6a442050, "\x00\x00\x44\x6a", 4); + qtest_bufwrite(s, 0x6a44204a, "\x01", 1); + qtest_bufwrite(s, 0x6a44204c, "\x02", 1); + qtest_bufwrite(s, 0x6a44005c, "\x02", 1); + qtest_bufwrite(s, 0x6a442050, "\x00\x00\x44\x6a", 4); + qtest_bufwrite(s, 0x6a44204a, "\x01", 1); + qtest_bufwrite(s, 0x6a44204c, "\x02", 1); + qtest_quit(s); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); qtest_add_func("/intel-hda/ich6", ich6_test); qtest_add_func("/intel-hda/ich9", ich9_test); + qtest_add_func("/intel-hda/fuzz/issue542", test_issue542_ich6); return g_test_run(); } diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index 19612e9405..ed431e34e6 100644 --- a/tests/qtest/ipmi-bt-test.c +++ b/tests/qtest/ipmi-bt-test.c @@ -31,7 +31,6 @@ #include "libqtest-single.h" -#include "qemu-common.h" #define IPMI_IRQ 5 diff --git a/tests/qtest/ipoctal232-test.c b/tests/qtest/ipoctal232-test.c index 65ce10b81b..53a8c9b13c 100644 --- a/tests/qtest/ipoctal232-test.c +++ b/tests/qtest/ipoctal232-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c new file mode 100644 index 0000000000..5553ea410a --- /dev/null +++ b/tests/qtest/isl_pmbus_vr-test.c @@ -0,0 +1,474 @@ +/* + * QTests for the ISL_PMBUS digital voltage regulators + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include +#include "hw/i2c/pmbus_device.h" +#include "hw/sensor/isl_pmbus_vr.h" +#include "libqtest-single.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnum.h" +#include "qemu/bitops.h" + +#define TEST_ID "isl_pmbus_vr-test" +#define TEST_ADDR (0x43) + +static uint16_t qmp_isl_pmbus_vr_get(const char *id, const char *property) +{ + QDict *response; + uint64_t ret; + + response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " + "'property': %s } }", id, property); + g_assert(qdict_haskey(response, "return")); + ret = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return"))); + qobject_unref(response); + return ret; +} + +static void qmp_isl_pmbus_vr_set(const char *id, + const char *property, + uint16_t value) +{ + QDict *response; + + response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " + "'property': %s, 'value': %u } }", id, property, value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big endian */ +static uint16_t isl_pmbus_vr_i2c_get16(QI2CDevice *i2cdev, uint8_t reg) +{ + uint8_t resp[2]; + i2c_read_block(i2cdev, reg, resp, sizeof(resp)); + return (resp[1] << 8) | resp[0]; +} + +/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big endian */ +static void isl_pmbus_vr_i2c_set16(QI2CDevice *i2cdev, uint8_t reg, + uint16_t value) +{ + uint8_t data[2]; + + data[0] = value & 255; + data[1] = value >> 8; + i2c_write_block(i2cdev, reg, data, sizeof(data)); +} + +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t value, i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + value = qmp_isl_pmbus_vr_get(TEST_ID, "vout[0]"); + g_assert_cmpuint(value, ==, ISL_READ_VOUT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IOUT); + g_assert_cmpuint(i2c_value, ==, ISL_READ_IOUT_DEFAULT); + + value = qmp_isl_pmbus_vr_get(TEST_ID, "pout[0]"); + g_assert_cmpuint(value, ==, ISL_READ_POUT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_VIN); + g_assert_cmpuint(i2c_value, ==, ISL_READ_VIN_DEFAULT); + + value = qmp_isl_pmbus_vr_get(TEST_ID, "iin[0]"); + g_assert_cmpuint(value, ==, ISL_READ_IIN_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_PIN); + g_assert_cmpuint(i2c_value, ==, ISL_READ_PIN_DEFAULT); + + value = qmp_isl_pmbus_vr_get(TEST_ID, "temp1[0]"); + g_assert_cmpuint(value, ==, ISL_READ_TEMP_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_2); + g_assert_cmpuint(i2c_value, ==, ISL_READ_TEMP_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_CAPABILITY); + g_assert_cmphex(i2c_value, ==, ISL_CAPABILITY_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmphex(i2c_value, ==, ISL_OPERATION_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG); + g_assert_cmphex(i2c_value, ==, ISL_ON_OFF_CONFIG_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MODE_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_COMMAND); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_COMMAND_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MAX); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MAX_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_HIGH); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MARGIN_HIGH_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_LOW); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MARGIN_LOW_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_TRANSITION_RATE); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_TRANSITION_RATE_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_OV_FAULT_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_OT_FAULT_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_OT_WARN_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_VIN_OV_WARN_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_VIN_UV_WARN_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_IIN_OC_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_IIN_OC_FAULT_LIMIT_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_REVISION); + g_assert_cmphex(i2c_value, ==, ISL_REVISION_DEFAULT); +} + +static void raa228000_test_defaults(void *obj, void *data, + QGuestAllocator *alloc) +{ + uint16_t value, i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + value = qmp_isl_pmbus_vr_get(TEST_ID, "vout[0]"); + g_assert_cmpuint(value, ==, 0); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IOUT); + g_assert_cmpuint(i2c_value, ==, 0); + + value = qmp_isl_pmbus_vr_get(TEST_ID, "pout[0]"); + g_assert_cmpuint(value, ==, 0); + + i2c_value = i2c_get8(i2cdev, PMBUS_CAPABILITY); + g_assert_cmphex(i2c_value, ==, ISL_CAPABILITY_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmphex(i2c_value, ==, ISL_OPERATION_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG); + g_assert_cmphex(i2c_value, ==, ISL_ON_OFF_CONFIG_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MODE_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_COMMAND); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_COMMAND_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MAX); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MAX_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_HIGH); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MARGIN_HIGH_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_LOW); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_MARGIN_LOW_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_TRANSITION_RATE); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_TRANSITION_RATE_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_VOUT_OV_FAULT_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_OT_FAULT_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_OT_WARN_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_VIN_OV_WARN_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_VIN_UV_WARN_LIMIT_DEFAULT); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_IIN_OC_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, ISL_IIN_OC_FAULT_LIMIT_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_REVISION); + g_assert_cmphex(i2c_value, ==, ISL_REVISION_DEFAULT); +} + +/* test qmp access */ +static void test_tx_rx(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value, value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + qmp_isl_pmbus_vr_set(TEST_ID, "vin[0]", 200); + value = qmp_isl_pmbus_vr_get(TEST_ID, "vin[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_VIN); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "vout[0]", 2500); + value = qmp_isl_pmbus_vr_get(TEST_ID, "vout[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_VOUT); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "iin[0]", 300); + value = qmp_isl_pmbus_vr_get(TEST_ID, "iin[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IIN); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "iout[0]", 310); + value = qmp_isl_pmbus_vr_get(TEST_ID, "iout[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IOUT); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "pin[0]", 100); + value = qmp_isl_pmbus_vr_get(TEST_ID, "pin[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_PIN); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "pout[0]", 95); + value = qmp_isl_pmbus_vr_get(TEST_ID, "pout[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_POUT); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "temp1[0]", 26); + value = qmp_isl_pmbus_vr_get(TEST_ID, "temp1[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "temp2[0]", 27); + value = qmp_isl_pmbus_vr_get(TEST_ID, "temp2[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_2); + g_assert_cmpuint(value, ==, i2c_value); + + qmp_isl_pmbus_vr_set(TEST_ID, "temp3[0]", 28); + value = qmp_isl_pmbus_vr_get(TEST_ID, "temp3[0]"); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_3); + g_assert_cmpuint(value, ==, i2c_value); + +} + +/* test r/w registers */ +static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_COMMAND, 0x1234); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_COMMAND); + g_assert_cmphex(i2c_value, ==, 0x1234); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_TRIM, 0x4567); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_TRIM); + g_assert_cmphex(i2c_value, ==, 0x4567); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_MAX, 0x9876); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MAX); + g_assert_cmphex(i2c_value, ==, 0x9876); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_MARGIN_HIGH, 0xABCD); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_HIGH); + g_assert_cmphex(i2c_value, ==, 0xABCD); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_MARGIN_LOW, 0xA1B2); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_LOW); + g_assert_cmphex(i2c_value, ==, 0xA1B2); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_TRANSITION_RATE, 0xDEF1); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_TRANSITION_RATE); + g_assert_cmphex(i2c_value, ==, 0xDEF1); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_DROOP, 0x5678); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_DROOP); + g_assert_cmphex(i2c_value, ==, 0x5678); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_MIN, 0x1234); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_MIN); + g_assert_cmphex(i2c_value, ==, 0x1234); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT, 0x2345); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, 0x2345); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT, 0xFA12); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xFA12); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_OT_FAULT_LIMIT, 0xF077); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xF077); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_OT_WARN_LIMIT, 0x7137); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0x7137); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VIN_OV_FAULT_LIMIT, 0x3456); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VIN_OV_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, 0x3456); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VIN_UV_FAULT_LIMIT, 0xBADA); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VIN_UV_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xBADA); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_IIN_OC_FAULT_LIMIT, 0xB1B0); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_IIN_OC_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xB1B0); + + i2c_set8(i2cdev, PMBUS_OPERATION, 0xA); + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmphex(i2c_value, ==, 0xA); + + i2c_set8(i2cdev, PMBUS_ON_OFF_CONFIG, 0x42); + i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG); + g_assert_cmphex(i2c_value, ==, 0x42); +} + +/* test that devices with multiple pages can switch between them */ +static void test_pages_rw(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_set8(i2cdev, PMBUS_PAGE, 1); + i2c_value = i2c_get8(i2cdev, PMBUS_PAGE); + g_assert_cmphex(i2c_value, ==, 1); + + i2c_set8(i2cdev, PMBUS_PAGE, 0); + i2c_value = i2c_get8(i2cdev, PMBUS_PAGE); + g_assert_cmphex(i2c_value, ==, 0); +} + +/* test read-only registers */ +static void test_ro_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_init_value, i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_VIN); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_VIN, 0xBEEF); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_VIN); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IIN); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_IIN, 0xB00F); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IIN); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_VOUT); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_VOUT, 0x1234); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_VOUT); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IOUT); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_IOUT, 0x6547); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IOUT); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_TEMPERATURE_1, 0x1597); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_2); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_TEMPERATURE_2, 0x1897); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_2); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_3); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_TEMPERATURE_3, 0x1007); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_3); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_PIN); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_PIN, 0xDEAD); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_PIN); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_POUT); + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_READ_POUT, 0xD00D); + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_POUT); + g_assert_cmphex(i2c_init_value, ==, i2c_value); +} + +/* test voltage fault handling */ +static void test_voltage_faults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + uint8_t i2c_byte; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT, 5000); + qmp_isl_pmbus_vr_set(TEST_ID, "vout[0]", 5100); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_STATUS_WORD); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT); + g_assert_true((i2c_value & PB_STATUS_VOUT) != 0); + g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_WARN) != 0); + + qmp_isl_pmbus_vr_set(TEST_ID, "vout[0]", 4500); + i2c_set8(i2cdev, PMBUS_CLEAR_FAULTS, 0); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT); + g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_WARN) == 0); + + isl_pmbus_vr_i2c_set16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT, 4600); + + i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_STATUS_WORD); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT); + g_assert_true((i2c_value & PB_STATUS_VOUT) != 0); + g_assert_true((i2c_byte & PB_STATUS_VOUT_UV_WARN) != 0); + +} + +static void isl_pmbus_vr_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" TEST_ID ",address=0x43" + }; + add_qi2c_address(&opts, &(QI2CAddress) { TEST_ADDR }); + + qos_node_create_driver("isl69260", i2c_device_create); + qos_node_consumes("isl69260", "i2c-bus", &opts); + + qos_add_test("test_defaults", "isl69260", test_defaults, NULL); + qos_add_test("test_tx_rx", "isl69260", test_tx_rx, NULL); + qos_add_test("test_rw_regs", "isl69260", test_rw_regs, NULL); + qos_add_test("test_pages_rw", "isl69260", test_pages_rw, NULL); + qos_add_test("test_ro_regs", "isl69260", test_ro_regs, NULL); + qos_add_test("test_ov_faults", "isl69260", test_voltage_faults, NULL); + + qos_node_create_driver("raa229004", i2c_device_create); + qos_node_consumes("raa229004", "i2c-bus", &opts); + + qos_add_test("test_tx_rx", "raa229004", test_tx_rx, NULL); + qos_add_test("test_rw_regs", "raa229004", test_rw_regs, NULL); + qos_add_test("test_pages_rw", "raa229004", test_pages_rw, NULL); + qos_add_test("test_ov_faults", "raa229004", test_voltage_faults, NULL); + + qos_node_create_driver("raa228000", i2c_device_create); + qos_node_consumes("raa228000", "i2c-bus", &opts); + + qos_add_test("test_defaults", "raa228000", raa228000_test_defaults, NULL); + qos_add_test("test_tx_rx", "raa228000", test_tx_rx, NULL); + qos_add_test("test_rw_regs", "raa228000", test_rw_regs, NULL); + qos_add_test("test_ov_faults", "raa228000", test_voltage_faults, NULL); +} +libqos_init(isl_pmbus_vr_register_nodes); diff --git a/tests/qtest/ivshmem-test.c b/tests/qtest/ivshmem-test.c index 4e8af42a9d..e23a97fa8e 100644 --- a/tests/qtest/ivshmem-test.c +++ b/tests/qtest/ivshmem-test.c @@ -13,8 +13,7 @@ #include "contrib/ivshmem-server/ivshmem-server.h" #include "libqos/libqos-pc.h" #include "libqos/libqos-spapr.h" -#include "libqos/libqtest.h" -#include "qemu-common.h" +#include "libqtest.h" #define TMPSHMSIZE (1 << 20) static char *tmpshm; @@ -305,6 +304,7 @@ static void setup_vm_with_server(IVState *s, int nvectors) static void test_ivshmem_server(void) { + g_autoptr(GError) err = NULL; IVState state1, state2, *s1, *s2; ServerThread thread; IvshmemServer server; @@ -321,8 +321,8 @@ static void test_ivshmem_server(void) g_assert_cmpint(ret, ==, 0); thread.server = &server; - ret = pipe(thread.pipe); - g_assert_cmpint(ret, ==, 0); + g_unix_open_pipe(thread.pipe, FD_CLOEXEC, &err); + g_assert_no_error(err); thread.thread = g_thread_new("ivshmem-server", server_thread, &thread); g_assert(thread.thread != NULL); diff --git a/tests/qtest/libqmp.c b/tests/qtest/libqmp.c new file mode 100644 index 0000000000..0358b8313d --- /dev/null +++ b/tests/qtest/libqmp.c @@ -0,0 +1,233 @@ +/* + * QTest + * + * Copyright IBM, Corp. 2012 + * Copyright Red Hat, Inc. 2012 + * Copyright SUSE LINUX Products GmbH 2013 + * + * Authors: + * Anthony Liguori + * Paolo Bonzini + * Andreas Färber + * + * 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 "libqmp.h" + +#include "qapi/error.h" +#include "qapi/qmp/json-parser.h" +#include "qapi/qmp/qjson.h" + +#define SOCKET_MAX_FDS 16 + +typedef struct { + JSONMessageParser parser; + QDict *response; +} QMPResponseParser; + +static void socket_send(int fd, const char *buf, size_t size) +{ + size_t res = qemu_write_full(fd, buf, size); + + assert(res == size); +} + +static void qmp_response(void *opaque, QObject *obj, Error *err) +{ + QMPResponseParser *qmp = opaque; + + assert(!obj != !err); + + if (err) { + error_prepend(&err, "QMP JSON response parsing failed: "); + error_report_err(err); + abort(); + } + + g_assert(!qmp->response); + qmp->response = qobject_to(QDict, obj); + g_assert(qmp->response); +} + +QDict *qmp_fd_receive(int fd) +{ + QMPResponseParser qmp; + bool log = getenv("QTEST_LOG") != NULL; + + qmp.response = NULL; + json_message_parser_init(&qmp.parser, qmp_response, &qmp, NULL); + while (!qmp.response) { + ssize_t len; + char c; + + len = read(fd, &c, 1); + if (len == -1 && errno == EINTR) { + continue; + } + + if (len == -1 || len == 0) { + fprintf(stderr, "Broken pipe\n"); + abort(); + } + + if (log) { + g_assert(write(2, &c, 1) == 1); + } + json_message_parser_feed(&qmp.parser, &c, 1); + } + if (log) { + g_assert(write(2, "\n", 1) == 1); + } + json_message_parser_destroy(&qmp.parser); + + return qmp.response; +} + +/* Sends a message and file descriptors to the socket. + * It's needed for qmp-commands like getfd/add-fd */ +static void socket_send_fds(int socket_fd, int *fds, size_t fds_num, + const char *buf, size_t buf_size) +{ + ssize_t ret; + struct msghdr msg = { 0 }; + char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)] = { 0 }; + size_t fdsize = sizeof(int) * fds_num; + struct cmsghdr *cmsg; + struct iovec iov = { .iov_base = (char *)buf, .iov_len = buf_size }; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + if (fds && fds_num > 0) { + g_assert_cmpuint(fds_num, <, SOCKET_MAX_FDS); + + msg.msg_control = control; + msg.msg_controllen = CMSG_SPACE(fdsize); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(fdsize); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), fds, fdsize); + } + + do { + ret = sendmsg(socket_fd, &msg, 0); + } while (ret < 0 && errno == EINTR); + g_assert_cmpint(ret, >, 0); +} + +/** + * Allow users to send a message without waiting for the reply, + * in the case that they choose to discard all replies up until + * a particular EVENT is received. + */ +void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, + const char *fmt, va_list ap) +{ + QObject *qobj; + + /* Going through qobject ensures we escape strings properly */ + qobj = qobject_from_vjsonf_nofail(fmt, ap); + + /* No need to send anything for an empty QObject. */ + if (qobj) { + int log = getenv("QTEST_LOG") != NULL; + GString *str = qobject_to_json(qobj); + + /* + * BUG: QMP doesn't react to input until it sees a newline, an + * object, or an array. Work-around: give it a newline. + */ + g_string_append_c(str, '\n'); + + if (log) { + fprintf(stderr, "%s", str->str); + } + /* Send QMP request */ + if (fds && fds_num > 0) { + socket_send_fds(fd, fds, fds_num, str->str, str->len); + } else { + socket_send(fd, str->str, str->len); + } + + g_string_free(str, true); + qobject_unref(qobj); + } +} + +void qmp_fd_vsend(int fd, const char *fmt, va_list ap) +{ + qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); +} + + +QDict *qmp_fdv(int fd, const char *fmt, va_list ap) +{ + qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); + + return qmp_fd_receive(fd); +} + +QDict *qmp_fd(int fd, const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qmp_fdv(fd, fmt, ap); + va_end(ap); + return response; +} + +void qmp_fd_send(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend(fd, fmt, ap); + va_end(ap); +} + +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) +{ + bool log = getenv("QTEST_LOG") != NULL; + char *str = g_strdup_vprintf(fmt, ap); + + if (log) { + fprintf(stderr, "%s", str); + } + socket_send(fd, str, strlen(str)); + g_free(str); +} + +void qmp_fd_send_raw(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend_raw(fd, fmt, ap); + va_end(ap); +} + +bool qmp_rsp_is_err(QDict *rsp) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + qobject_unref(rsp); + return !!error; +} + +void qmp_expect_error_and_unref(QDict *rsp, const char *class) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + + g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, class); + g_assert_nonnull(qdict_get_try_str(error, "desc")); + g_assert(!qdict_haskey(rsp, "return")); + + qobject_unref(rsp); +} diff --git a/tests/qtest/libqmp.h b/tests/qtest/libqmp.h new file mode 100644 index 0000000000..94aa97328a --- /dev/null +++ b/tests/qtest/libqmp.h @@ -0,0 +1,50 @@ +/* + * libqmp test unit + * + * Copyright IBM, Corp. 2012 + * Copyright Red Hat, Inc. 2012 + * Copyright SUSE LINUX Products GmbH 2013 + * + * Authors: + * Anthony Liguori + * Paolo Bonzini + * Andreas Färber + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#ifndef LIBQMP_H_ +#define LIBQMP_H_ + +#include "qapi/qmp/qdict.h" + +QDict *qmp_fd_receive(int fd); +void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, + const char *fmt, va_list ap) G_GNUC_PRINTF(4, 0); +void qmp_fd_vsend(int fd, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); +void qmp_fd_send(int fd, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +void qmp_fd_send_raw(int fd, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); +QDict *qmp_fdv(int fd, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); +QDict *qmp_fd(int fd, const char *fmt, ...) G_GNUC_PRINTF(2, 3); + +/** + * qmp_rsp_is_err: + * @rsp: QMP response to check for error + * + * Test @rsp for error and discard @rsp. + * Returns 'true' if there is error in @rsp and 'false' otherwise. + */ +bool qmp_rsp_is_err(QDict *rsp); + +/** + * qmp_expect_error_and_unref: + * @rsp: QMP response to check for error + * @class: an error class + * + * Assert the response has the given error class and discard @rsp. + */ +void qmp_expect_error_and_unref(QDict *rsp, const char *class); + +#endif /* LIBQMP_H_ */ diff --git a/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c b/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c index 79631cc7a9..c8a3ea11eb 100644 --- a/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c +++ b/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "malloc.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index eaa2096512..f53f12aa99 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -24,11 +24,10 @@ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "ahci.h" #include "pci-pc.h" -#include "qemu-common.h" #include "qemu/host-utils.h" #include "hw/pci/pci_ids.h" diff --git a/tests/qtest/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c index 6692adfa4f..54d0c95330 100644 --- a/tests/qtest/libqos/arm-imx25-pdk-machine.c +++ b/tests/qtest/libqos/arm-imx25-pdk-machine.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "malloc.h" #include "qgraph.h" #include "i2c.h" diff --git a/tests/qtest/libqos/arm-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c index ff2049c3a7..ecd46b1daf 100644 --- a/tests/qtest/libqos/arm-n800-machine.c +++ b/tests/qtest/libqos/arm-n800-machine.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "malloc.h" #include "qgraph.h" #include "i2c.h" diff --git a/tests/qtest/libqos/arm-raspi2-machine.c b/tests/qtest/libqos/arm-raspi2-machine.c index 09ca863c10..0a2943440b 100644 --- a/tests/qtest/libqos/arm-raspi2-machine.c +++ b/tests/qtest/libqos/arm-raspi2-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "malloc.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/arm-sabrelite-machine.c b/tests/qtest/libqos/arm-sabrelite-machine.c index 72425f0ad4..ec19a01660 100644 --- a/tests/qtest/libqos/arm-sabrelite-machine.c +++ b/tests/qtest/libqos/arm-sabrelite-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "malloc.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/arm-smdkc210-machine.c b/tests/qtest/libqos/arm-smdkc210-machine.c index 321b8826d4..4bff249ee8 100644 --- a/tests/qtest/libqos/arm-smdkc210-machine.c +++ b/tests/qtest/libqos/arm-smdkc210-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "malloc.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/arm-virt-machine.c b/tests/qtest/libqos/arm-virt-machine.c index e0f5932284..2e0beaefb8 100644 --- a/tests/qtest/libqos/arm-virt-machine.c +++ b/tests/qtest/libqos/arm-virt-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "malloc.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c b/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c index 56e53c745b..3be80020a6 100644 --- a/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c +++ b/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "malloc.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/e1000e.c b/tests/qtest/libqos/e1000e.c index a451f6168f..f87e0e84b2 100644 --- a/tests/qtest/libqos/e1000e.c +++ b/tests/qtest/libqos/e1000e.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "pci-pc.h" #include "qemu/sockets.h" #include "qemu/iov.h" diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c index 6b8e1babe5..89f053ccac 100644 --- a/tests/qtest/libqos/fw_cfg.c +++ b/tests/qtest/libqos/fw_cfg.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "fw_cfg.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/bswap.h" #include "hw/nvram/fw_cfg.h" diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h index c6a7cf8cf0..b0456a15df 100644 --- a/tests/qtest/libqos/fw_cfg.h +++ b/tests/qtest/libqos/fw_cfg.h @@ -13,7 +13,7 @@ #ifndef LIBQOS_FW_CFG_H #define LIBQOS_FW_CFG_H -#include "libqtest.h" +#include "../libqtest.h" typedef struct QFWCFG QFWCFG; diff --git a/tests/qtest/libqos/i2c-imx.c b/tests/qtest/libqos/i2c-imx.c index 8f9a7e3831..710cb926d6 100644 --- a/tests/qtest/libqos/i2c-imx.c +++ b/tests/qtest/libqos/i2c-imx.c @@ -21,7 +21,7 @@ #include "i2c.h" -#include "libqtest.h" +#include "../libqtest.h" #include "hw/i2c/imx_i2c.h" diff --git a/tests/qtest/libqos/i2c-omap.c b/tests/qtest/libqos/i2c-omap.c index eb4e453485..6f98f54820 100644 --- a/tests/qtest/libqos/i2c-omap.c +++ b/tests/qtest/libqos/i2c-omap.c @@ -11,7 +11,7 @@ #include "qemu/bswap.h" -#include "libqtest.h" +#include "../libqtest.h" enum OMAPI2CRegisters { OMAP_I2C_REV = 0x00, diff --git a/tests/qtest/libqos/i2c.c b/tests/qtest/libqos/i2c.c index ade1bdb40e..1a54c004eb 100644 --- a/tests/qtest/libqos/i2c.c +++ b/tests/qtest/libqos/i2c.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" #include "i2c.h" -#include "libqtest.h" +#include "../libqtest.h" void qi2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len) { diff --git a/tests/qtest/libqos/i2c.h b/tests/qtest/libqos/i2c.h index 1341bac1c5..d032240922 100644 --- a/tests/qtest/libqos/i2c.h +++ b/tests/qtest/libqos/i2c.h @@ -9,7 +9,7 @@ #ifndef LIBQOS_I2C_H #define LIBQOS_I2C_H -#include "libqtest.h" +#include "../libqtest.h" #include "qgraph.h" typedef struct I2CAdapter I2CAdapter; diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c index 2251e864ef..85c7641add 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include -#include "libqtest.h" +#include "../libqtest.h" #include "libqos.h" #include "pci.h" #include "qapi/qmp/qdict.h" diff --git a/tests/qtest/libqos/libqos.h b/tests/qtest/libqos/libqos.h index e0b2bfe7ca..ba7df448ca 100644 --- a/tests/qtest/libqos/libqos.h +++ b/tests/qtest/libqos/libqos.h @@ -1,7 +1,7 @@ #ifndef LIBQOS_H #define LIBQOS_H -#include "libqtest.h" +#include "../libqtest.h" #include "pci.h" #include "malloc.h" diff --git a/tests/qtest/libqos/malloc-pc.c b/tests/qtest/libqos/malloc-pc.c index f1e3b392a5..bbd1b4827e 100644 --- a/tests/qtest/libqos/malloc-pc.c +++ b/tests/qtest/libqos/malloc-pc.c @@ -16,8 +16,6 @@ #include "standard-headers/linux/qemu_fw_cfg.h" -#include "qemu-common.h" - #define ALLOC_PAGE_SIZE (4096) void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags) diff --git a/tests/qtest/libqos/malloc-spapr.c b/tests/qtest/libqos/malloc-spapr.c index 05b306c191..d90ed3c51d 100644 --- a/tests/qtest/libqos/malloc-spapr.c +++ b/tests/qtest/libqos/malloc-spapr.c @@ -8,8 +8,6 @@ #include "qemu/osdep.h" #include "malloc-spapr.h" -#include "qemu-common.h" - #define SPAPR_PAGE_SIZE 4096 /* Memory must be a multiple of 256 MB, diff --git a/tests/qtest/libqos/malloc.c b/tests/qtest/libqos/malloc.c index f708b01432..f0c8f950c8 100644 --- a/tests/qtest/libqos/malloc.c +++ b/tests/qtest/libqos/malloc.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "malloc.h" -#include "qemu-common.h" #include "qemu/host-utils.h" typedef struct MemBlock { diff --git a/tests/qtest/libqos/malloc.h b/tests/qtest/libqos/malloc.h index 4d1a2e2bef..bbb8c743cc 100644 --- a/tests/qtest/libqos/malloc.h +++ b/tests/qtest/libqos/malloc.h @@ -14,7 +14,7 @@ #define LIBQOS_MALLOC_H #include "qemu/queue.h" -#include "libqtest.h" +#include "../libqtest.h" typedef enum { ALLOC_NO_FLAGS = 0x00, diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index e988d15791..9f292339f9 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -1,4 +1,7 @@ -libqos_srcs = files('../libqtest.c', +libqos_srcs = files( + '../libqtest.c', + '../libqmp.c', + 'qgraph.c', 'qos_external.c', 'pci.c', diff --git a/tests/qtest/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c index f97844289f..e9dd5a57ec 100644 --- a/tests/qtest/libqos/pci-pc.c +++ b/tests/qtest/libqos/pci-pc.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "pci-pc.h" #include "qapi/qmp/qdict.h" #include "hw/pci/pci_regs.h" diff --git a/tests/qtest/libqos/pci-spapr.c b/tests/qtest/libqos/pci-spapr.c index 262226985f..76bf9a855d 100644 --- a/tests/qtest/libqos/pci-spapr.c +++ b/tests/qtest/libqos/pci-spapr.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "pci-spapr.h" #include "rtas.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/pci.h b/tests/qtest/libqos/pci.h index becb800f9e..e705e06598 100644 --- a/tests/qtest/libqos/pci.h +++ b/tests/qtest/libqos/pci.h @@ -13,7 +13,7 @@ #ifndef LIBQOS_PCI_H #define LIBQOS_PCI_H -#include "libqtest.h" +#include "../libqtest.h" #include "qgraph.h" #define QPCI_PIO_LIMIT 0x10000 diff --git a/tests/qtest/libqos/ppc64_pseries-machine.c b/tests/qtest/libqos/ppc64_pseries-machine.c index 24ca179976..364e9c689b 100644 --- a/tests/qtest/libqos/ppc64_pseries-machine.c +++ b/tests/qtest/libqos/ppc64_pseries-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qgraph.h" #include "pci-spapr.h" #include "qemu/module.h" diff --git a/tests/qtest/libqos/qgraph.c b/tests/qtest/libqos/qgraph.c index 109ff04e1e..0a2dddfafa 100644 --- a/tests/qtest/libqos/qgraph.c +++ b/tests/qtest/libqos/qgraph.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/queue.h" #include "qgraph_internal.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c index 10ee0f75b2..b7a0b873a3 100644 --- a/tests/qtest/libqos/qos_external.c +++ b/tests/qtest/libqos/qos_external.c @@ -18,7 +18,7 @@ #include "qemu/osdep.h" #include -#include "libqtest.h" +#include "../libqtest.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qstring.h" diff --git a/tests/qtest/libqos/rtas.c b/tests/qtest/libqos/rtas.c index db29d5554d..dedbfb4cb3 100644 --- a/tests/qtest/libqos/rtas.c +++ b/tests/qtest/libqos/rtas.c @@ -4,7 +4,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "rtas.h" static void qrtas_copy_args(QTestState *qts, uint64_t target_args, diff --git a/tests/qtest/libqos/sdhci-cmd.c b/tests/qtest/libqos/sdhci-cmd.c index 2d9e518341..a6f073ac1a 100644 --- a/tests/qtest/libqos/sdhci-cmd.c +++ b/tests/qtest/libqos/sdhci-cmd.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "sdhci-cmd.h" -#include "libqtest.h" +#include "../libqtest.h" static ssize_t read_fifo(QTestState *qts, uint64_t reg, char *msg, size_t count) { diff --git a/tests/qtest/libqos/sdhci-cmd.h b/tests/qtest/libqos/sdhci-cmd.h index 64763c5a2a..9e61dd4944 100644 --- a/tests/qtest/libqos/sdhci-cmd.h +++ b/tests/qtest/libqos/sdhci-cmd.h @@ -14,7 +14,7 @@ * for more details. */ -#include "libqtest.h" +#include "../libqtest.h" /* more details at hw/sd/sdhci-internal.h */ #define SDHC_BLKSIZE 0x04 diff --git a/tests/qtest/libqos/sdhci.c b/tests/qtest/libqos/sdhci.c index 65f0d07fc5..71696980f8 100644 --- a/tests/qtest/libqos/sdhci.c +++ b/tests/qtest/libqos/sdhci.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qgraph.h" #include "pci.h" #include "qemu/module.h" diff --git a/tests/qtest/libqos/tpci200.c b/tests/qtest/libqos/tpci200.c index 1787b1f188..8b00603247 100644 --- a/tests/qtest/libqos/tpci200.c +++ b/tests/qtest/libqos/tpci200.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "qgraph.h" #include "pci.h" diff --git a/tests/qtest/libqos/usb.c b/tests/qtest/libqos/usb.c index 8b45b02984..446fdb5796 100644 --- a/tests/qtest/libqos/usb.c +++ b/tests/qtest/libqos/usb.c @@ -12,7 +12,7 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "hw/usb/uhci-regs.h" #include "usb.h" diff --git a/tests/qtest/libqos/vhost-user-blk.c b/tests/qtest/libqos/vhost-user-blk.c index 568c3426ed..2f3c9cb533 100644 --- a/tests/qtest/libqos/vhost-user-blk.c +++ b/tests/qtest/libqos/vhost-user-blk.c @@ -21,7 +21,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "standard-headers/linux/virtio_blk.h" #include "vhost-user-blk.h" diff --git a/tests/qtest/libqos/virtio-9p.c b/tests/qtest/libqos/virtio-9p.c index f51f0635cc..70aea8bf62 100644 --- a/tests/qtest/libqos/virtio-9p.c +++ b/tests/qtest/libqos/virtio-9p.c @@ -22,7 +22,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "standard-headers/linux/virtio_ids.h" #include "virtio-9p.h" diff --git a/tests/qtest/libqos/virtio-balloon.c b/tests/qtest/libqos/virtio-balloon.c index a3da5c234d..29b5d17584 100644 --- a/tests/qtest/libqos/virtio-balloon.c +++ b/tests/qtest/libqos/virtio-balloon.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "qgraph.h" #include "virtio-balloon.h" diff --git a/tests/qtest/libqos/virtio-blk.c b/tests/qtest/libqos/virtio-blk.c index 5da02591bc..ee4943f32b 100644 --- a/tests/qtest/libqos/virtio-blk.c +++ b/tests/qtest/libqos/virtio-blk.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "standard-headers/linux/virtio_blk.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/virtio-iommu.c b/tests/qtest/libqos/virtio-iommu.c index 18cba4ca36..afc7d14e9a 100644 --- a/tests/qtest/libqos/virtio-iommu.c +++ b/tests/qtest/libqos/virtio-iommu.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "qgraph.h" #include "virtio-iommu.h" diff --git a/tests/qtest/libqos/virtio-mmio.c b/tests/qtest/libqos/virtio-mmio.c index 75efda3029..a6cca8613b 100644 --- a/tests/qtest/libqos/virtio-mmio.c +++ b/tests/qtest/libqos/virtio-mmio.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "virtio.h" #include "virtio-mmio.h" diff --git a/tests/qtest/libqos/virtio-net.c b/tests/qtest/libqos/virtio-net.c index 1cae07f60d..2ac73ac0b4 100644 --- a/tests/qtest/libqos/virtio-net.c +++ b/tests/qtest/libqos/virtio-net.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "qgraph.h" #include "virtio-net.h" diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c index cd3c0f5bf3..67c2498c84 100644 --- a/tests/qtest/libqos/virtio-pci.c +++ b/tests/qtest/libqos/virtio-pci.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "virtio.h" #include "virtio-pci.h" #include "pci.h" diff --git a/tests/qtest/libqos/virtio-rng.c b/tests/qtest/libqos/virtio-rng.c index 2e09dd7c48..078e3abaa7 100644 --- a/tests/qtest/libqos/virtio-rng.c +++ b/tests/qtest/libqos/virtio-rng.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "qgraph.h" #include "virtio-rng.h" diff --git a/tests/qtest/libqos/virtio-scsi.c b/tests/qtest/libqos/virtio-scsi.c index 5644e32fc3..c4d0461420 100644 --- a/tests/qtest/libqos/virtio-scsi.c +++ b/tests/qtest/libqos/virtio-scsi.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "standard-headers/linux/virtio_ids.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/virtio-serial.c b/tests/qtest/libqos/virtio-serial.c index ee34afd95a..1d689c3e38 100644 --- a/tests/qtest/libqos/virtio-serial.c +++ b/tests/qtest/libqos/virtio-serial.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qemu/module.h" #include "qgraph.h" #include "virtio-serial.h" diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c index 6fe7bf9555..09ec09b655 100644 --- a/tests/qtest/libqos/virtio.c +++ b/tests/qtest/libqos/virtio.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "libqtest.h" +#include "../libqtest.h" #include "virtio.h" #include "standard-headers/linux/virtio_config.h" #include "standard-headers/linux/virtio_ring.h" @@ -260,6 +260,8 @@ void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq, /* vq->used->flags */ qvirtio_writew(vq->vdev, qts, vq->used, 0); + /* vq->used->idx */ + qvirtio_writew(vq->vdev, qts, vq->used + 2, 0); /* vq->used->avail_event */ qvirtio_writew(vq->vdev, qts, vq->used + 2 + sizeof(struct vring_used_elem) * vq->size, 0); diff --git a/tests/qtest/libqos/x86_64_pc-machine.c b/tests/qtest/libqos/x86_64_pc-machine.c index ad96742a92..dce0c9463a 100644 --- a/tests/qtest/libqos/x86_64_pc-machine.c +++ b/tests/qtest/libqos/x86_64_pc-machine.c @@ -17,7 +17,7 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" +#include "../libqtest.h" #include "qgraph.h" #include "pci-pc.h" #include "qemu/module.h" diff --git a/tests/qtest/libqtest-single.h b/tests/qtest/libqtest-single.h index 0d7f568678..4e7d0ae1dc 100644 --- a/tests/qtest/libqtest-single.h +++ b/tests/qtest/libqtest-single.h @@ -11,7 +11,7 @@ #ifndef LIBQTEST_SINGLE_H #define LIBQTEST_SINGLE_H -#include "libqos/libqtest.h" +#include "libqtest.h" QTestState *global_qtest __attribute__((common, weak)); @@ -52,7 +52,7 @@ static inline void qtest_end(void) * * Sends a QMP message to QEMU and returns the response. */ -GCC_FMT_ATTR(1, 2) +G_GNUC_PRINTF(1, 2) static inline QDict *qmp(const char *fmt, ...) { va_list ap; diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 41f4da4e54..228357f1ea 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -20,12 +20,10 @@ #include #include -#include "libqos/libqtest.h" -#include "qemu-common.h" +#include "libqtest.h" +#include "libqmp.h" #include "qemu/ctype.h" #include "qemu/cutils.h" -#include "qapi/error.h" -#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" @@ -33,8 +31,6 @@ #define MAX_IRQ 256 #define SOCKET_TIMEOUT 50 -#define SOCKET_MAX_FDS 16 - typedef void (*QTestSendFn)(QTestState *s, const char *buf); typedef void (*ExternalSendFn)(void *s, const char *buf); @@ -94,8 +90,8 @@ static int socket_accept(int sock) struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT, .tv_usec = 0 }; - if (qemu_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, - (void *)&timeout, sizeof(timeout))) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + (void *)&timeout, sizeof(timeout))) { fprintf(stderr, "%s failed to set SO_RCVTIMEO: %s\n", __func__, strerror(errno)); close(sock); @@ -260,6 +256,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) gchar *qmp_socket_path; gchar *command; const char *qemu_binary = qtest_qemu_binary(); + const char *trace = g_getenv("QTEST_TRACE"); + g_autofree char *tracearg = trace ? + g_strdup_printf("-trace %s ", trace) : g_strdup(""); s = g_new(QTestState, 1); @@ -282,14 +281,15 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) qtest_add_abrt_handler(kill_qemu_hook_func, s); - command = g_strdup_printf("exec %s " + command = g_strdup_printf("exec %s %s" "-qtest unix:%s " "-qtest-log %s " "-chardev socket,path=%s,id=char0 " "-mon chardev=char0,mode=control " "-display none " "%s" - " -accel qtest", qemu_binary, socket_path, + " -accel qtest", + qemu_binary, tracearg, socket_path, getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", qmp_socket_path, extra_args ?: ""); @@ -415,21 +415,9 @@ void qtest_quit(QTestState *s) static void socket_send(int fd, const char *buf, size_t size) { - size_t offset; + size_t res = qemu_write_full(fd, buf, size); - offset = 0; - while (offset < size) { - ssize_t len; - - len = write(fd, buf + offset, size - offset); - if (len == -1 && errno == EINTR) { - continue; - } - - g_assert_cmpint(len, >, 0); - - offset += len; - } + assert(res == size); } static void qtest_client_socket_send(QTestState *s, const char *buf) @@ -437,7 +425,7 @@ static void qtest_client_socket_send(QTestState *s, const char *buf) socket_send(s->fd, buf, strlen(buf)); } -static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) +static void G_GNUC_PRINTF(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) { va_list ap; @@ -449,40 +437,6 @@ static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) g_free(str); } -/* Sends a message and file descriptors to the socket. - * It's needed for qmp-commands like getfd/add-fd */ -static void socket_send_fds(int socket_fd, int *fds, size_t fds_num, - const char *buf, size_t buf_size) -{ - ssize_t ret; - struct msghdr msg = { 0 }; - char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)] = { 0 }; - size_t fdsize = sizeof(int) * fds_num; - struct cmsghdr *cmsg; - struct iovec iov = { .iov_base = (char *)buf, .iov_len = buf_size }; - - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - if (fds && fds_num > 0) { - g_assert_cmpuint(fds_num, <, SOCKET_MAX_FDS); - - msg.msg_control = control; - msg.msg_controllen = CMSG_SPACE(fdsize); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(fdsize); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), fds, fdsize); - } - - do { - ret = sendmsg(socket_fd, &msg, 0); - } while (ret < 0 && errno == EINTR); - g_assert_cmpint(ret, >, 0); -} - static GString *qtest_client_socket_recv_line(QTestState *s) { GString *line; @@ -577,59 +531,6 @@ static int qtest_query_target_endianness(QTestState *s) return big_endian; } -typedef struct { - JSONMessageParser parser; - QDict *response; -} QMPResponseParser; - -static void qmp_response(void *opaque, QObject *obj, Error *err) -{ - QMPResponseParser *qmp = opaque; - - assert(!obj != !err); - - if (err) { - error_prepend(&err, "QMP JSON response parsing failed: "); - error_report_err(err); - abort(); - } - - g_assert(!qmp->response); - qmp->response = qobject_to(QDict, obj); - g_assert(qmp->response); -} - -QDict *qmp_fd_receive(int fd) -{ - QMPResponseParser qmp; - bool log = getenv("QTEST_LOG") != NULL; - - qmp.response = NULL; - json_message_parser_init(&qmp.parser, qmp_response, &qmp, NULL); - while (!qmp.response) { - ssize_t len; - char c; - - len = read(fd, &c, 1); - if (len == -1 && errno == EINTR) { - continue; - } - - if (len == -1 || len == 0) { - fprintf(stderr, "Broken pipe\n"); - abort(); - } - - if (log) { - len = write(2, &c, 1); - } - json_message_parser_feed(&qmp.parser, &c, 1); - } - json_message_parser_destroy(&qmp.parser); - - return qmp.response; -} - QDict *qtest_qmp_receive(QTestState *s) { while (true) { @@ -670,50 +571,6 @@ int qtest_socket_server(const char *socket_path) return sock; } -/** - * Allow users to send a message without waiting for the reply, - * in the case that they choose to discard all replies up until - * a particular EVENT is received. - */ -void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, - const char *fmt, va_list ap) -{ - QObject *qobj; - - /* Going through qobject ensures we escape strings properly */ - qobj = qobject_from_vjsonf_nofail(fmt, ap); - - /* No need to send anything for an empty QObject. */ - if (qobj) { - int log = getenv("QTEST_LOG") != NULL; - GString *str = qobject_to_json(qobj); - - /* - * BUG: QMP doesn't react to input until it sees a newline, an - * object, or an array. Work-around: give it a newline. - */ - g_string_append_c(str, '\n'); - - if (log) { - fprintf(stderr, "%s", str->str); - } - /* Send QMP request */ - if (fds && fds_num > 0) { - socket_send_fds(fd, fds, fds_num, str->str, str->len); - } else { - socket_send(fd, str->str, str->len); - } - - g_string_free(str, true); - qobject_unref(qobj); - } -} - -void qmp_fd_vsend(int fd, const char *fmt, va_list ap) -{ - qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); -} - void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) { @@ -725,13 +582,6 @@ void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) qmp_fd_vsend_fds(s->qmp_fd, NULL, 0, fmt, ap); } -QDict *qmp_fdv(int fd, const char *fmt, va_list ap) -{ - qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); - - return qmp_fd_receive(fd); -} - QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) { @@ -749,26 +599,6 @@ QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) return qtest_qmp_receive(s); } -QDict *qmp_fd(int fd, const char *fmt, ...) -{ - va_list ap; - QDict *response; - - va_start(ap, fmt); - response = qmp_fdv(fd, fmt, ap); - va_end(ap); - return response; -} - -void qmp_fd_send(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - qmp_fd_vsend(fd, fmt, ap); - va_end(ap); -} - QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, ...) { @@ -801,27 +631,6 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...) va_end(ap); } -void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) -{ - bool log = getenv("QTEST_LOG") != NULL; - char *str = g_strdup_vprintf(fmt, ap); - - if (log) { - fprintf(stderr, "%s", str); - } - socket_send(fd, str, strlen(str)); - g_free(str); -} - -void qmp_fd_send_raw(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - qmp_fd_vsend_raw(fd, fmt, ap); - va_end(ap); -} - void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) { va_list ap; @@ -1545,24 +1354,6 @@ void qtest_qmp_device_del(QTestState *qts, const char *id) qtest_qmp_eventwait(qts, "DEVICE_DELETED"); } -bool qmp_rsp_is_err(QDict *rsp) -{ - QDict *error = qdict_get_qdict(rsp, "error"); - qobject_unref(rsp); - return !!error; -} - -void qmp_expect_error_and_unref(QDict *rsp, const char *class) -{ - QDict *error = qdict_get_qdict(rsp, "error"); - - g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, class); - g_assert_nonnull(qdict_get_try_str(error, "desc")); - g_assert(!qdict_haskey(rsp, "return")); - - qobject_unref(rsp); -} - static void qtest_client_set_tx_handler(QTestState *s, QTestSendFn send) { diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqtest.h similarity index 93% rename from tests/qtest/libqos/libqtest.h rename to tests/qtest/libqtest.h index cf38d273f5..4ab0cad326 100644 --- a/tests/qtest/libqos/libqtest.h +++ b/tests/qtest/libqtest.h @@ -19,6 +19,7 @@ #include "qapi/qmp/qobject.h" #include "qapi/qmp/qdict.h" +#include "libqmp.h" typedef struct QTestState QTestState; @@ -31,7 +32,7 @@ typedef struct QTestState QTestState; * * Returns: #QTestState instance. */ -QTestState *qtest_initf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +QTestState *qtest_initf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); /** * qtest_vinitf: @@ -43,7 +44,7 @@ QTestState *qtest_initf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); * * Returns: #QTestState instance. */ -QTestState *qtest_vinitf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); +QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); /** * qtest_init: @@ -106,7 +107,7 @@ void qtest_quit(QTestState *s); */ QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, ...) - GCC_FMT_ATTR(4, 5); + G_GNUC_PRINTF(4, 5); /** * qtest_qmp: @@ -118,7 +119,7 @@ QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, * Sends a QMP message to QEMU and returns the response. */ QDict *qtest_qmp(QTestState *s, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /** * qtest_qmp_send: @@ -130,7 +131,7 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...) * Sends a QMP message to QEMU and leaves the response in the stream. */ void qtest_qmp_send(QTestState *s, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /** * qtest_qmp_send_raw: @@ -141,7 +142,7 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...) * this is useful for negative tests. */ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /** * qtest_socket_server: @@ -165,7 +166,7 @@ int qtest_socket_server(const char *socket_path); */ QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) - GCC_FMT_ATTR(4, 0); + G_GNUC_PRINTF(4, 0); /** * qtest_vqmp: @@ -178,7 +179,7 @@ QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, * Sends a QMP message to QEMU and returns the response. */ QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); + G_GNUC_PRINTF(2, 0); /** * qtest_qmp_vsend_fds: @@ -194,7 +195,7 @@ QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) */ void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) - GCC_FMT_ATTR(4, 0); + G_GNUC_PRINTF(4, 0); /** * qtest_qmp_vsend: @@ -207,7 +208,7 @@ void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, * Sends a QMP message to QEMU and leaves the response in the stream. */ void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); + G_GNUC_PRINTF(2, 0); /** * qtest_qmp_receive_dict: @@ -269,7 +270,7 @@ QDict *qtest_qmp_event_ref(QTestState *s, const char *event); * * Returns: the command's output. The caller should g_free() it. */ -char *qtest_hmp(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +char *qtest_hmp(QTestState *s, const char *fmt, ...) G_GNUC_PRINTF(2, 3); /** * qtest_hmpv: @@ -283,7 +284,7 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); * Returns: the command's output. The caller should g_free() it. */ char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); + G_GNUC_PRINTF(2, 0); void qtest_module_load(QTestState *s, const char *prefix, const char *libname); @@ -688,17 +689,7 @@ void qtest_remove_abrt_handler(void *data); * the response. */ void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); - -QDict *qmp_fd_receive(int fd); -void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, - const char *fmt, va_list ap) GCC_FMT_ATTR(4, 0); -void qmp_fd_vsend(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); -void qmp_fd_send(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -void qmp_fd_send_raw(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); -QDict *qmp_fdv(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); -QDict *qmp_fd(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); + G_GNUC_PRINTF(2, 3); /** * qtest_cb_for_every_machine: @@ -750,7 +741,7 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, * Generic hot-plugging test via the device_add QMP command. */ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, - const char *fmt, ...) GCC_FMT_ATTR(4, 5); + const char *fmt, ...) G_GNUC_PRINTF(4, 5); /** * qtest_qmp_add_client: @@ -771,24 +762,6 @@ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd); */ void qtest_qmp_device_del(QTestState *qts, const char *id); -/** - * qmp_rsp_is_err: - * @rsp: QMP response to check for error - * - * Test @rsp for error and discard @rsp. - * Returns 'true' if there is error in @rsp and 'false' otherwise. - */ -bool qmp_rsp_is_err(QDict *rsp); - -/** - * qmp_expect_error_and_unref: - * @rsp: QMP response to check for error - * @class: an error class - * - * Assert the response has the given error class and discard @rsp. - */ -void qmp_expect_error_and_unref(QDict *rsp, const char *class); - /** * qtest_probe_child: * @s: QTestState instance to operate on. diff --git a/tests/qtest/lpc-ich9-test.c b/tests/qtest/lpc-ich9-test.c index fe0bef9980..8ac95b89f7 100644 --- a/tests/qtest/lpc-ich9-test.c +++ b/tests/qtest/lpc-ich9-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" static void test_lp1878642_pci_bus_get_irq_level_assert(void) { diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index 6db3234100..b94a1230f7 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #define RTC_SECONDS 0x9 #define RTC_MINUTES 0xa diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index 138101b46a..d0f8cd9902 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -12,9 +12,8 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" diff --git a/tests/qtest/megasas-test.c b/tests/qtest/megasas-test.c index eae70ff95f..d6796b9bd7 100644 --- a/tests/qtest/megasas-test.c +++ b/tests/qtest/megasas-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/bswap.h" #include "qemu/module.h" #include "libqos/qgraph.h" diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index f33d84d19b..32fb8cf755 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -17,13 +17,7 @@ slow_qtests = { 'test-hmp' : 120, } -qtests_generic = \ - (config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \ - (config_all_devices.has_key('CONFIG_LSI_SCSI_PCI') ? ['fuzz-lsi53c895a-test'] : []) + \ - (config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \ - (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \ - (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \ - [ +qtests_generic = [ 'cdrom-test', 'device-introspect-test', 'machine-none-test', @@ -67,6 +61,11 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_TPM_TIS_ISA') ? ['tpm-tis-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_RTL8139_PCI') ? ['rtl8139-test'] : []) + \ (config_all_devices.has_key('CONFIG_E1000E_PCI_EXPRESS') ? ['fuzz-e1000e-test'] : []) + \ + (config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \ + (config_all_devices.has_key('CONFIG_LSI_SCSI_PCI') ? ['fuzz-lsi53c895a-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \ + (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \ + (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \ (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \ (config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ (config_all_devices.has_key('CONFIG_VIRTIO_NET') and \ @@ -99,14 +98,13 @@ if dbus_display endif dbus_daemon = find_program('dbus-daemon', required: false) -if dbus_daemon.found() and config_host.has_key('GDBUS_CODEGEN') +if dbus_daemon.found() and gdbus_codegen.found() # Temporarily disabled due to Patchew failures: #qtests_i386 += ['dbus-vmstate-test'] dbus_vmstate1 = custom_target('dbus-vmstate description', output: ['dbus-vmstate1.h', 'dbus-vmstate1.c'], input: meson.project_source_root() / 'backends/dbus-vmstate1.xml', - command: [config_host['GDBUS_CODEGEN'], - '@INPUT@', + command: [gdbus_codegen, '@INPUT@', '--interface-prefix', 'org.qemu', '--generate-c-code', '@BASENAME@']).to_list() else @@ -143,24 +141,18 @@ qtests_mips = \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) -qtests_mips64 = \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + \ - (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ - (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) - -qtests_mips64el = \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + \ - (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ - (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) +qtests_mipsel = qtests_mips +qtests_mips64 = qtests_mips +qtests_mips64el = qtests_mips qtests_ppc = \ ['test-filter-mirror', 'test-filter-redirector'] + \ (slirp.found() ? ['test-netfilter'] : []) + \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ (config_all_devices.has_key('CONFIG_M48T59') ? ['m48t59-test'] : []) + \ - ['boot-order-test', 'prom-env-test', 'boot-serial-test'] \ + (config_all_devices.has_key('CONFIG_TCG') ? ['prom-env-test'] : []) + \ + (config_all_devices.has_key('CONFIG_TCG') ? ['boot-serial-test'] : []) + \ + ['boot-order-test'] qtests_ppc64 = \ qtests_ppc + \ @@ -190,13 +182,15 @@ qtests_npcm7xx = \ 'npcm7xx_gpio-test', 'npcm7xx_pwm-test', 'npcm7xx_rng-test', + 'npcm7xx_sdhci-test', 'npcm7xx_smbus-test', 'npcm7xx_timer-test', 'npcm7xx_watchdog_timer-test'] + \ (slirp.found() ? ['npcm7xx_emc-test'] : []) qtests_aspeed = \ ['aspeed_hace-test', - 'aspeed_smc-test'] + 'aspeed_smc-test', + 'aspeed_gpio-test'] qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \ @@ -244,6 +238,7 @@ qos_test_ss.add( 'es1370-test.c', 'ipoctal232-test.c', 'lsm303dlhc-mag-test.c', + 'isl_pmbus_vr-test.c', 'max34451-test.c', 'megasas-test.c', 'ne2000-test.c', diff --git a/tests/qtest/microbit-test.c b/tests/qtest/microbit-test.c index 2b255579df..04e199ec33 100644 --- a/tests/qtest/microbit-test.c +++ b/tests/qtest/microbit-test.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "exec/hwaddr.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "hw/arm/nrf51.h" #include "hw/char/nrf51_uart.h" diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index d63bba9630..c7872e8442 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -12,17 +12,17 @@ #ifndef MIGRATION_HELPERS_H_ #define MIGRATION_HELPERS_H_ -#include "libqos/libqtest.h" +#include "libqtest.h" extern bool got_stop; -GCC_FMT_ATTR(3, 4) +G_GNUC_PRINTF(3, 4) QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); -GCC_FMT_ATTR(2, 3) +G_GNUC_PRINTF(2, 3) QDict *wait_command(QTestState *who, const char *command, ...); -GCC_FMT_ATTR(3, 4) +G_GNUC_PRINTF(3, 4) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); QDict *migrate_query(QTestState *who); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 7b42f6fd90..cba6023eb5 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qemu/module.h" @@ -474,26 +474,10 @@ typedef struct { bool only_target; /* Use dirty ring if true; dirty logging otherwise */ bool use_dirty_ring; - char *opts_source; - char *opts_target; + const char *opts_source; + const char *opts_target; } MigrateStart; -static MigrateStart *migrate_start_new(void) -{ - MigrateStart *args = g_new0(MigrateStart, 1); - - args->opts_source = g_strdup(""); - args->opts_target = g_strdup(""); - return args; -} - -static void migrate_start_destroy(MigrateStart *args) -{ - g_free(args->opts_source); - g_free(args->opts_target); - g_free(args); -} - static int test_migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { @@ -508,13 +492,11 @@ static int test_migrate_start(QTestState **from, QTestState **to, const char *arch = qtest_get_arch(); const char *machine_opts = NULL; const char *memory_size; - int ret = 0; if (args->use_shmem) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { g_test_skip("/dev/shm is not supported"); - ret = -1; - goto out; + return -1; } } @@ -590,7 +572,8 @@ static int test_migrate_start(QTestState **from, QTestState **to, machine_opts ? " -machine " : "", machine_opts ? machine_opts : "", memory_size, tmpfs, - arch_source, shmem_opts, args->opts_source, + arch_source, shmem_opts, + args->opts_source ? args->opts_source : "", ignore_stderr); if (!args->only_target) { *from = qtest_init(cmd_source); @@ -608,7 +591,8 @@ static int test_migrate_start(QTestState **from, QTestState **to, machine_opts ? machine_opts : "", memory_size, tmpfs, uri, arch_target, shmem_opts, - args->opts_target, ignore_stderr); + args->opts_target ? args->opts_target : "", + ignore_stderr); *to = qtest_init(cmd_target); /* @@ -619,9 +603,7 @@ static int test_migrate_start(QTestState **from, QTestState **to, unlink(shmem_path); } -out: - migrate_start_destroy(args); - return ret; + return 0; } static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) @@ -709,10 +691,10 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to) static void test_postcopy(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = {}; QTestState *from, *to; - if (migrate_postcopy_prepare(&from, &to, args)) { + if (migrate_postcopy_prepare(&from, &to, &args)) { return; } migrate_postcopy_start(from, to); @@ -721,13 +703,13 @@ static void test_postcopy(void) static void test_postcopy_recovery(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = { + .hide_stderr = true, + }; QTestState *from, *to; g_autofree char *uri = NULL; - args->hide_stderr = true; - - if (migrate_postcopy_prepare(&from, &to, args)) { + if (migrate_postcopy_prepare(&from, &to, &args)) { return; } @@ -783,12 +765,12 @@ static void test_postcopy_recovery(void) static void test_baddest(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = { + .hide_stderr = true + }; QTestState *from, *to; - args->hide_stderr = true; - - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) { + if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } migrate_qmp(from, "tcp:127.0.0.1:0", "{}"); @@ -796,19 +778,86 @@ static void test_baddest(void) test_migrate_end(from, to, false); } -static void test_precopy_unix_common(bool dirty_ring) +/* + * A hook that runs after the src and dst QEMUs have been + * created, but before the migration is started. This can + * be used to set migration parameters and capabilities. + * + * Returns: NULL, or a pointer to opaque state to be + * later passed to the TestMigrateFinishHook + */ +typedef void * (*TestMigrateStartHook)(QTestState *from, + QTestState *to); + +/* + * A hook that runs after the migration has finished, + * regardless of whether it succeeded or failed, but + * before QEMU has terminated (unless it self-terminated + * due to migration error) + * + * @opaque is a pointer to state previously returned + * by the TestMigrateStartHook if any, or NULL. + */ +typedef void (*TestMigrateFinishHook)(QTestState *from, + QTestState *to, + void *opaque); + +typedef struct { + /* Optional: fine tune start parameters */ + MigrateStart start; + + /* Required: the URI for the dst QEMU to listen on */ + const char *listen_uri; + + /* + * Optional: the URI for the src QEMU to connect to + * If NULL, then it will query the dst QEMU for its actual + * listening address and use that as the connect address. + * This allows for dynamically picking a free TCP port. + */ + const char *connect_uri; + + /* Optional: callback to run at start to set migration parameters */ + TestMigrateStartHook start_hook; + /* Optional: callback to run at finish to cleanup */ + TestMigrateFinishHook finish_hook; + + /* + * Optional: normally we expect the migration process to complete. + * + * There can be a variety of reasons and stages in which failure + * can happen during tests. + * + * If a failure is expected to happen at time of establishing + * the connection, then MIG_TEST_FAIL will indicate that the dst + * QEMU is expected to stay running and accept future migration + * connections. + * + * If a failure is expected to happen while processing the + * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate + * that the dst QEMU is expected to quit with non-zero exit status + */ + enum { + /* This test should succeed, the default */ + MIG_TEST_SUCCEED = 0, + /* This test should fail, dest qemu should keep alive */ + MIG_TEST_FAIL, + /* This test should fail, dest qemu should fail with abnormal status */ + MIG_TEST_FAIL_DEST_QUIT_ERR, + } result; +} MigrateCommon; + +static void test_precopy_common(MigrateCommon *args) { - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart *args = migrate_start_new(); QTestState *from, *to; + void *data_hook = NULL; - args->use_dirty_ring = dirty_ring; - - if (test_migrate_start(&from, &to, uri, args)) { + if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } - /* We want to pick a speed slow enough that the test completes + /* + * We want to pick a speed slow enough that the test completes * quickly, but that it doesn't complete precopy even on a slow * machine, so also set the downtime. */ @@ -817,37 +866,74 @@ static void test_precopy_unix_common(bool dirty_ring) /* 1GB/s */ migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, uri, "{}"); - - wait_for_migration_pass(from); - - migrate_set_parameter_int(from, "downtime-limit", CONVERGE_DOWNTIME); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); + if (!args->connect_uri) { + g_autofree char *local_connect_uri = + migrate_get_socket_address(to, "socket-address"); + migrate_qmp(from, local_connect_uri, "{}"); + } else { + migrate_qmp(from, args->connect_uri, "{}"); } - qtest_qmp_eventwait(to, "RESUME"); - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); + if (args->result != MIG_TEST_SUCCEED) { + bool allow_active = args->result == MIG_TEST_FAIL; + wait_for_migration_fail(from, allow_active); - test_migrate_end(from, to, true); + if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { + qtest_set_expected_status(to, 1); + } + } else { + wait_for_migration_pass(from); + + migrate_set_parameter_int(from, "downtime-limit", CONVERGE_DOWNTIME); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + } + + if (args->finish_hook) { + args->finish_hook(from, to, data_hook); + } + + test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); } static void test_precopy_unix(void) { - /* Using default dirty logging */ - test_precopy_unix_common(false); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + }; + + test_precopy_common(&args); } static void test_precopy_unix_dirty_ring(void) { - /* Using dirty ring tracking */ - test_precopy_unix_common(true); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + test_precopy_common(&args); } #if 0 @@ -889,10 +975,10 @@ static void test_ignore_shared(void) static void test_xbzrle(const char *uri) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = {}; QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, args)) { + if (test_migrate_start(&from, &to, uri, &args)) { return; } @@ -942,71 +1028,19 @@ static void test_xbzrle_unix(void) static void test_precopy_tcp(void) { - MigrateStart *args = migrate_start_new(); - g_autofree char *uri = NULL; - QTestState *from, *to; + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + }; - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) { - return; - } - - /* - * We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge*/ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - uri = migrate_get_socket_address(to, "socket-address"); - - migrate_qmp(from, uri, "{}"); - - wait_for_migration_pass(from); - - migrate_set_parameter_int(from, "downtime-limit", CONVERGE_DOWNTIME); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - test_migrate_end(from, to, true); + test_precopy_common(&args); } -static void test_migrate_fd_proto(void) +static void *test_migrate_fd_start_hook(QTestState *from, + QTestState *to) { - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; + QDict *rsp; int ret; int pair[2]; - QDict *rsp; - const char *error_desc; - - if (test_migrate_start(&from, &to, "defer", args)) { - return; - } - - /* - * We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge */ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); /* Create two connected sockets for migration */ ret = socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); @@ -1031,17 +1065,15 @@ static void test_migrate_fd_proto(void) qobject_unref(rsp); close(pair[1]); - /* Start migration to the 2nd socket*/ - migrate_qmp(from, "fd:fd-mig", "{}"); + return NULL; +} - wait_for_migration_pass(from); - - migrate_set_parameter_int(from, "downtime-limit", CONVERGE_DOWNTIME); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - qtest_qmp_eventwait(to, "RESUME"); +static void test_migrate_fd_finish_hook(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *rsp; + const char *error_desc; /* Test closing fds */ /* We assume, that QEMU removes named fd from its list, @@ -1059,11 +1091,17 @@ static void test_migrate_fd_proto(void) error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); qobject_unref(rsp); +} - /* Complete migration */ - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - test_migrate_end(from, to, true); +static void test_migrate_fd_proto(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = test_migrate_fd_start_hook, + .finish_hook = test_migrate_fd_finish_hook + }; + test_precopy_common(&args); } static void do_test_validate_uuid(MigrateStart *args, bool should_fail) @@ -1100,51 +1138,49 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) static void test_validate_uuid(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", + }; - g_free(args->opts_source); - g_free(args->opts_target); - args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - args->opts_target = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - do_test_validate_uuid(args, false); + do_test_validate_uuid(&args, false); } static void test_validate_uuid_error(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; - g_free(args->opts_source); - g_free(args->opts_target); - args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); - args->hide_stderr = true; - do_test_validate_uuid(args, true); + do_test_validate_uuid(&args, true); } static void test_validate_uuid_src_not_set(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = { + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; - g_free(args->opts_target); - args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); - args->hide_stderr = true; - do_test_validate_uuid(args, false); + do_test_validate_uuid(&args, false); } static void test_validate_uuid_dst_not_set(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .hide_stderr = true, + }; - g_free(args->opts_source); - args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - args->hide_stderr = true; - do_test_validate_uuid(args, false); + do_test_validate_uuid(&args, false); } static void test_migrate_auto_converge(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart *args = migrate_start_new(); + MigrateStart args = {}; QTestState *from, *to; int64_t remaining, percentage; @@ -1163,7 +1199,7 @@ static void test_migrate_auto_converge(void) */ const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; - if (test_migrate_start(&from, &to, uri, args)) { + if (test_migrate_start(&from, &to, uri, &args)) { return; } @@ -1227,12 +1263,12 @@ static void test_migrate_auto_converge(void) static void test_multifd_tcp(const char *method) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = {}; QTestState *from, *to; QDict *rsp; g_autofree char *uri = NULL; - if (test_migrate_start(&from, &to, "defer", args)) { + if (test_migrate_start(&from, &to, "defer", &args)) { return; } @@ -1311,14 +1347,14 @@ static void test_multifd_tcp_zstd(void) */ static void test_multifd_tcp_cancel(void) { - MigrateStart *args = migrate_start_new(); + MigrateStart args = { + .hide_stderr = true, + }; QTestState *from, *to, *to2; QDict *rsp; g_autofree char *uri = NULL; - args->hide_stderr = true; - - if (test_migrate_start(&from, &to, "defer", args)) { + if (test_migrate_start(&from, &to, "defer", &args)) { return; } @@ -1354,10 +1390,11 @@ static void test_multifd_tcp_cancel(void) migrate_cancel(from); - args = migrate_start_new(); - args->only_target = true; + args = (MigrateStart){ + .only_target = true, + }; - if (test_migrate_start(&from, &to2, "defer", args)) { + if (test_migrate_start(&from, &to2, "defer", &args)) { return; } diff --git a/tests/qtest/modules-test.c b/tests/qtest/modules-test.c index c238b3f422..88217686e1 100644 --- a/tests/qtest/modules-test.c +++ b/tests/qtest/modules-test.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/ne2000-test.c b/tests/qtest/ne2000-test.c index 43cfc4535a..3fc0e555d5 100644 --- a/tests/qtest/ne2000-test.c +++ b/tests/qtest/ne2000-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c index 5ce8ce13b3..3fa6d9ece0 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/timer.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #define REF_HZ (25000000) diff --git a/tests/qtest/npcm7xx_emc-test.c b/tests/qtest/npcm7xx_emc-test.c index 9eec71d87c..a353fef0ca 100644 --- a/tests/qtest/npcm7xx_emc-test.c +++ b/tests/qtest/npcm7xx_emc-test.c @@ -15,7 +15,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "libqos/libqos.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" @@ -521,12 +520,12 @@ static void emc_send_verify1(QTestState *qts, const EMCModule *mod, int fd, /* Check data sent to the backend. */ recv_len = ~0; - ret = qemu_recv(fd, &recv_len, sizeof(recv_len), MSG_DONTWAIT); + ret = recv(fd, &recv_len, sizeof(recv_len), MSG_DONTWAIT); g_assert_cmpint(ret, == , sizeof(recv_len)); g_assert(wait_socket_readable(fd)); memset(buffer, 0xff, sizeof(buffer)); - ret = qemu_recv(fd, buffer, test_size, MSG_DONTWAIT); + ret = recv(fd, buffer, test_size, MSG_DONTWAIT); g_assert_cmpmem(buffer, ret, test_data, test_size); } diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c index a54fd70d27..c4a5fdcacd 100644 --- a/tests/qtest/npcm7xx_pwm-test.c +++ b/tests/qtest/npcm7xx_pwm-test.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" diff --git a/tests/qtest/npcm7xx_rng-test.c b/tests/qtest/npcm7xx_rng-test.c index 797f832e53..35b1c1f5f6 100644 --- a/tests/qtest/npcm7xx_rng-test.c +++ b/tests/qtest/npcm7xx_rng-test.c @@ -20,7 +20,7 @@ #include "libqtest-single.h" #include "qemu/bitops.h" -#include "qemu-common.h" +#include "qemu/cutils.h" #define RNG_BASE_ADDR 0xf000b000 diff --git a/tests/qtest/npcm7xx_sdhci-test.c b/tests/qtest/npcm7xx_sdhci-test.c new file mode 100644 index 0000000000..aa35a77e8d --- /dev/null +++ b/tests/qtest/npcm7xx_sdhci-test.c @@ -0,0 +1,215 @@ +/* + * QTests for NPCM7xx SD-3.0 / MMC-4.51 Host Controller + * + * Copyright (c) 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "hw/sd/npcm7xx_sdhci.h" + +#include "libqtest.h" +#include "libqtest-single.h" +#include "libqos/sdhci-cmd.h" + +#define NPCM7XX_REG_SIZE 0x100 +#define NPCM7XX_MMC_BA 0xF0842000 +#define NPCM7XX_BLK_SIZE 512 +#define NPCM7XX_TEST_IMAGE_SIZE (1 << 30) + +char *sd_path; + +static QTestState *setup_sd_card(void) +{ + QTestState *qts = qtest_initf( + "-machine kudo-bmc " + "-device sd-card,drive=drive0 " + "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off", + sd_path); + + qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL); + qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON, + SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE | + SDHC_CLOCK_INT_EN); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0, + SDHC_SELECT_DESELECT_CARD); + + return qts; +} + +static void write_sdread(QTestState *qts, const char *msg) +{ + int fd, ret; + size_t len = strlen(msg); + char *rmsg = g_malloc(len); + + /* write message to sd */ + fd = open(sd_path, O_WRONLY); + g_assert(fd >= 0); + ret = write(fd, msg, len); + close(fd); + g_assert(ret == len); + + /* read message using sdhci */ + ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len); + g_assert(ret == len); + g_assert(!memcmp(rmsg, msg, len)); + + g_free(rmsg); +} + +/* Check MMC can read values from sd */ +static void test_read_sd(void) +{ + QTestState *qts = setup_sd_card(); + + write_sdread(qts, "hello world"); + write_sdread(qts, "goodbye"); + + qtest_quit(qts); +} + +static void sdwrite_read(QTestState *qts, const char *msg) +{ + int fd, ret; + size_t len = strlen(msg); + char *rmsg = g_malloc(len); + + /* write message using sdhci */ + sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE); + + /* read message from sd */ + fd = open(sd_path, O_RDONLY); + g_assert(fd >= 0); + ret = read(fd, rmsg, len); + close(fd); + g_assert(ret == len); + + g_assert(!memcmp(rmsg, msg, len)); + + g_free(rmsg); +} + +/* Check MMC can write values to sd */ +static void test_write_sd(void) +{ + QTestState *qts = setup_sd_card(); + + sdwrite_read(qts, "hello world"); + sdwrite_read(qts, "goodbye"); + + qtest_quit(qts); +} + +/* Check SDHCI has correct default values. */ +static void test_reset(void) +{ + QTestState *qts = qtest_init("-machine kudo-bmc"); + uint64_t addr = NPCM7XX_MMC_BA; + uint64_t end_addr = addr + NPCM7XX_REG_SIZE; + uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET, + NPCM7XX_PRSTVALS_1_RESET, + 0, + NPCM7XX_PRSTVALS_3_RESET, + 0, + 0}; + int i; + uint32_t mask; + + while (addr < end_addr) { + switch (addr - NPCM7XX_MMC_BA) { + case SDHC_PRNSTS: + /* + * ignores bits 20 to 24: they are changed when reading registers + */ + mask = 0x1f00000; + g_assert_cmphex(qtest_readl(qts, addr) | mask, ==, + NPCM7XX_PRSNTS_RESET | mask); + addr += 4; + break; + case SDHC_BLKGAP: + g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET); + addr += 1; + break; + case SDHC_CAPAB: + g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET); + addr += 8; + break; + case SDHC_MAXCURR: + g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET); + addr += 8; + break; + case SDHC_HCVER: + g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET); + addr += 2; + break; + case NPCM7XX_PRSTVALS: + for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) { + g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==, + prstvals_resets[i]); + } + addr += NPCM7XX_PRSTVALS_SIZE * 2; + break; + default: + g_assert_cmphex(qtest_readb(qts, addr), ==, 0); + addr += 1; + } + } + + qtest_quit(qts); +} + +static void drive_destroy(void) +{ + unlink(sd_path); + g_free(sd_path); +} + +static void drive_create(void) +{ + int fd, ret; + GError *error = NULL; + + /* Create a temporary raw image */ + fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error); + if (fd == -1) { + fprintf(stderr, "unable to create sdhci file: %s\n", error->message); + g_error_free(error); + } + g_assert(sd_path != NULL); + + ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE); + g_assert_cmpint(ret, ==, 0); + g_message("%s", sd_path); + close(fd); +} + +int main(int argc, char **argv) +{ + int ret; + + drive_create(); + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("npcm7xx_sdhci/reset", test_reset); + qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd); + qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd); + + ret = g_test_run(); + drive_destroy(); + return ret; +} diff --git a/tests/qtest/npcm7xx_smbus-test.c b/tests/qtest/npcm7xx_smbus-test.c index 6b3038ac59..a878cdc001 100644 --- a/tests/qtest/npcm7xx_smbus-test.c +++ b/tests/qtest/npcm7xx_smbus-test.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "libqos/i2c.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "hw/sensor/tmp105_regs.h" #define NR_SMBUS_DEVICES 16 diff --git a/tests/qtest/npcm7xx_watchdog_timer-test.c b/tests/qtest/npcm7xx_watchdog_timer-test.c index 3aae5a0438..4773a673b2 100644 --- a/tests/qtest/npcm7xx_watchdog_timer-test.c +++ b/tests/qtest/npcm7xx_watchdog_timer-test.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #define WTCR_OFFSET 0x1c diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 90bf68a5b3..749429dd27 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c index f8bafb5d70..008d189b0f 100644 --- a/tests/qtest/nvme-test.c +++ b/tests/qtest/nvme-test.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/units.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" #include "include/block/nvme.h" diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c index 42a1312665..d80ed93cd3 100644 --- a/tests/qtest/pca9552-test.c +++ b/tests/qtest/pca9552-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" #include "hw/misc/pca9552_regs.h" diff --git a/tests/qtest/pci-test.c b/tests/qtest/pci-test.c index e15d4d94d1..4b2092b949 100644 --- a/tests/qtest/pci-test.c +++ b/tests/qtest/pci-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/pcnet-test.c b/tests/qtest/pcnet-test.c index 7583aeb3c3..900944fa7e 100644 --- a/tests/qtest/pcnet-test.c +++ b/tests/qtest/pcnet-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/pflash-cfi02-test.c b/tests/qtest/pflash-cfi02-test.c index 6168edc821..7fce614b64 100644 --- a/tests/qtest/pflash-cfi02-test.c +++ b/tests/qtest/pflash-cfi02-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* * To test the pflash_cfi02 device, we run QEMU with the musicpal machine with diff --git a/tests/qtest/pnv-xscom-test.c b/tests/qtest/pnv-xscom-test.c index c8d4043362..2c46d5cf6d 100644 --- a/tests/qtest/pnv-xscom-test.c +++ b/tests/qtest/pnv-xscom-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" typedef enum PnvChipType { PNV_CHIP_POWER8E, /* AKA Murano (default) */ diff --git a/tests/qtest/prom-env-test.c b/tests/qtest/prom-env-test.c index bdbb01d8e5..c2b0448e55 100644 --- a/tests/qtest/prom-env-test.c +++ b/tests/qtest/prom-env-test.c @@ -20,7 +20,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/libqos-spapr.h" #define MAGIC 0xcafec0de diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c index 2358852d35..c82c365c26 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c index 6dcad2db49..bc7b7dfc39 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" static void test_panic_nopause(void) diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c index 32bbae33c5..52f0b5c67c 100644 --- a/tests/qtest/pxe-test.c +++ b/tests/qtest/pxe-test.c @@ -14,8 +14,7 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "boot-sector.h" #include "libqos/libqos-spapr.h" diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c index b7cf144990..c922d81bc0 100644 --- a/tests/qtest/q35-test.c +++ b/tests/qtest/q35-test.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "hw/pci-host/q35.h" diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 7f103ea3fd..056b40e67f 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-introspect.h" #include "qapi/qmp/qdict.h" diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c index cd27fae3de..9a42480cef 100644 --- a/tests/qtest/qmp-test.c +++ b/tests/qtest/qmp-test.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-control.h" #include "qapi/qmp/qdict.h" diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index eb34af843b..7b871b2a31 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -9,11 +9,10 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qemu/cutils.h" -#include "libqos/libqtest.h" +#include "libqtest.h" static void test_properties(QTestState *qts, const char *path, bool recurse) { diff --git a/tests/qtest/rtas-test.c b/tests/qtest/rtas-test.c index 5f1194a6eb..50df60e5b2 100644 --- a/tests/qtest/rtas-test.c +++ b/tests/qtest/rtas-test.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/libqos-spapr.h" #include "libqos/rtas.h" diff --git a/tests/qtest/rtl8139-test.c b/tests/qtest/rtl8139-test.c index 4506049264..8fa3313cc3 100644 --- a/tests/qtest/rtl8139-test.c +++ b/tests/qtest/rtl8139-test.c @@ -11,7 +11,6 @@ #include "libqtest-single.h" #include "libqos/pci-pc.h" #include "qemu/timer.h" -#include "qemu-common.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/qtest/sdhci-test.c b/tests/qtest/sdhci-test.c index a110cfe321..6275e7626c 100644 --- a/tests/qtest/sdhci-test.c +++ b/tests/qtest/sdhci-test.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "hw/registerfields.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/pci-pc.h" #include "hw/pci/pci.h" diff --git a/tests/qtest/spapr-phb-test.c b/tests/qtest/spapr-phb-test.c index ea8d596507..093dc22f2f 100644 --- a/tests/qtest/spapr-phb-test.c +++ b/tests/qtest/spapr-phb-test.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c index 47bc7ad301..254f735370 100644 --- a/tests/qtest/tco-test.c +++ b/tests/qtest/tco-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "qapi/qmp/qdict.h" diff --git a/tests/qtest/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c index 95367d14d3..c8b0a92b53 100644 --- a/tests/qtest/test-filter-mirror.c +++ b/tests/qtest/test-filter-mirror.c @@ -9,8 +9,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" @@ -58,13 +57,13 @@ static void test_mirror(void) g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); close(send_sock[0]); - ret = qemu_recv(recv_sock[0], &len, sizeof(len), 0); + ret = recv(recv_sock[0], &len, sizeof(len), 0); g_assert_cmpint(ret, ==, sizeof(len)); len = ntohl(len); g_assert_cmpint(len, ==, sizeof(send_buf)); recv_buf = g_malloc(len); - ret = qemu_recv(recv_sock[0], recv_buf, len, 0); + ret = recv(recv_sock[0], recv_buf, len, 0); g_assert_cmpstr(recv_buf, ==, send_buf); g_free(recv_buf); diff --git a/tests/qtest/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c index 4f3f59cba8..24ca9280f8 100644 --- a/tests/qtest/test-filter-redirector.c +++ b/tests/qtest/test-filter-redirector.c @@ -51,8 +51,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" @@ -115,13 +114,13 @@ static void test_redirector_tx(void) g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); close(backend_sock[0]); - ret = qemu_recv(recv_sock, &len, sizeof(len), 0); + ret = recv(recv_sock, &len, sizeof(len), 0); g_assert_cmpint(ret, ==, sizeof(len)); len = ntohl(len); g_assert_cmpint(len, ==, sizeof(send_buf)); recv_buf = g_malloc(len); - ret = qemu_recv(recv_sock, recv_buf, len, 0); + ret = recv(recv_sock, recv_buf, len, 0); g_assert_cmpstr(recv_buf, ==, send_buf); g_free(recv_buf); @@ -182,13 +181,13 @@ static void test_redirector_rx(void) ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf)); g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); - ret = qemu_recv(backend_sock[0], &len, sizeof(len), 0); + ret = recv(backend_sock[0], &len, sizeof(len), 0); g_assert_cmpint(ret, ==, sizeof(len)); len = ntohl(len); g_assert_cmpint(len, ==, sizeof(send_buf)); recv_buf = g_malloc(len); - ret = qemu_recv(backend_sock[0], recv_buf, len, 0); + ret = recv(backend_sock[0], recv_buf, len, 0); g_assert_cmpstr(recv_buf, ==, send_buf); close(send_sock); diff --git a/tests/qtest/test-hmp.c b/tests/qtest/test-hmp.c index 413eb95d2a..f8b22abe4c 100644 --- a/tests/qtest/test-hmp.c +++ b/tests/qtest/test-hmp.c @@ -15,7 +15,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" static int verbose; diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index 39138db774..b39c9055b3 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -1,5 +1,4 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qnum.h" diff --git a/tests/qtest/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c index 1d82a48c04..55fdb5657d 100644 --- a/tests/qtest/tpm-crb-swtpm-test.c +++ b/tests/qtest/tpm-crb-swtpm-test.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "tpm-tests.h" #include "hw/acpi/tpm.h" diff --git a/tests/qtest/tpm-emu.h b/tests/qtest/tpm-emu.h index c33d99af37..712cee9b7a 100644 --- a/tests/qtest/tpm-emu.h +++ b/tests/qtest/tpm-emu.h @@ -22,7 +22,7 @@ #include "qemu/sockets.h" #include "io/channel.h" #include "sysemu/tpm.h" -#include "libqos/libqtest.h" +#include "libqtest.h" struct tpm_hdr { uint16_t tag; diff --git a/tests/qtest/tpm-tis-device-swtpm-test.c b/tests/qtest/tpm-tis-device-swtpm-test.c index f7126eff9e..7b20035142 100644 --- a/tests/qtest/tpm-tis-device-swtpm-test.c +++ b/tests/qtest/tpm-tis-device-swtpm-test.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "tpm-tests.h" #include "hw/acpi/tpm.h" diff --git a/tests/qtest/tpm-tis-swtpm-test.c b/tests/qtest/tpm-tis-swtpm-test.c index fa590e68f1..90131cb3c4 100644 --- a/tests/qtest/tpm-tis-swtpm-test.c +++ b/tests/qtest/tpm-tis-swtpm-test.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "tpm-tests.h" #include "hw/acpi/tpm.h" diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c index 3a40ff3f96..e0dc5da0af 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include "hw/acpi/tpm.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "tpm-util.h" #include "qapi/qmp/qdict.h" diff --git a/tests/qtest/tulip-test.c b/tests/qtest/tulip-test.c index da16cbfafc..2fb6c4d5a7 100644 --- a/tests/qtest/tulip-test.c +++ b/tests/qtest/tulip-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c index 62e670f39b..659b5050d8 100644 --- a/tests/qtest/vhost-user-blk-test.c +++ b/tests/qtest/vhost-user-blk-test.c @@ -37,7 +37,7 @@ typedef struct QVirtioBlkReq { uint8_t status; } QVirtioBlkReq; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN static const bool host_is_big_endian = true; #else static const bool host_is_big_endian; /* false */ diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index ee30f54796..a2cec87684 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -302,6 +302,7 @@ static int chr_can_read(void *opaque) static void chr_read(void *opaque, const uint8_t *buf, int size) { + g_autoptr(GError) err = NULL; TestServer *s = opaque; CharBackend *chr = &s->chr; VhostUserMsg msg; @@ -394,7 +395,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * The receive function forces it to be blocking, * so revert it back to non-blocking. */ - qemu_set_nonblock(fd); + g_unix_set_fd_nonblocking(fd, true, &err); + g_assert_no_error(err); break; case VHOST_USER_SET_LOG_BASE: diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index 502e5ad0c7..e28c71bd8f 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -468,12 +468,12 @@ static void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, togo -= 13 + 8 + 1 + 2 + slen, ++n) { if (!e) { - e = g_malloc(sizeof(struct V9fsDirent)); + e = g_new(struct V9fsDirent, 1); if (entries) { *entries = e; } } else { - e = e->next = g_malloc(sizeof(struct V9fsDirent)); + e = e->next = g_new(struct V9fsDirent, 1); } e->next = NULL; /* qid[13] offset[8] type[1] name[s] */ @@ -1253,7 +1253,7 @@ static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc) /* ... and is actually a directory */ g_assert((st.st_mode & S_IFMT) == S_IFDIR); - do_unlinkat(v9p, "/", "02", AT_REMOVEDIR); + do_unlinkat(v9p, "/", "02", P9_DOTL_AT_REMOVEDIR); /* directory should be gone now */ g_assert(stat(new_dir, &st) != 0); } diff --git a/tests/qtest/virtio-blk-test.c b/tests/qtest/virtio-blk-test.c index 2a23698211..f22594a1a8 100644 --- a/tests/qtest/virtio-blk-test.c +++ b/tests/qtest/virtio-blk-test.c @@ -33,7 +33,7 @@ typedef struct QVirtioBlkReq { } QVirtioBlkReq; -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN const bool host_is_big_endian = true; #else const bool host_is_big_endian; /* false */ diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c index 47e68388a0..068e7a9e6c 100644 --- a/tests/qtest/virtio-iommu-test.c +++ b/tests/qtest/virtio-iommu-test.c @@ -31,11 +31,13 @@ static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) uint64_t input_range_end = qvirtio_config_readq(dev, 16); uint32_t domain_range_start = qvirtio_config_readl(dev, 24); uint32_t domain_range_end = qvirtio_config_readl(dev, 28); + uint8_t bypass = qvirtio_config_readb(dev, 36); g_assert_cmpint(input_range_start, ==, 0); g_assert_cmphex(input_range_end, ==, UINT64_MAX); g_assert_cmpint(domain_range_start, ==, 0); g_assert_cmpint(domain_range_end, ==, UINT32_MAX); + g_assert_cmpint(bypass, ==, 1); } static int read_tail_status(struct virtio_iommu_req_tail *buffer) diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 80292eecf6..443ee56de9 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -8,7 +8,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "qapi/qmp/qdict.h" @@ -1141,6 +1141,11 @@ static void test_migrate_guest_off_abort(gconstpointer opaque) ret = migrate_status(qts); status = qdict_get_str(ret, "status"); + if (strcmp(status, "completed") == 0) { + g_test_skip("Failed to cancel the migration"); + qobject_unref(ret); + goto out; + } if (strcmp(status, "active") == 0) { qobject_unref(ret); break; @@ -1155,8 +1160,12 @@ static void test_migrate_guest_off_abort(gconstpointer opaque) while (true) { ret = migrate_status(qts); - status = qdict_get_str(ret, "status"); + if (strcmp(status, "completed") == 0) { + g_test_skip("Failed to cancel the migration"); + qobject_unref(ret); + goto out; + } if (strcmp(status, "cancelled") == 0) { qobject_unref(ret); break; @@ -1169,6 +1178,7 @@ static void test_migrate_guest_off_abort(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); +out: qos_object_destroy((QOSGraphObject *)vdev); machine_stop(qts); } @@ -1251,8 +1261,7 @@ static void test_migrate_abort_wait_unplug(gconstpointer opaque) qobject_unref(ret); break; } - g_assert_cmpstr(status, !=, "failed"); - g_assert_cmpstr(status, !=, "active"); + g_assert_cmpstr(status, ==, "cancelling"); qobject_unref(ret); } @@ -1324,11 +1333,11 @@ static void test_migrate_abort_active(gconstpointer opaque) ret = migrate_status(qts); status = qdict_get_str(ret, "status"); + g_assert_cmpstr(status, !=, "failed"); if (strcmp(status, "wait-unplug") != 0) { qobject_unref(ret); break; } - g_assert_cmpstr(status, !=, "failed"); qobject_unref(ret); } @@ -1340,6 +1349,11 @@ static void test_migrate_abort_active(gconstpointer opaque) ret = migrate_status(qts); status = qdict_get_str(ret, "status"); + if (strcmp(status, "completed") == 0) { + g_test_skip("Failed to cancel the migration"); + qobject_unref(ret); + goto out; + } if (strcmp(status, "cancelled") == 0) { qobject_unref(ret); break; @@ -1352,6 +1366,7 @@ static void test_migrate_abort_active(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); +out: qos_object_destroy((QOSGraphObject *)vdev); machine_stop(qts); } @@ -1425,6 +1440,11 @@ static void test_migrate_off_abort(gconstpointer opaque) ret = migrate_status(qts); status = qdict_get_str(ret, "status"); + if (strcmp(status, "completed") == 0) { + g_test_skip("Failed to cancel the migration"); + qobject_unref(ret); + goto out; + } if (strcmp(status, "cancelled") == 0) { qobject_unref(ret); break; @@ -1437,6 +1457,7 @@ static void test_migrate_off_abort(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); +out: qos_object_destroy((QOSGraphObject *)vdev); machine_stop(qts); } diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c index 8bf74e516c..fc9f2b9498 100644 --- a/tests/qtest/virtio-net-test.c +++ b/tests/qtest/virtio-net-test.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "libqtest-single.h" #include "qemu/iov.h" #include "qemu/module.h" @@ -87,11 +86,11 @@ static void tx_test(QVirtioDevice *dev, QVIRTIO_NET_TIMEOUT_US); guest_free(alloc, req_addr); - ret = qemu_recv(socket, &len, sizeof(len), 0); + ret = recv(socket, &len, sizeof(len), 0); g_assert_cmpint(ret, ==, sizeof(len)); len = ntohl(len); - ret = qemu_recv(socket, buffer, len, 0); + ret = recv(socket, buffer, len, 0); g_assert_cmpstr(buffer, ==, "TEST"); } @@ -202,11 +201,11 @@ static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) qobject_unref(rsp); /* Catch the first packet and make sure it's a RARP */ - ret = qemu_recv(sv[0], &len, sizeof(len), 0); + ret = recv(sv[0], &len, sizeof(len), 0); g_assert_cmpint(ret, ==, sizeof(len)); len = ntohl(len); - ret = qemu_recv(sv[0], buffer, len, 0); + ret = recv(sv[0], buffer, len, 0); g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); /* @@ -230,7 +229,7 @@ static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) while (true) { int saved_err; - ret = qemu_recv(sv[0], buffer, 60, MSG_DONTWAIT); + ret = recv(sv[0], buffer, 60, MSG_DONTWAIT); saved_err = errno; now = g_get_monotonic_time(); g_assert_cmpint(now, <, deadline); diff --git a/tests/qtest/virtio-rng-test.c b/tests/qtest/virtio-rng-test.c index e6b8cd8e0c..092ba13068 100644 --- a/tests/qtest/virtio-rng-test.c +++ b/tests/qtest/virtio-rng-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/virtio-rng.h" diff --git a/tests/qtest/virtio-test.c b/tests/qtest/virtio-test.c index 6313417630..f7c6afdcf1 100644 --- a/tests/qtest/virtio-test.c +++ b/tests/qtest/virtio-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c index 6781a51447..efba76e716 100644 --- a/tests/qtest/vmgenid-test.c +++ b/tests/qtest/vmgenid-test.c @@ -14,7 +14,7 @@ #include "hw/acpi/acpi-defs.h" #include "boot-sector.h" #include "acpi-utils.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" diff --git a/tests/qtest/vmxnet3-test.c b/tests/qtest/vmxnet3-test.c index 97c23fd3a8..a81025252c 100644 --- a/tests/qtest/vmxnet3-test.c +++ b/tests/qtest/vmxnet3-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qemu/module.h" #include "libqos/qgraph.h" #include "libqos/pci.h" diff --git a/tests/qtest/wdt_ib700-test.c b/tests/qtest/wdt_ib700-test.c index 6c36e43fb8..797288d939 100644 --- a/tests/qtest/wdt_ib700-test.c +++ b/tests/qtest/wdt_ib700-test.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" #include "qapi/qmp/qdict.h" #include "qemu/timer.h" diff --git a/tests/qtest/xlnx-can-test.c b/tests/qtest/xlnx-can-test.c index 54de71a686..89610fc499 100644 --- a/tests/qtest/xlnx-can-test.c +++ b/tests/qtest/xlnx-can-test.c @@ -25,7 +25,7 @@ */ #include "qemu/osdep.h" -#include "libqos/libqtest.h" +#include "libqtest.h" /* Base address. */ #define CAN0_BASE_ADDR 0xFF060000 diff --git a/tests/tcg/Makefile.prereqs b/tests/tcg/Makefile.prereqs deleted file mode 100644 index 9a29604a83..0000000000 --- a/tests/tcg/Makefile.prereqs +++ /dev/null @@ -1,18 +0,0 @@ -# -*- Mode: makefile -*- -# -# TCG Compiler Probe -# -# This Makefile fragment is included multiple times in the main make -# script to probe for available compilers. This is used to build up a -# selection of required docker targets before we invoke a sub-make for -# each target. - -DOCKER_IMAGE:= - --include $(BUILD_DIR)/tests/tcg/config-$(PROBE_TARGET).mak - -ifneq ($(DOCKER_IMAGE),) -build-tcg-tests-$(PROBE_TARGET): docker-image-$(DOCKER_IMAGE) -endif -$(BUILD_DIR)/tests/tcg/config_$(PROBE_TARGET).mak: config-host.mak -config-host.mak: $(SRC_PATH)/tests/tcg/configure.sh diff --git a/tests/tcg/Makefile.qemu b/tests/tcg/Makefile.qemu deleted file mode 100644 index 84c8543878..0000000000 --- a/tests/tcg/Makefile.qemu +++ /dev/null @@ -1,121 +0,0 @@ -# -*- Mode: makefile -*- -# -# TCG tests (per-target rules) -# -# This Makefile fragment is included from the build-tcg target, once -# for each target we build. We have two options for compiling, either -# using a configured guest compiler or calling one of our docker images -# to do it for us. -# - -# The configure script fills in extra information about -# useful docker images or alternative compiler flags. - -# Usage: $(call quiet-command,command and args,"NAME","args to print") -# This will run "command and args", and either: -# if V=1 just print the whole command and args -# otherwise print the 'quiet' output in the format " NAME args to print" -# NAME should be a short name of the command, 7 letters or fewer. -# If called with only a single argument, will print nothing in quiet mode. -quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1 -quiet-@ = $(if $(V),,@) -quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3) - -CROSS_CC_GUEST:= -CROSS_AS_GUEST:= -CROSS_LD_GUEST:= -DOCKER_IMAGE:= - --include tests/tcg/config-$(TARGET).mak - -GUEST_BUILD= -TCG_MAKE=../Makefile.target - -# We also need the Docker make rules to depend on -SKIP_DOCKER_BUILD=1 -include $(SRC_PATH)/tests/docker/Makefile.include - -# Support installed Cross Compilers - -ifdef CROSS_CC_GUEST - -.PHONY: cross-build-guest-tests -cross-build-guest-tests: - $(call quiet-command, \ - (mkdir -p tests/tcg/$(TARGET) && cd tests/tcg/$(TARGET) && \ - $(MAKE) -f $(TCG_MAKE) TARGET="$(TARGET)" CC="$(CROSS_CC_GUEST)" \ - $(if $(CROSS_AS_GUEST),AS="$(CROSS_AS_GUEST)") \ - $(if $(CROSS_LD_GUEST),LD="$(CROSS_LD_GUEST)") \ - SRC_PATH="$(SRC_PATH)" BUILD_STATIC=$(CROSS_CC_GUEST_STATIC) \ - EXTRA_CFLAGS="$(CROSS_CC_GUEST_CFLAGS)"), \ - "BUILD","$(TARGET) guest-tests with $(CROSS_CC_GUEST)") - -GUEST_BUILD=cross-build-guest-tests - -endif - -# Support building with Docker - -ifneq ($(DOCKER_IMAGE),) - -DOCKER_COMPILE_CMD="$(DOCKER_SCRIPT) cc \ - --cc $(DOCKER_CROSS_CC_GUEST) \ - -i qemu/$(DOCKER_IMAGE) \ - -s $(SRC_PATH) -- " - -DOCKER_AS_CMD=$(if $(DOCKER_CROSS_AS_GUEST),"$(DOCKER_SCRIPT) cc \ - --cc $(DOCKER_CROSS_AS_GUEST) \ - -i qemu/$(DOCKER_IMAGE) \ - -s $(SRC_PATH) -- ") - -DOCKER_LD_CMD=$(if $(DOCKER_CROSS_LD_GUEST),"$(DOCKER_SCRIPT) cc \ - --cc $(DOCKER_CROSS_LD_GUEST) \ - -i qemu/$(DOCKER_IMAGE) \ - -s $(SRC_PATH) -- ") - - -.PHONY: docker-build-guest-tests -docker-build-guest-tests: docker-image-$(DOCKER_IMAGE) - $(call quiet-command, \ - (mkdir -p tests/tcg/$(TARGET) && cd tests/tcg/$(TARGET) && \ - $(MAKE) -f $(TCG_MAKE) TARGET="$(TARGET)" CC=$(DOCKER_COMPILE_CMD) \ - $(if $(DOCKER_AS_CMD),AS=$(DOCKER_AS_CMD)) \ - $(if $(DOCKER_LD_CMD),LD=$(DOCKER_LD_CMD)) \ - SRC_PATH="$(SRC_PATH)" BUILD_STATIC=y \ - EXTRA_CFLAGS="$(CROSS_CC_GUEST_CFLAGS)"), \ - "BUILD","$(TARGET) guest-tests with docker qemu/$(DOCKER_IMAGE)") - -GUEST_BUILD=docker-build-guest-tests - -endif - -# Final targets -all: - @echo "Do not invoke this Makefile directly"; exit 1 - -.PHONY: guest-tests - -ifneq ($(GUEST_BUILD),) -guest-tests: $(GUEST_BUILD) - -run-guest-tests: guest-tests - $(call quiet-command, \ - (cd tests/tcg/$(TARGET) && \ - $(MAKE) -f $(TCG_MAKE) TARGET="$(TARGET)" \ - SRC_PATH="$(SRC_PATH)" SPEED=$(SPEED) run), \ - "RUN", "tests for $(TARGET_NAME)") - -else -guest-tests: - $(call quiet-command, true, "BUILD", \ - "$(TARGET) guest-tests SKIPPED") - -run-guest-tests: - $(call quiet-command, true, "RUN", \ - "tests for $(TARGET) SKIPPED") -endif - -# It doesn't matter if these don't exits -.PHONY: clean-guest-tests -clean-guest-tests: - rm -rf tests/tcg/$(TARGET) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index ae8004c76e..f427a0304e 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -30,10 +30,12 @@ # all: --include ../../../config-host.mak +-include ../config-host.mak -include ../config-$(TARGET).mak -ifeq ($(CONFIG_USER_ONLY),y) --include $(SRC_PATH)/configs/targets/$(TARGET)/default.mak + +# Get semihosting definitions for user-mode emulation +ifeq ($(filter %-softmmu, $(TARGET)),) +-include $(SRC_PATH)/configs/targets/$(TARGET).mak endif # for including , in command strings @@ -42,7 +44,7 @@ COMMA := , quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) # $1 = test name, $2 = cmd, $3 = desc -ifdef CONFIG_USER_ONLY +ifeq ($(filter %-softmmu, $(TARGET)),) run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2 > $1.out, \ "TEST",$3) else @@ -75,7 +77,6 @@ EXTRA_TESTS= # Start with a blank slate, the build targets get to add stuff first CFLAGS= -QEMU_CFLAGS= LDFLAGS= QEMU_OPTS= @@ -89,7 +90,7 @@ QEMU_OPTS= # 90s with --enable-tcg-interpreter TIMEOUT=90 -ifdef CONFIG_USER_ONLY +ifeq ($(filter %-softmmu, $(TARGET)),) # The order we include is important. We include multiarch first and # then the target. If there are common tests shared between # sub-targets (e.g. ARM & AArch64) then it is up to @@ -151,7 +152,7 @@ extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1)) RUN_TESTS+=$(EXTRA_RUNS) -ifdef CONFIG_USER_ONLY +ifeq ($(filter %-softmmu, $(TARGET)),) run-%: % $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)") @@ -185,4 +186,5 @@ gdb-%: % .PHONY: run run: $(RUN_TESTS) -# There is no clean target, the calling make just rm's the tests build dir +clean: + rm -f $(TESTS) *.o diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index a7286ac295..f6fcd4829e 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -64,7 +64,7 @@ run-memory-replay: memory-replay run-memory-record EXTRA_RUNS+=run-memory-replay -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_3),) +ifneq ($(CROSS_CC_HAS_ARMV8_3),) pauth-3: CFLAGS += -march=armv8.3-a else pauth-3: diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 1d967901bd..6ad0ad49f9 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -18,7 +18,7 @@ run-fcvt: fcvt $(call diff-out,$<,$(AARCH64_SRC)/fcvt.ref) # Pauth Tests -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_3),) +ifneq ($(CROSS_CC_HAS_ARMV8_3),) AARCH64_TESTS += pauth-1 pauth-2 pauth-4 pauth-5 pauth-%: CFLAGS += -march=armv8.3-a run-pauth-%: QEMU_OPTS += -cpu max @@ -27,7 +27,7 @@ endif # BTI Tests # bti-1 tests the elf notes, so we require special compiler support. -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_BTI),) +ifneq ($(CROSS_CC_HAS_ARMV8_BTI),) AARCH64_TESTS += bti-1 bti-1: CFLAGS += -mbranch-protection=standard bti-1: LDFLAGS += -nostdlib @@ -36,12 +36,12 @@ endif AARCH64_TESTS += bti-2 # MTE Tests -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_MTE),) +ifneq ($(CROSS_CC_HAS_ARMV8_MTE),) AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 mte-%: CFLAGS += -march=armv8.5-a+memtag endif -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_SVE),) +ifneq ($(CROSS_CC_HAS_SVE),) # System Registers Tests AARCH64_TESTS += sysregs sysregs: CFLAGS+=-march=armv8.1-a+sve @@ -50,6 +50,23 @@ sysregs: CFLAGS+=-march=armv8.1-a+sve AARCH64_TESTS += sve-ioctls sve-ioctls: CFLAGS+=-march=armv8.1-a+sve +# Vector SHA1 +sha1-vector: CFLAGS=-O3 +sha1-vector: sha1.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) +run-sha1-vector: sha1-vector run-sha1 + $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)") + $(call diff-out, sha1-vector, sha1.out) + +TESTS += sha1-vector + +# Vector versions of sha512 (-O3 triggers vectorisation) +sha512-vector: CFLAGS=-O3 +sha512-vector: sha512.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +TESTS += sha512-vector + ifneq ($(HAVE_GDB_BIN),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py @@ -69,7 +86,11 @@ run-gdbstub-sve-ioctls: sve-ioctls EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls endif +endif +ifneq ($(CROSS_CC_HAS_SVE2),) +AARCH64_TESTS += test-826 +test-826: CFLAGS+=-march=armv8.1-a+sve2 endif TESTS += $(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/float_convd.ref b/tests/tcg/aarch64/float_convd.ref new file mode 100644 index 0000000000..cc5e2ef08b --- /dev/null +++ b/tests/tcg/aarch64/float_convd.ref @@ -0,0 +1,988 @@ +### Rounding to nearest +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding upwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000200000000000000p-25:0x33000001) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe800000000000000p-25:0x337ffff4) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801c00000000000000p-15:0x387fc00e) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000e00000000000000p-14:0x38800007) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0aa00000000000000p+1:0x402df855) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding downwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x1.00000000000000000000p-149:0x80000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding to zero +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) diff --git a/tests/tcg/aarch64/system/semiheap.c b/tests/tcg/aarch64/system/semiheap.c new file mode 100644 index 0000000000..4ed258476d --- /dev/null +++ b/tests/tcg/aarch64/system/semiheap.c @@ -0,0 +1,93 @@ +/* + * Semihosting System HEAPINFO Test + * + * Copyright (c) 2021 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +#define SYS_HEAPINFO 0x16 + +uintptr_t __semi_call(uintptr_t type, uintptr_t arg0) +{ + register uintptr_t t asm("x0") = type; + register uintptr_t a0 asm("x1") = arg0; + asm("hlt 0xf000" + : "=r" (t) + : "r" (t), "r" (a0) + : "memory" ); + + return t; +} + +int main(int argc, char *argv[argc]) +{ + struct { + void *heap_base; + void *heap_limit; + void *stack_base; + void *stack_limit; + } info = { }; + void *ptr_to_info = (void *) &info; + uint32_t *ptr_to_heap; + int i; + + ml_printf("Semihosting Heap Info Test\n"); + + __semi_call(SYS_HEAPINFO, (uintptr_t) &ptr_to_info); + + if (info.heap_base == NULL || info.heap_limit == NULL) { + ml_printf("null heap: %p -> %p\n", info.heap_base, info.heap_limit); + return -1; + } + + /* Error if heap base is above limit */ + if ((uintptr_t) info.heap_base >= (uintptr_t) info.heap_limit) { + ml_printf("heap base %p >= heap_limit %p\n", + info.heap_base, info.heap_limit); + return -2; + } + + if (info.stack_base == NULL) { + ml_printf("null stack: %p -> %p\n", info.stack_base, info.stack_limit); + return -3; + } + + /* + * boot.S put our stack somewhere inside the data segment of the + * ELF file, and we know that SYS_HEAPINFO won't pick a range + * that overlaps with part of a loaded ELF file. So the info + * struct (on the stack) should not be inside the reported heap. + */ + if (ptr_to_info > info.heap_base && ptr_to_info < info.heap_limit) { + ml_printf("info appears to be inside the heap: %p in %p:%p\n", + ptr_to_info, info.heap_base, info.heap_limit); + return -4; + } + + ml_printf("heap: %p -> %p\n", info.heap_base, info.heap_limit); + ml_printf("stack: %p <- %p\n", info.stack_limit, info.stack_base); + + /* finally can we read/write the heap */ + ptr_to_heap = (uint32_t *) info.heap_base; + for (i = 0; i < 512; i++) { + *ptr_to_heap++ = i; + } + ptr_to_heap = (uint32_t *) info.heap_base; + for (i = 0; i < 512; i++) { + uint32_t tmp = *ptr_to_heap; + if (tmp != i) { + ml_printf("unexpected value in heap: %d @ %p", tmp, ptr_to_heap); + return -5; + } + ptr_to_heap++; + } + ml_printf("r/w to heap upto %p\n", ptr_to_heap); + + ml_printf("Passed HeapInfo checks\n"); + return 0; +} diff --git a/tests/tcg/aarch64/test-826.c b/tests/tcg/aarch64/test-826.c new file mode 100644 index 0000000000..f59740a8c5 --- /dev/null +++ b/tests/tcg/aarch64/test-826.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include + +static void *expected; + +void sigsegv(int sig, siginfo_t *info, void *vuc) +{ + ucontext_t *uc = vuc; + + assert(info->si_addr == expected); + uc->uc_mcontext.pc += 4; +} + +int main() +{ + struct sigaction sa = { + .sa_sigaction = sigsegv, + .sa_flags = SA_SIGINFO + }; + + void *page; + long ofs; + + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + page = mmap(0, getpagesize(), PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (page == MAP_FAILED) { + perror("mmap"); + return EXIT_FAILURE; + } + + ofs = 0x124; + expected = page + ofs; + + asm("ptrue p0.d, vl1\n\t" + "dup z0.d, %0\n\t" + "ldnt1h {z1.d}, p0/z, [z0.d, %1]\n\t" + "dup z1.d, %1\n\t" + "ldnt1h {z0.d}, p0/z, [z1.d, %0]" + : : "r"(page), "r"(ofs) : "v0", "v1"); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index f509d823d4..2f815120a5 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -70,6 +70,23 @@ endif ARM_TESTS += commpage +# Vector SHA1 +sha1-vector: CFLAGS=-O3 +sha1-vector: sha1.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) +run-sha1-vector: sha1-vector run-sha1 + $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)") + $(call diff-out, sha1-vector, sha1.out) + +ARM_TESTS += sha1-vector + +# Vector versions of sha512 (-O3 triggers vectorisation) +sha512-vector: CFLAGS=-O3 +sha512-vector: sha512.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +ARM_TESTS += sha512-vector + TESTS += $(ARM_TESTS) # On ARM Linux only supports 4k pages diff --git a/tests/tcg/arm/float_convd.ref b/tests/tcg/arm/float_convd.ref new file mode 100644 index 0000000000..5032c2ee2e --- /dev/null +++ b/tests/tcg/arm/float_convd.ref @@ -0,0 +1,988 @@ +### Rounding to nearest +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (INEXACT ) + to uint32: 2 (OK) + to uint64: 2 (INEXACT ) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (INEXACT ) + to uint32: 65503 (OK) + to uint64: 65503 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (INEXACT ) + to uint32: 65504 (OK) + to uint64: 65504 (INEXACT ) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (INEXACT ) + to uint32: 65505 (OK) + to uint64: 65505 (INEXACT ) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (INEXACT ) + to uint32: 131007 (OK) + to uint64: 131007 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (INEXACT ) + to uint32: 131008 (OK) + to uint64: 131008 (INEXACT ) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (INEXACT ) + to uint32: 131009 (OK) + to uint64: 131009 (INEXACT ) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (INEXACT ) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (INEXACT ) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding upwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000200000000000000p-25:0x33000001) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe800000000000000p-25:0x337ffff4) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801c00000000000000p-15:0x387fc00e) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000e00000000000000p-14:0x38800007) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (INEXACT ) + to uint32: 2 (OK) + to uint64: 2 (INEXACT ) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0aa00000000000000p+1:0x402df855) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (INEXACT ) + to uint32: 65503 (OK) + to uint64: 65503 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (INEXACT ) + to uint32: 65504 (OK) + to uint64: 65504 (INEXACT ) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (INEXACT ) + to uint32: 65505 (OK) + to uint64: 65505 (INEXACT ) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (INEXACT ) + to uint32: 131007 (OK) + to uint64: 131007 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (INEXACT ) + to uint32: 131008 (OK) + to uint64: 131008 (INEXACT ) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (INEXACT ) + to uint32: 131009 (OK) + to uint64: 131009 (INEXACT ) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (INEXACT ) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (INEXACT ) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding downwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x1.00000000000000000000p-149:0x80000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (INEXACT ) + to uint32: 2 (OK) + to uint64: 2 (INEXACT ) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (INEXACT ) + to uint32: 65503 (OK) + to uint64: 65503 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (INEXACT ) + to uint32: 65504 (OK) + to uint64: 65504 (INEXACT ) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (INEXACT ) + to uint32: 65505 (OK) + to uint64: 65505 (INEXACT ) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (INEXACT ) + to uint32: 131007 (OK) + to uint64: 131007 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (INEXACT ) + to uint32: 131008 (OK) + to uint64: 131008 (INEXACT ) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (INEXACT ) + to uint32: 131009 (OK) + to uint64: 131009 (INEXACT ) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (INEXACT ) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (INEXACT ) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding to zero +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: 1 (INEXACT INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (UNDERFLOW INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (UNDERFLOW INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (INEXACT ) + to uint32: 1 (OK) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (INEXACT ) + to uint32: 2 (OK) + to uint64: 2 (INEXACT ) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (INEXACT ) + to uint32: 65503 (OK) + to uint64: 65503 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (INEXACT ) + to uint32: 65504 (OK) + to uint64: 65504 (INEXACT ) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (INEXACT ) + to uint32: 65505 (OK) + to uint64: 65505 (INEXACT ) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (INEXACT ) + to uint32: 131007 (OK) + to uint64: 131007 (INEXACT ) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (INEXACT ) + to uint32: 131008 (OK) + to uint64: 131008 (INEXACT ) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (INEXACT ) + to uint32: 131009 (OK) + to uint64: 131009 (INEXACT ) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (INEXACT ) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (INEXACT ) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: -1 (INEXACT INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index 763e9b6ad8..691d90abac 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -64,9 +64,9 @@ fi : ${cross_cc_ppc="powerpc-linux-gnu-gcc"} : ${cross_cc_cflags_ppc="-m32"} : ${cross_cc_ppc64="powerpc64-linux-gnu-gcc"} -: ${cross_cc_cflags_ppc64="-m64 -mbig"} +: ${cross_cc_cflags_ppc64="-m64 -mbig-endian"} : ${cross_cc_ppc64le="$cross_cc_ppc64"} -: ${cross_cc_cflags_ppc64le="-m64 -mlittle"} +: ${cross_cc_cflags_ppc64le="-m64 -mlittle-endian"} : ${cross_cc_riscv64="riscv64-linux-gnu-gcc"} : ${cross_cc_s390x="s390x-linux-gnu-gcc"} : ${cross_cc_sh4="sh4-linux-gnu-gcc"} @@ -80,6 +80,15 @@ fi : ${cross_as_tricore="tricore-as"} : ${cross_ld_tricore="tricore-ld"} +makefile=tests/tcg/Makefile.prereqs +echo "# Automatically generated by configure - do not modify" > $makefile + +config_host_mak=tests/tcg/config-host.mak +echo "# Automatically generated by configure - do not modify" > $config_host_mak +echo "SRC_PATH=$source_path" >> $config_host_mak +echo "HOST_CC=$host_cc" >> $config_host_mak + +tcg_tests_targets= for target in $target_list; do arch=${target%%-*} @@ -97,7 +106,7 @@ for target in $target_list; do aarch64-*) # We don't have any bigendian build tools so we only use this for AArch64 container_hosts="x86_64 aarch64" - container_image=debian-arm64-test-cross + container_image=debian-arm64-cross container_cross_cc=aarch64-linux-gnu-gcc-10 ;; alpha-*) @@ -180,7 +189,7 @@ for target in $target_list; do ;; riscv64-*) container_hosts=x86_64 - container_image=debian-riscv64-cross + container_image=debian-riscv64-test-cross container_cross_cc=riscv64-linux-gnu-gcc ;; s390x-*) @@ -223,26 +232,17 @@ for target in $target_list; do echo "# Automatically generated by configure - do not modify" > $config_target_mak echo "TARGET_NAME=$arch" >> $config_target_mak - echo "target=$target" >> $config_target_mak case $target in - *-linux-user) - echo "CONFIG_USER_ONLY=y" >> $config_target_mak - echo "CONFIG_LINUX_USER=y" >> $config_target_mak - echo "QEMU=$PWD/qemu-$arch" >> $config_target_mak - ;; - *-bsd-user) - echo "CONFIG_USER_ONLY=y" >> $config_target_mak - echo "CONFIG_BSD_USER=y" >> $config_target_mak - echo "QEMU=$PWD/qemu-$arch" >> $config_target_mak - ;; *-softmmu) - echo "CONFIG_SOFTMMU=y" >> $config_target_mak - echo "QEMU=$PWD/qemu-system-$arch" >> $config_target_mak + test -f $source_path/tests/tcg/$arch/Makefile.softmmu-target || continue + qemu="qemu-system-$arch" + ;; + *-linux-user|*-bsd-user) + qemu="qemu-$arch" ;; esac eval "target_compiler_cflags=\${cross_cc_cflags_$arch}" - echo "CROSS_CC_GUEST_CFLAGS=$target_compiler_cflags" >> $config_target_mak got_cross_cc=no @@ -261,13 +261,12 @@ for target in $target_list; do if do_compiler "$target_compiler" $target_compiler_cflags \ -o $TMPE $TMPC ; then got_cross_cc=yes - echo "CROSS_CC_GUEST_STATIC=y" >> $config_target_mak - echo "CROSS_CC_GUEST=$target_compiler" >> $config_target_mak + echo "CC=$target_compiler" >> $config_target_mak fi else got_cross_cc=yes - echo "CROSS_CC_GUEST_STATIC=y" >> $config_target_mak - echo "CROSS_CC_GUEST=$target_compiler" >> $config_target_mak + echo "BUILD_STATIC=y" >> $config_target_mak + echo "CC=$target_compiler" >> $config_target_mak fi fi fi @@ -278,9 +277,8 @@ for target in $target_list; do if has $target_as && has $target_ld; then case $target in tricore-softmmu) - echo "CROSS_CC_GUEST=$target_as" >> $config_target_mak - echo "CROSS_AS_GUEST=$target_as" >> $config_target_mak - echo "CROSS_LD_GUEST=$target_ld" >> $config_target_mak + echo "AS=$target_as" >> $config_target_mak + echo "LD=$target_ld" >> $config_target_mak got_cross_cc=yes ;; esac @@ -299,6 +297,10 @@ for target in $target_list; do -march=armv8.1-a+sve -o $TMPE $TMPC; then echo "CROSS_CC_HAS_SVE=y" >> $config_target_mak fi + if do_compiler "$target_compiler" $target_compiler_cflags \ + -march=armv8.1-a+sve2 -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_SVE2=y" >> $config_target_mak + fi if do_compiler "$target_compiler" $target_compiler_cflags \ -march=armv8.3-a -o $TMPE $TMPC; then echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak @@ -333,18 +335,42 @@ for target in $target_list; do test -n "$container_image"; then for host in $container_hosts; do if test "$host" = "$cpu"; then - echo "DOCKER_IMAGE=$container_image" >> $config_target_mak - echo "DOCKER_CROSS_CC_GUEST=$container_cross_cc" >> \ - $config_target_mak + echo "build-tcg-tests-$target: docker-image-$container_image" >> $makefile + echo "BUILD_STATIC=y" >> $config_target_mak + echo "CC=\$(DOCKER_SCRIPT) cc --cc $container_cross_cc -i qemu/$container_image -s $source_path --" >> $config_target_mak if test -n "$container_cross_as"; then - echo "DOCKER_CROSS_AS_GUEST=$container_cross_as" >> \ - $config_target_mak + echo "AS=\$(DOCKER_SCRIPT) cc --cc $container_cross_as -i qemu/$container_image -s $source_path --" >> $config_target_mak fi if test -n "$container_cross_ld"; then - echo "DOCKER_CROSS_LD_GUEST=$container_cross_ld" >> \ - $config_target_mak + echo "LD=\$(DOCKER_SCRIPT) cc --cc $container_cross_ld -i qemu/$container_image -s $source_path --" >> $config_target_mak fi + case $target in + aarch64-*) + echo "CROSS_CC_HAS_SVE=y" >> $config_target_mak + echo "CROSS_CC_HAS_SVE2=y" >> $config_target_mak + echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak + echo "CROSS_CC_HAS_ARMV8_BTI=y" >> $config_target_mak + echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak + ;; + ppc*) + echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak + echo "CROSS_CC_HAS_POWER10=y" >> $config_target_mak + ;; + i386-linux-user) + echo "CROSS_CC_HAS_I386_NOPIE=y" >> $config_target_mak + ;; + esac + got_cross_cc=yes + break fi done fi + if test $got_cross_cc = yes; then + mkdir -p tests/tcg/$target + echo "QEMU=$PWD/$qemu" >> $config_target_mak + echo "EXTRA_CFLAGS=$target_compiler_cflags" >> $config_target_mak + echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> $makefile + tcg_tests_targets="$tcg_tests_targets $target" + fi done +echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> $makefile diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 8b07a28166..23b9870534 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -1,5 +1,5 @@ ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -30,6 +30,7 @@ first: $(HEX_SRC)/first.S HEX_TESTS = first HEX_TESTS += hex_sigsegv HEX_TESTS += misc +HEX_TESTS += usr HEX_TESTS += preg_alias HEX_TESTS += dual_stores HEX_TESTS += multi_result @@ -43,3 +44,8 @@ HEX_TESTS += fpstuff HEX_TESTS += overflow TESTS += $(HEX_TESTS) + +# This test has to be compiled for the -mv67t target +usr: usr.c + $(CC) $(CFLAGS) -mv67t -O2 -Wno-inline-asm -Wno-expansion-to-defined $< -o $@ $(LDFLAGS) + diff --git a/tests/tcg/hexagon/circ.c b/tests/tcg/hexagon/circ.c index 67a1aa3054..354416eb6d 100644 --- a/tests/tcg/hexagon/circ.c +++ b/tests/tcg/hexagon/circ.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -415,7 +415,8 @@ static void circ_test_v3(void) { int *p = wbuf; int size = 15; - int K = 4; /* 64 bytes */ + /* set high bit in K to test unsigned extract in fcirc */ + int K = 8; /* 1024 bytes */ int element; int i; diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c index 0dff429f4c..56bf562a40 100644 --- a/tests/tcg/hexagon/fpstuff.c +++ b/tests/tcg/hexagon/fpstuff.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2020-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2020-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,8 +38,11 @@ const int SF_NaN_special = 0x7f800001; const int SF_ANY = 0x3f800000; const int SF_HEX_NAN = 0xffffffff; const int SF_small_neg = 0xab98fba8; +const int SF_denorm = 0x00000001; +const int SF_random = 0x346001d6; -const long long DF_NaN = 0x7ff8000000000000ULL; +const long long DF_QNaN = 0x7ff8000000000000ULL; +const long long DF_SNaN = 0x7ff7000000000000ULL; const long long DF_ANY = 0x3f80000000000000ULL; const long long DF_HEX_NAN = 0xffffffffffffffffULL; const long long DF_small_neg = 0xbd731f7500000000ULL; @@ -126,7 +129,7 @@ static void check_compare_exception(void) "p0 = dfcmp.eq(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(DF_NaN), "r"(DF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -135,7 +138,7 @@ static void check_compare_exception(void) "p0 = dfcmp.gt(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(DF_NaN), "r"(DF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -144,7 +147,7 @@ static void check_compare_exception(void) "p0 = dfcmp.ge(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(DF_NaN), "r"(DF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -206,7 +209,7 @@ static void check_dfminmax(void) int usr; /* - * Execute dfmin/dfmax instructions with one operand as NaN + * Execute dfmin/dfmax instructions with one operand as SNaN * Check that * Result is the other operand * Invalid bit in USR is set @@ -214,7 +217,7 @@ static void check_dfminmax(void) asm (CLEAR_FPSTATUS "%0 = dfmin(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_NaN), "r"(DF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_ANY) : "r2", "usr"); check64(minmax, DF_ANY); check_fpstatus(usr, FPINVF); @@ -222,13 +225,35 @@ static void check_dfminmax(void) asm (CLEAR_FPSTATUS "%0 = dfmax(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_NaN), "r"(DF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_ANY) : "r2", "usr"); check64(minmax, DF_ANY); check_fpstatus(usr, FPINVF); /* - * Execute dfmin/dfmax instructions with both operands NaN + * Execute dfmin/dfmax instructions with one operand as QNaN + * Check that + * Result is the other operand + * No bit in USR is set + */ + asm (CLEAR_FPSTATUS + "%0 = dfmin(%2, %3)\n\t" + "%1 = usr\n\t" + : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "r2", "usr"); + check64(minmax, DF_ANY); + check_fpstatus(usr, 0); + + asm (CLEAR_FPSTATUS + "%0 = dfmax(%2, %3)\n\t" + "%1 = usr\n\t" + : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "r2", "usr"); + check64(minmax, DF_ANY); + check_fpstatus(usr, 0); + + /* + * Execute dfmin/dfmax instructions with both operands SNaN * Check that * Result is DF_HEX_NAN * Invalid bit in USR is set @@ -236,7 +261,7 @@ static void check_dfminmax(void) asm (CLEAR_FPSTATUS "%0 = dfmin(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_NaN), "r"(DF_NaN) + : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_SNaN) : "r2", "usr"); check64(minmax, DF_HEX_NAN); check_fpstatus(usr, FPINVF); @@ -244,16 +269,39 @@ static void check_dfminmax(void) asm (CLEAR_FPSTATUS "%0 = dfmax(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_NaN), "r"(DF_NaN) + : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_SNaN) : "r2", "usr"); check64(minmax, DF_HEX_NAN); check_fpstatus(usr, FPINVF); + + /* + * Execute dfmin/dfmax instructions with both operands QNaN + * Check that + * Result is DF_HEX_NAN + * No bit in USR is set + */ + asm (CLEAR_FPSTATUS + "%0 = dfmin(%2, %3)\n\t" + "%1 = usr\n\t" + : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_QNaN) + : "r2", "usr"); + check64(minmax, DF_HEX_NAN); + check_fpstatus(usr, 0); + + asm (CLEAR_FPSTATUS + "%0 = dfmax(%2, %3)\n\t" + "%1 = usr\n\t" + : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_QNaN) + : "r2", "usr"); + check64(minmax, DF_HEX_NAN); + check_fpstatus(usr, 0); } -static void check_recip_exception(void) +static void check_sfrecipa(void) { int result; int usr; + int pred; /* * Check that sfrecipa doesn't set status bits when @@ -329,6 +377,17 @@ static void check_recip_exception(void) : "r2", "p0", "usr"); check32(result, 0x3f800000); check_fpstatus(usr, 0); + + /* + * Check that sfrecipa properly handles denorm + */ + asm (CLEAR_FPSTATUS + "%0,p0 = sfrecipa(%2, %3)\n\t" + "%1 = p0\n\t" + : "=r"(result), "=r"(pred) : "r"(SF_denorm), "r"(SF_random) + : "p0", "usr"); + check32(result, 0x6a920001); + check32(pred, 0x80); } static void check_canonical_NaN(void) @@ -411,7 +470,7 @@ static void check_canonical_NaN(void) asm(CLEAR_FPSTATUS "%0 = convert_df2sf(%2)\n\t" "%1 = usr\n\t" - : "=r"(sf_result), "=r"(usr) : "r"(DF_NaN) + : "=r"(sf_result), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check32(sf_result, SF_HEX_NAN); check_fpstatus(usr, 0); @@ -419,7 +478,7 @@ static void check_canonical_NaN(void) asm(CLEAR_FPSTATUS "%0 = dfadd(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(df_result), "=r"(usr) : "r"(DF_NaN), "r"(DF_ANY) + : "=r"(df_result), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) : "r2", "usr"); check64(df_result, DF_HEX_NAN); check_fpstatus(usr, 0); @@ -427,7 +486,7 @@ static void check_canonical_NaN(void) asm(CLEAR_FPSTATUS "%0 = dfsub(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(df_result), "=r"(usr) : "r"(DF_NaN), "r"(DF_ANY) + : "=r"(df_result), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) : "r2", "usr"); check64(df_result, DF_HEX_NAN); check_fpstatus(usr, 0); @@ -455,6 +514,28 @@ static void check_invsqrta(void) check32(predval, 0x0); } +static void check_sffixupn(void) +{ + int result; + + /* Check that sffixupn properly deals with denorm */ + asm volatile("%0 = sffixupn(%1, %2)\n\t" + : "=r"(result) + : "r"(SF_random), "r"(SF_denorm)); + check32(result, 0x246001d6); +} + +static void check_sffixupd(void) +{ + int result; + + /* Check that sffixupd properly deals with denorm */ + asm volatile("%0 = sffixupd(%1, %2)\n\t" + : "=r"(result) + : "r"(SF_denorm), "r"(SF_random)); + check32(result, 0x146001d6); +} + static void check_float2int_convs() { int res32; @@ -567,7 +648,7 @@ static void check_float2int_convs() asm(CLEAR_FPSTATUS "%0 = convert_df2w(%2)\n\t" "%1 = usr\n\t" - : "=r"(res32), "=r"(usr) : "r"(DF_NaN) + : "=r"(res32), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check32(res32, -1); check_fpstatus(usr, FPINVF); @@ -575,7 +656,7 @@ static void check_float2int_convs() asm(CLEAR_FPSTATUS "%0 = convert_df2w(%2):chop\n\t" "%1 = usr\n\t" - : "=r"(res32), "=r"(usr) : "r"(DF_NaN) + : "=r"(res32), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check32(res32, -1); check_fpstatus(usr, FPINVF); @@ -583,7 +664,7 @@ static void check_float2int_convs() asm(CLEAR_FPSTATUS "%0 = convert_df2d(%2)\n\t" "%1 = usr\n\t" - : "=r"(res64), "=r"(usr) : "r"(DF_NaN) + : "=r"(res64), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check64(res64, -1); check_fpstatus(usr, FPINVF); @@ -591,7 +672,7 @@ static void check_float2int_convs() asm(CLEAR_FPSTATUS "%0 = convert_df2d(%2):chop\n\t" "%1 = usr\n\t" - : "=r"(res64), "=r"(usr) : "r"(DF_NaN) + : "=r"(res64), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check64(res64, -1); check_fpstatus(usr, FPINVF); @@ -602,9 +683,11 @@ int main() check_compare_exception(); check_sfminmax(); check_dfminmax(); - check_recip_exception(); + check_sfrecipa(); check_canonical_NaN(); check_invsqrta(); + check_sffixupn(); + check_sffixupd(); check_float2int_convs(); puts(err ? "FAIL" : "PASS"); diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index 312bb98b41..b896f5897e 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2021-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ #include #include #include +#include int err; @@ -432,6 +433,71 @@ TEST_PRED_OP2(pred_and, and, &, "") TEST_PRED_OP2(pred_and_n, and, &, "!") TEST_PRED_OP2(pred_xor, xor, ^, "") +static void test_vadduwsat(void) +{ + /* + * Test for saturation by adding two numbers that add to more than UINT_MAX + * and make sure the result saturates to UINT_MAX + */ + const uint32_t x = 0xffff0000; + const uint32_t y = 0x000fffff; + + memset(expect, 0x12, sizeof(MMVector)); + memset(output, 0x34, sizeof(MMVector)); + + asm volatile ("v10 = vsplat(%0)\n\t" + "v11 = vsplat(%1)\n\t" + "v21.uw = vadd(v11.uw, v10.uw):sat\n\t" + "vmem(%2+#0) = v21\n\t" + : /* no outputs */ + : "r"(x), "r"(y), "r"(output) + : "v10", "v11", "v21", "memory"); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[0].uw[j] = UINT_MAX; + } + + check_output_w(__LINE__, 1); +} + +static void test_vsubuwsat_dv(void) +{ + /* + * Test for saturation by subtracting two numbers where the result is + * negative and make sure the result saturates to zero + * + * vsubuwsat_dv operates on an HVX register pair, so we'll have a + * pair of subtractions + * w - x < 0 + * y - z < 0 + */ + const uint32_t w = 0x000000b7; + const uint32_t x = 0xffffff4e; + const uint32_t y = 0x31fe88e7; + const uint32_t z = 0x7fffff79; + + memset(expect, 0x12, sizeof(MMVector) * 2); + memset(output, 0x34, sizeof(MMVector) * 2); + + asm volatile ("v16 = vsplat(%0)\n\t" + "v17 = vsplat(%1)\n\t" + "v26 = vsplat(%2)\n\t" + "v27 = vsplat(%3)\n\t" + "v25:24.uw = vsub(v17:16.uw, v27:26.uw):sat\n\t" + "vmem(%4+#0) = v24\n\t" + "vmem(%4+#1) = v25\n\t" + : /* no outputs */ + : "r"(w), "r"(y), "r"(x), "r"(z), "r"(output) + : "v16", "v17", "v24", "v25", "v26", "v27", "memory"); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[0].uw[j] = 0x00000000; + expect[1].uw[j] = 0x00000000; + } + + check_output_w(__LINE__, 2); +} + int main() { init_buffers(); @@ -464,6 +530,9 @@ int main() test_pred_and_n(true); test_pred_xor(false); + test_vadduwsat(); + test_vsubuwsat_dv(); + puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; } diff --git a/tests/tcg/hexagon/overflow.c b/tests/tcg/hexagon/overflow.c index 196fcf7f3a..94087851b0 100644 --- a/tests/tcg/hexagon/overflow.c +++ b/tests/tcg/hexagon/overflow.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2021-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,6 +72,20 @@ int read_usr_overflow(void) return result & 1; } +int get_usr_overflow(int usr) +{ + return usr & 1; +} + +int get_usr_fp_invalid(int usr) +{ + return (usr >> 1) & 1; +} + +int get_usr_lpcfg(int usr) +{ + return (usr >> 8) & 0x3; +} jmp_buf jmp_env; int usr_overflow; @@ -82,6 +96,49 @@ static void sig_segv(int sig, siginfo_t *info, void *puc) longjmp(jmp_env, 1); } +static void test_packet(void) +{ + int convres; + int satres; + int usr; + + asm("r2 = usr\n\t" + "r2 = clrbit(r2, #0)\n\t" /* clear overflow bit */ + "r2 = clrbit(r2, #1)\n\t" /* clear FP invalid bit */ + "usr = r2\n\t" + "{\n\t" + " %0 = convert_sf2uw(%3):chop\n\t" + " %1 = satb(%4)\n\t" + "}\n\t" + "%2 = usr\n\t" + : "=r"(convres), "=r"(satres), "=r"(usr) + : "r"(0x6a051b86), "r"(0x0410eec0) + : "r2", "usr"); + + check(convres, 0xffffffff); + check(satres, 0x7f); + check(get_usr_overflow(usr), 1); + check(get_usr_fp_invalid(usr), 1); + + asm("r2 = usr\n\t" + "r2 = clrbit(r2, #0)\n\t" /* clear overflow bit */ + "usr = r2\n\t" + "%2 = r2\n\t" + "p3 = sp3loop0(1f, #1)\n\t" + "1:\n\t" + "{\n\t" + " %0 = satb(%2)\n\t" + "}:endloop0\n\t" + "%1 = usr\n\t" + : "=r"(satres), "=r"(usr) + : "r"(0x0410eec0) + : "r2", "usr", "p3", "sa0", "lc0"); + + check(satres, 0x7f); + check(get_usr_overflow(usr), 1); + check(get_usr_lpcfg(usr), 2); +} + int main() { struct sigaction act; @@ -102,6 +159,8 @@ int main() check(usr_overflow, 0); + test_packet(); + puts(err ? "FAIL" : "PASS"); return err ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/tests/tcg/hexagon/preg_alias.c b/tests/tcg/hexagon/preg_alias.c index 0cac469b78..b44a8112b4 100644 --- a/tests/tcg/hexagon/preg_alias.c +++ b/tests/tcg/hexagon/preg_alias.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -57,17 +57,15 @@ typedef union { static inline void creg_alias(int cval, PRegs *pregs) { - unsigned char val; - asm volatile("c4 = %0" : : "r"(cval)); - - asm volatile("%0 = p0" : "=r"(val)); - pregs->pregs.p0 = val; - asm volatile("%0 = p1" : "=r"(val)); - pregs->pregs.p1 = val; - asm volatile("%0 = p2" : "=r"(val)); - pregs->pregs.p2 = val; - asm volatile("%0 = p3" : "=r"(val)); - pregs->pregs.p3 = val; + asm("c4 = %4\n\t" + "%0 = p0\n\t" + "%1 = p1\n\t" + "%2 = p2\n\t" + "%3 = p3\n\t" + : "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1), + "=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3) + : "r"(cval) + : "p0", "p1", "p2", "p3"); } int err; @@ -83,22 +81,58 @@ static void check(int val, int expect) static inline void creg_alias_pair(unsigned int cval, PRegs *pregs) { unsigned long long cval_pair = (0xdeadbeefULL << 32) | cval; - unsigned char val; int c5; - asm volatile("c5:4 = %0" : : "r"(cval_pair)); - asm volatile("%0 = p0" : "=r"(val)); - pregs->pregs.p0 = val; - asm volatile("%0 = p1" : "=r"(val)); - pregs->pregs.p1 = val; - asm volatile("%0 = p2" : "=r"(val)); - pregs->pregs.p2 = val; - asm volatile("%0 = p3" : "=r"(val)); - pregs->pregs.p3 = val; - asm volatile("%0 = c5" : "=r"(c5)); + asm ("c5:4 = %5\n\t" + "%0 = p0\n\t" + "%1 = p1\n\t" + "%2 = p2\n\t" + "%3 = p3\n\t" + "%4 = c5\n\t" + : "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1), + "=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3), "=r"(c5) + : "r"(cval_pair) + : "p0", "p1", "p2", "p3"); + check(c5, 0xdeadbeef); } +static void test_packet(void) +{ + /* + * Test that setting c4 inside a packet doesn't impact the predicates + * that are read during the packet. + */ + + int result; + int old_val = 0x0000001c; + + /* Test a predicated register transfer */ + result = old_val; + asm ( + "c4 = %1\n\t" + "{\n\t" + " c4 = %2\n\t" + " if (!p2) %0 = %3\n\t" + "}\n\t" + : "+r"(result) + : "r"(0xffffffff), "r"(0xff00ffff), "r"(0x837ed653) + : "p0", "p1", "p2", "p3"); + check(result, old_val); + + /* Test a predicated store */ + result = 0xffffffff; + asm ("c4 = %0\n\t" + "{\n\t" + " c4 = %1\n\t" + " if (!p2) memw(%2) = #0\n\t" + "}\n\t" + : + : "r"(0), "r"(0xffffffff), "r"(&result) + : "p0", "p1", "p2", "p3", "memory"); + check(result, 0x0); +} + int main() { int c4; @@ -164,6 +198,8 @@ int main() creg_alias_pair(0xffffffff, &pregs); check(pregs.creg, 0xffffffff); + test_packet(); + puts(err ? "FAIL" : "PASS"); return err; } diff --git a/tests/tcg/hexagon/usr.c b/tests/tcg/hexagon/usr.c new file mode 100644 index 0000000000..a531511cec --- /dev/null +++ b/tests/tcg/hexagon/usr.c @@ -0,0 +1,1141 @@ +/* + * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Test instructions that might set bits in user status register (USR) + */ + +#include +#include + +int err; + +static void __check(int line, uint32_t val, uint32_t expect) +{ + if (val != expect) { + printf("ERROR at line %d: %d != %d\n", line, val, expect); + err++; + } +} + +#define check(RES, EXP) __check(__LINE__, RES, EXP) + +static void __check32(int line, uint32_t val, uint32_t expect) +{ + if (val != expect) { + printf("ERROR at line %d: 0x%08x != 0x%08x\n", line, val, expect); + err++; + } +} + +#define check32(RES, EXP) __check32(__LINE__, RES, EXP) + +static void __check64(int line, uint64_t val, uint64_t expect) +{ + if (val != expect) { + printf("ERROR at line %d: 0x%016llx != 0x%016llx\n", line, val, expect); + err++; + } +} + +#define check64(RES, EXP) __check64(__LINE__, RES, EXP) + +/* + * Some of the instructions tested are only available on certain versions + * of the Hexagon core + */ +#define CORE_HAS_AUDIO (__HEXAGON_ARCH__ >= 67 && defined(__HEXAGON_AUDIO__)) +#define CORE_IS_V67 (__HEXAGON_ARCH__ >= 67) + +/* Define the bits in Hexagon USR register */ +#define USR_OVF_BIT 0 /* Sticky saturation overflow */ +#define USR_FPINVF_BIT 1 /* IEEE FP invalid sticky flag */ +#define USR_FPDBZF_BIT 2 /* IEEE FP divide-by-zero sticky flag */ +#define USR_FPOVFF_BIT 3 /* IEEE FP overflow sticky flag */ +#define USR_FPUNFF_BIT 4 /* IEEE FP underflow sticky flag */ +#define USR_FPINPF_BIT 5 /* IEEE FP inexact sticky flag */ + +/* Corresponding values in USR */ +#define USR_CLEAR 0 +#define USR_OVF (1 << USR_OVF_BIT) +#define USR_FPINVF (1 << USR_FPINVF_BIT) +#define USR_FPDBZF (1 << USR_FPDBZF_BIT) +#define USR_FPOVFF (1 << USR_FPOVFF_BIT) +#define USR_FPUNFF (1 << USR_FPUNFF_BIT) +#define USR_FPINPF (1 << USR_FPINPF_BIT) + +/* Some useful floating point values */ +const uint32_t SF_INF = 0x7f800000; +const uint32_t SF_QNaN = 0x7fc00000; +const uint32_t SF_SNaN = 0x7fb00000; +const uint32_t SF_QNaN_neg = 0xffc00000; +const uint32_t SF_SNaN_neg = 0xffb00000; +const uint32_t SF_HEX_NaN = 0xffffffff; +const uint32_t SF_zero = 0x00000000; +const uint32_t SF_one = 0x3f800000; +const uint32_t SF_one_recip = 0x3f7f0001; /* 0.9960... */ +const uint32_t SF_one_invsqrta = 0x3f7f0000; /* 0.99609375 */ +const uint32_t SF_two = 0x40000000; +const uint32_t SF_four = 0x40800000; +const uint32_t SF_small_neg = 0xab98fba8; +const uint32_t SF_large_pos = 0x5afa572e; + +const uint64_t DF_QNaN = 0x7ff8000000000000ULL; +const uint64_t DF_SNaN = 0x7ff7000000000000ULL; +const uint64_t DF_QNaN_neg = 0xfff8000000000000ULL; +const uint64_t DF_SNaN_neg = 0xfff7000000000000ULL; +const uint64_t DF_HEX_NaN = 0xffffffffffffffffULL; +const uint64_t DF_zero = 0x0000000000000000ULL; +const uint64_t DF_any = 0x3f80000000000000ULL; +const uint64_t DF_one = 0x3ff0000000000000ULL; +const uint64_t DF_one_hh = 0x3ff001ff80000000ULL; /* 1.00048... */ +const uint64_t DF_small_neg = 0xbd731f7500000000ULL; +const uint64_t DF_large_pos = 0x7f80000000000001ULL; + +/* + * Templates for functions to execute an instruction + * + * The templates vary by the number of arguments and the types of the args + * and result. We use one letter in the macro name for the result and each + * argument: + * x unknown (specified in a subsequent template) or don't care + * R register (32 bits) + * P pair (64 bits) + * p predicate + * I immediate + * Xx read/write + */ + +/* Clear bits 0-5 in USR */ +#define CLEAR_USRBITS \ + "r2 = usr\n\t" \ + "r2 = and(r2, #0xffffffc0)\n\t" \ + "usr = r2\n\t" + +/* Template for instructions with one register operand */ +#define FUNC_x_OP_x(RESTYPE, SRCTYPE, NAME, INSN) \ +static RESTYPE NAME(SRCTYPE src, uint32_t *usr_result) \ +{ \ + RESTYPE result; \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%1 = usr\n\t" \ + : "=r"(result), "=r"(usr) \ + : "r"(src) \ + : "r2", "usr"); \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_R_OP_R(NAME, INSN) \ +FUNC_x_OP_x(uint32_t, uint32_t, NAME, INSN) + +#define FUNC_R_OP_P(NAME, INSN) \ +FUNC_x_OP_x(uint32_t, uint64_t, NAME, INSN) + +#define FUNC_P_OP_P(NAME, INSN) \ +FUNC_x_OP_x(uint64_t, uint64_t, NAME, INSN) + +#define FUNC_P_OP_R(NAME, INSN) \ +FUNC_x_OP_x(uint64_t, uint32_t, NAME, INSN) + +/* + * Template for instructions with a register and predicate result + * and one register operand + */ +#define FUNC_xp_OP_x(RESTYPE, SRCTYPE, NAME, INSN) \ +static RESTYPE NAME(SRCTYPE src, uint8_t *pred_result, uint32_t *usr_result) \ +{ \ + RESTYPE result; \ + uint8_t pred; \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%1 = p2\n\t" \ + "%2 = usr\n\t" \ + : "=r"(result), "=r"(pred), "=r"(usr) \ + : "r"(src) \ + : "r2", "p2", "usr"); \ + *pred_result = pred; \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_Rp_OP_R(NAME, INSN) \ +FUNC_xp_OP_x(uint32_t, uint32_t, NAME, INSN) + +/* Template for instructions with two register operands */ +#define FUNC_x_OP_xx(RESTYPE, SRC1TYPE, SRC2TYPE, NAME, INSN) \ +static RESTYPE NAME(SRC1TYPE src1, SRC2TYPE src2, uint32_t *usr_result) \ +{ \ + RESTYPE result; \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%1 = usr\n\t" \ + : "=r"(result), "=r"(usr) \ + : "r"(src1), "r"(src2) \ + : "r2", "usr"); \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_P_OP_PP(NAME, INSN) \ +FUNC_x_OP_xx(uint64_t, uint64_t, uint64_t, NAME, INSN) + +#define FUNC_R_OP_PP(NAME, INSN) \ +FUNC_x_OP_xx(uint32_t, uint64_t, uint64_t, NAME, INSN) + +#define FUNC_P_OP_RR(NAME, INSN) \ +FUNC_x_OP_xx(uint64_t, uint32_t, uint32_t, NAME, INSN) + +#define FUNC_R_OP_RR(NAME, INSN) \ +FUNC_x_OP_xx(uint32_t, uint32_t, uint32_t, NAME, INSN) + +#define FUNC_R_OP_PR(NAME, INSN) \ +FUNC_x_OP_xx(uint32_t, uint64_t, uint32_t, NAME, INSN) + +#define FUNC_P_OP_PR(NAME, INSN) \ +FUNC_x_OP_xx(uint64_t, uint64_t, uint32_t, NAME, INSN) + +/* + * Template for instructions with a register and predicate result + * and two register operands + */ +#define FUNC_xp_OP_xx(RESTYPE, SRC1TYPE, SRC2TYPE, NAME, INSN) \ +static RESTYPE NAME(SRC1TYPE src1, SRC2TYPE src2, \ + uint8_t *pred_result, uint32_t *usr_result) \ +{ \ + RESTYPE result; \ + uint8_t pred; \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%1 = p2\n\t" \ + "%2 = usr\n\t" \ + : "=r"(result), "=r"(pred), "=r"(usr) \ + : "r"(src1), "r"(src2) \ + : "r2", "p2", "usr"); \ + *pred_result = pred; \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_Rp_OP_RR(NAME, INSN) \ +FUNC_xp_OP_xx(uint32_t, uint32_t, uint32_t, NAME, INSN) + +/* Template for instructions with one register and one immediate */ +#define FUNC_x_OP_xI(RESTYPE, SRC1TYPE, NAME, INSN) \ +static RESTYPE NAME(SRC1TYPE src1, int32_t src2, uint32_t *usr_result) \ +{ \ + RESTYPE result; \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%1 = usr\n\t" \ + : "=r"(result), "=r"(usr) \ + : "r"(src1), "i"(src2) \ + : "r2", "usr"); \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_R_OP_RI(NAME, INSN) \ +FUNC_x_OP_xI(uint32_t, uint32_t, NAME, INSN) + +#define FUNC_R_OP_PI(NAME, INSN) \ +FUNC_x_OP_xI(uint32_t, uint64_t, NAME, INSN) + +/* + * Template for instructions with a read/write result + * and two register operands + */ +#define FUNC_Xx_OP_xx(RESTYPE, SRC1TYPE, SRC2TYPE, NAME, INSN) \ +static RESTYPE NAME(RESTYPE result, SRC1TYPE src1, SRC2TYPE src2, \ + uint32_t *usr_result) \ +{ \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%1 = usr\n\t" \ + : "+r"(result), "=r"(usr) \ + : "r"(src1), "r"(src2) \ + : "r2", "usr"); \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_XR_OP_RR(NAME, INSN) \ +FUNC_Xx_OP_xx(uint32_t, uint32_t, uint32_t, NAME, INSN) + +#define FUNC_XP_OP_PP(NAME, INSN) \ +FUNC_Xx_OP_xx(uint64_t, uint64_t, uint64_t, NAME, INSN) + +#define FUNC_XP_OP_RR(NAME, INSN) \ +FUNC_Xx_OP_xx(uint64_t, uint32_t, uint32_t, NAME, INSN) + +/* + * Template for instructions with a read/write result + * and two register operands + */ +#define FUNC_Xxp_OP_xx(RESTYPE, SRC1TYPE, SRC2TYPE, NAME, INSN) \ +static RESTYPE NAME(RESTYPE result, SRC1TYPE src1, SRC2TYPE src2, \ + uint8_t *pred_result, uint32_t *usr_result) \ +{ \ + uint32_t usr; \ + uint8_t pred; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%1 = p2\n\t" \ + "%2 = usr\n\t" \ + : "+r"(result), "=r"(pred), "=r"(usr) \ + : "r"(src1), "r"(src2) \ + : "r2", "usr"); \ + *pred_result = pred; \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_XPp_OP_PP(NAME, INSN) \ +FUNC_Xxp_OP_xx(uint64_t, uint64_t, uint64_t, NAME, INSN) + +/* + * Template for instructions with a read/write result and + * two register and one predicate operands + */ +#define FUNC_Xx_OP_xxp(RESTYPE, SRC1TYPE, SRC2TYPE, NAME, INSN) \ +static RESTYPE NAME(RESTYPE result, SRC1TYPE src1, SRC2TYPE src2, uint8_t pred,\ + uint32_t *usr_result) \ +{ \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + "p2 = %4\n\t" \ + INSN "\n\t" \ + "%1 = usr\n\t" \ + : "+r"(result), "=r"(usr) \ + : "r"(src1), "r"(src2), "r"(pred) \ + : "r2", "p2", "usr"); \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_XR_OP_RRp(NAME, INSN) \ +FUNC_Xx_OP_xxp(uint32_t, uint32_t, uint32_t, NAME, INSN) + +/* Template for compare instructions with two register operands */ +#define FUNC_CMP_xx(SRC1TYPE, SRC2TYPE, NAME, INSN) \ +static uint32_t NAME(SRC1TYPE src1, SRC2TYPE src2, uint32_t *usr_result) \ +{ \ + uint32_t result; \ + uint32_t usr; \ + asm(CLEAR_USRBITS \ + INSN "\n\t" \ + "%0 = p1\n\t" \ + "%1 = usr\n\t" \ + : "=r"(result), "=r"(usr) \ + : "r"(src1), "r"(src2) \ + : "p1", "r2", "usr"); \ + *usr_result = usr & 0x3f; \ + return result; \ +} + +#define FUNC_CMP_RR(NAME, INSN) \ +FUNC_CMP_xx(uint32_t, uint32_t, NAME, INSN) + +#define FUNC_CMP_PP(NAME, INSN) \ +FUNC_CMP_xx(uint64_t, uint64_t, NAME, INSN) + +/* + * Function declarations using the templates + */ +FUNC_R_OP_R(satub, "%0 = satub(%2)") +FUNC_P_OP_PP(vaddubs, "%0 = vaddub(%2, %3):sat") +FUNC_P_OP_PP(vadduhs, "%0 = vadduh(%2, %3):sat") +FUNC_P_OP_PP(vsububs, "%0 = vsubub(%2, %3):sat") +FUNC_P_OP_PP(vsubuhs, "%0 = vsubuh(%2, %3):sat") + +/* Add vector of half integers with saturation and pack to unsigned bytes */ +FUNC_R_OP_PP(vaddhubs, "%0 = vaddhub(%2, %3):sat") + +/* Vector saturate half to unsigned byte */ +FUNC_R_OP_P(vsathub, "%0 = vsathub(%2)") + +/* Similar to above but takes a 32-bit argument */ +FUNC_R_OP_R(svsathub, "%0 = vsathub(%2)") + +/* Vector saturate word to unsigned half */ +FUNC_P_OP_P(vsatwuh_nopack, "%0 = vsatwuh(%2)") + +/* Similar to above but returns a 32-bit result */ +FUNC_R_OP_P(vsatwuh, "%0 = vsatwuh(%2)") + +/* Vector arithmetic shift halfwords with saturate and pack */ +FUNC_R_OP_PI(asrhub_sat, "%0 = vasrhub(%2, #%3):sat") + +/* Vector arithmetic shift halfwords with round, saturate and pack */ +FUNC_R_OP_PI(asrhub_rnd_sat, "%0 = vasrhub(%2, #%3):raw") + +FUNC_R_OP_RR(addsat, "%0 = add(%2, %3):sat") +/* Similar to above but with register pairs */ +FUNC_P_OP_PP(addpsat, "%0 = add(%2, %3):sat") + +FUNC_XR_OP_RR(mpy_acc_sat_hh_s0, "%0 += mpy(%2.H, %3.H):sat") +FUNC_R_OP_RR(mpy_sat_hh_s1, "%0 = mpy(%2.H, %3.H):<<1:sat") +FUNC_R_OP_RR(mpy_sat_rnd_hh_s1, "%0 = mpy(%2.H, %3.H):<<1:rnd:sat") +FUNC_R_OP_RR(mpy_up_s1_sat, "%0 = mpy(%2, %3):<<1:sat") +FUNC_P_OP_RR(vmpy2s_s1, "%0 = vmpyh(%2, %3):<<1:sat") +FUNC_P_OP_RR(vmpy2su_s1, "%0 = vmpyhsu(%2, %3):<<1:sat") +FUNC_R_OP_RR(vmpy2s_s1pack, "%0 = vmpyh(%2, %3):<<1:rnd:sat") +FUNC_P_OP_PP(vmpy2es_s1, "%0 = vmpyeh(%2, %3):<<1:sat") +FUNC_R_OP_PP(vdmpyrs_s1, "%0 = vdmpy(%2, %3):<<1:rnd:sat") +FUNC_XP_OP_PP(vdmacs_s0, "%0 += vdmpy(%2, %3):sat") +FUNC_R_OP_RR(cmpyrs_s0, "%0 = cmpy(%2, %3):rnd:sat") +FUNC_XP_OP_RR(cmacs_s0, "%0 += cmpy(%2, %3):sat") +FUNC_XP_OP_RR(cnacs_s0, "%0 -= cmpy(%2, %3):sat") +FUNC_P_OP_PP(vrcmpys_s1_h, "%0 = vrcmpys(%2, %3):<<1:sat:raw:hi") +FUNC_XP_OP_PP(mmacls_s0, "%0 += vmpyweh(%2, %3):sat") +FUNC_R_OP_RR(hmmpyl_rs1, "%0 = mpy(%2, %3.L):<<1:rnd:sat") +FUNC_XP_OP_PP(mmaculs_s0, "%0 += vmpyweuh(%2, %3):sat") +FUNC_R_OP_PR(cmpyi_wh, "%0 = cmpyiwh(%2, %3):<<1:rnd:sat") +FUNC_P_OP_PP(vcmpy_s0_sat_i, "%0 = vcmpyi(%2, %3):sat") +FUNC_P_OP_PR(vcrotate, "%0 = vcrotate(%2, %3)") +FUNC_P_OP_PR(vcnegh, "%0 = vcnegh(%2, %3)") + +#if CORE_HAS_AUDIO +FUNC_R_OP_PP(wcmpyrw, "%0 = cmpyrw(%2, %3):<<1:sat") +#endif + +FUNC_R_OP_RR(addh_l16_sat_ll, "%0 = add(%2.L, %3.L):sat") +FUNC_P_OP_P(vconj, "%0 = vconj(%2):sat") +FUNC_P_OP_PP(vxaddsubw, "%0 = vxaddsubw(%2, %3):sat") +FUNC_P_OP_P(vabshsat, "%0 = vabsh(%2):sat") +FUNC_P_OP_PP(vnavgwr, "%0 = vnavgw(%2, %3):rnd:sat") +FUNC_R_OP_RI(round_ri_sat, "%0 = round(%2, #%3):sat") +FUNC_R_OP_RR(asr_r_r_sat, "%0 = asr(%2, %3):sat") + +FUNC_XPp_OP_PP(ACS, "%0, p2 = vacsh(%3, %4)") + +/* Floating point */ +FUNC_R_OP_RR(sfmin, "%0 = sfmin(%2, %3)") +FUNC_R_OP_RR(sfmax, "%0 = sfmax(%2, %3)") +FUNC_R_OP_RR(sfadd, "%0 = sfadd(%2, %3)") +FUNC_R_OP_RR(sfsub, "%0 = sfsub(%2, %3)") +FUNC_R_OP_RR(sfmpy, "%0 = sfmpy(%2, %3)") +FUNC_XR_OP_RR(sffma, "%0 += sfmpy(%2, %3)") +FUNC_XR_OP_RR(sffms, "%0 -= sfmpy(%2, %3)") +FUNC_CMP_RR(sfcmpuo, "p1 = sfcmp.uo(%2, %3)") +FUNC_CMP_RR(sfcmpeq, "p1 = sfcmp.eq(%2, %3)") +FUNC_CMP_RR(sfcmpgt, "p1 = sfcmp.gt(%2, %3)") +FUNC_CMP_RR(sfcmpge, "p1 = sfcmp.ge(%2, %3)") + +FUNC_P_OP_PP(dfadd, "%0 = dfadd(%2, %3)") +FUNC_P_OP_PP(dfsub, "%0 = dfsub(%2, %3)") + +#if CORE_IS_V67 +FUNC_P_OP_PP(dfmin, "%0 = dfmin(%2, %3)") +FUNC_P_OP_PP(dfmax, "%0 = dfmax(%2, %3)") +FUNC_XP_OP_PP(dfmpyhh, "%0 += dfmpyhh(%2, %3)") +#endif + +FUNC_CMP_PP(dfcmpuo, "p1 = dfcmp.uo(%2, %3)") +FUNC_CMP_PP(dfcmpeq, "p1 = dfcmp.eq(%2, %3)") +FUNC_CMP_PP(dfcmpgt, "p1 = dfcmp.gt(%2, %3)") +FUNC_CMP_PP(dfcmpge, "p1 = dfcmp.ge(%2, %3)") + +/* Conversions from sf */ +FUNC_P_OP_R(conv_sf2df, "%0 = convert_sf2df(%2)") +FUNC_R_OP_R(conv_sf2uw, "%0 = convert_sf2uw(%2)") +FUNC_R_OP_R(conv_sf2w, "%0 = convert_sf2w(%2)") +FUNC_P_OP_R(conv_sf2ud, "%0 = convert_sf2ud(%2)") +FUNC_P_OP_R(conv_sf2d, "%0 = convert_sf2d(%2)") +FUNC_R_OP_R(conv_sf2uw_chop, "%0 = convert_sf2uw(%2):chop") +FUNC_R_OP_R(conv_sf2w_chop, "%0 = convert_sf2w(%2):chop") +FUNC_P_OP_R(conv_sf2ud_chop, "%0 = convert_sf2ud(%2):chop") +FUNC_P_OP_R(conv_sf2d_chop, "%0 = convert_sf2d(%2):chop") + +/* Conversions from df */ +FUNC_R_OP_P(conv_df2sf, "%0 = convert_df2sf(%2)") +FUNC_R_OP_P(conv_df2uw, "%0 = convert_df2uw(%2)") +FUNC_R_OP_P(conv_df2w, "%0 = convert_df2w(%2)") +FUNC_P_OP_P(conv_df2ud, "%0 = convert_df2ud(%2)") +FUNC_P_OP_P(conv_df2d, "%0 = convert_df2d(%2)") +FUNC_R_OP_P(conv_df2uw_chop, "%0 = convert_df2uw(%2):chop") +FUNC_R_OP_P(conv_df2w_chop, "%0 = convert_df2w(%2):chop") +FUNC_P_OP_P(conv_df2ud_chop, "%0 = convert_df2ud(%2):chop") +FUNC_P_OP_P(conv_df2d_chop, "%0 = convert_df2d(%2):chop") + +/* Integer to float conversions */ +FUNC_R_OP_R(conv_uw2sf, "%0 = convert_uw2sf(%2)") +FUNC_R_OP_R(conv_w2sf, "%0 = convert_w2sf(%2)") +FUNC_R_OP_P(conv_ud2sf, "%0 = convert_ud2sf(%2)") +FUNC_R_OP_P(conv_d2sf, "%0 = convert_d2sf(%2)") + +/* Special purpose floating point instructions */ +FUNC_XR_OP_RRp(sffma_sc, "%0 += sfmpy(%2, %3, p2):scale") +FUNC_Rp_OP_RR(sfrecipa, "%0, p2 = sfrecipa(%3, %4)") +FUNC_R_OP_RR(sffixupn, "%0 = sffixupn(%2, %3)") +FUNC_R_OP_RR(sffixupd, "%0 = sffixupd(%2, %3)") +FUNC_R_OP_R(sffixupr, "%0 = sffixupr(%2)") +FUNC_Rp_OP_R(sfinvsqrta, "%0, p2 = sfinvsqrta(%3)") + +/* + * Templates for test cases + * + * Same naming convention as the function templates + */ +#define TEST_x_OP_x(RESTYPE, CHECKFN, SRCTYPE, FUNC, SRC, RES, USR_RES) \ + do { \ + RESTYPE result; \ + SRCTYPE src = SRC; \ + uint32_t usr_result; \ + result = FUNC(src, &usr_result); \ + CHECKFN(result, RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_R_OP_R(FUNC, SRC, RES, USR_RES) \ +TEST_x_OP_x(uint32_t, check32, uint32_t, FUNC, SRC, RES, USR_RES) + +#define TEST_R_OP_P(FUNC, SRC, RES, USR_RES) \ +TEST_x_OP_x(uint32_t, check32, uint64_t, FUNC, SRC, RES, USR_RES) + +#define TEST_P_OP_P(FUNC, SRC, RES, USR_RES) \ +TEST_x_OP_x(uint64_t, check64, uint64_t, FUNC, SRC, RES, USR_RES) + +#define TEST_P_OP_R(FUNC, SRC, RES, USR_RES) \ +TEST_x_OP_x(uint64_t, check64, uint32_t, FUNC, SRC, RES, USR_RES) + +#define TEST_xp_OP_x(RESTYPE, CHECKFN, SRCTYPE, FUNC, SRC, \ + RES, PRED_RES, USR_RES) \ + do { \ + RESTYPE result; \ + SRCTYPE src = SRC; \ + uint8_t pred_result; \ + uint32_t usr_result; \ + result = FUNC(src, &pred_result, &usr_result); \ + CHECKFN(result, RES); \ + check(pred_result, PRED_RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_Rp_OP_R(FUNC, SRC, RES, PRED_RES, USR_RES) \ +TEST_xp_OP_x(uint32_t, check32, uint32_t, FUNC, SRC, RES, PRED_RES, USR_RES) + +#define TEST_x_OP_xx(RESTYPE, CHECKFN, SRC1TYPE, SRC2TYPE, \ + FUNC, SRC1, SRC2, RES, USR_RES) \ + do { \ + RESTYPE result; \ + SRC1TYPE src1 = SRC1; \ + SRC2TYPE src2 = SRC2; \ + uint32_t usr_result; \ + result = FUNC(src1, src2, &usr_result); \ + CHECKFN(result, RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_P_OP_PP(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xx(uint64_t, check64, uint64_t, uint64_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_R_OP_PP(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xx(uint32_t, check32, uint64_t, uint64_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_P_OP_RR(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xx(uint64_t, check64, uint32_t, uint32_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_R_OP_RR(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xx(uint32_t, check32, uint32_t, uint32_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_R_OP_PR(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xx(uint32_t, check32, uint64_t, uint32_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_P_OP_PR(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xx(uint64_t, check64, uint64_t, uint32_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_xp_OP_xx(RESTYPE, CHECKFN, SRC1TYPE, SRC2TYPE, FUNC, SRC1, SRC2, \ + RES, PRED_RES, USR_RES) \ + do { \ + RESTYPE result; \ + SRC1TYPE src1 = SRC1; \ + SRC2TYPE src2 = SRC2; \ + uint8_t pred_result; \ + uint32_t usr_result; \ + result = FUNC(src1, src2, &pred_result, &usr_result); \ + CHECKFN(result, RES); \ + check(pred_result, PRED_RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_Rp_OP_RR(FUNC, SRC1, SRC2, RES, PRED_RES, USR_RES) \ +TEST_xp_OP_xx(uint32_t, check32, uint32_t, uint32_t, FUNC, SRC1, SRC2, \ + RES, PRED_RES, USR_RES) + +#define TEST_x_OP_xI(RESTYPE, CHECKFN, SRC1TYPE, \ + FUNC, SRC1, SRC2, RES, USR_RES) \ + do { \ + RESTYPE result; \ + SRC1TYPE src1 = SRC1; \ + uint32_t src2 = SRC2; \ + uint32_t usr_result; \ + result = FUNC(src1, src2, &usr_result); \ + CHECKFN(result, RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_R_OP_RI(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xI(uint32_t, check32, uint32_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_R_OP_PI(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_x_OP_xI(uint32_t, check64, uint64_t, \ + FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_Xx_OP_xx(RESTYPE, CHECKFN, SRC1TYPE, SRC2TYPE, \ + FUNC, RESIN, SRC1, SRC2, RES, USR_RES) \ + do { \ + RESTYPE result = RESIN; \ + SRC1TYPE src1 = SRC1; \ + SRC2TYPE src2 = SRC2; \ + uint32_t usr_result; \ + result = FUNC(result, src1, src2, &usr_result); \ + CHECKFN(result, RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_XR_OP_RR(FUNC, RESIN, SRC1, SRC2, RES, USR_RES) \ +TEST_Xx_OP_xx(uint32_t, check32, uint32_t, uint32_t, \ + FUNC, RESIN, SRC1, SRC2, RES, USR_RES) + +#define TEST_XP_OP_PP(FUNC, RESIN, SRC1, SRC2, RES, USR_RES) \ +TEST_Xx_OP_xx(uint64_t, check64, uint64_t, uint64_t, \ + FUNC, RESIN, SRC1, SRC2, RES, USR_RES) + +#define TEST_XP_OP_RR(FUNC, RESIN, SRC1, SRC2, RES, USR_RES) \ +TEST_Xx_OP_xx(uint64_t, check64, uint32_t, uint32_t, \ + FUNC, RESIN, SRC1, SRC2, RES, USR_RES) + +#define TEST_Xxp_OP_xx(RESTYPE, CHECKFN, SRC1TYPE, SRC2TYPE, \ + FUNC, RESIN, SRC1, SRC2, RES, PRED_RES, USR_RES) \ + do { \ + RESTYPE result = RESIN; \ + SRC1TYPE src1 = SRC1; \ + SRC2TYPE src2 = SRC2; \ + uint8_t pred_res; \ + uint32_t usr_result; \ + result = FUNC(result, src1, src2, &pred_res, &usr_result); \ + CHECKFN(result, RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_XPp_OP_PP(FUNC, RESIN, SRC1, SRC2, RES, PRED_RES, USR_RES) \ +TEST_Xxp_OP_xx(uint64_t, check64, uint64_t, uint64_t, FUNC, RESIN, SRC1, SRC2, \ + RES, PRED_RES, USR_RES) + +#define TEST_Xx_OP_xxp(RESTYPE, CHECKFN, SRC1TYPE, SRC2TYPE, \ + FUNC, RESIN, SRC1, SRC2, PRED, RES, USR_RES) \ + do { \ + RESTYPE result = RESIN; \ + SRC1TYPE src1 = SRC1; \ + SRC2TYPE src2 = SRC2; \ + uint8_t pred = PRED; \ + uint32_t usr_result; \ + result = FUNC(result, src1, src2, pred, &usr_result); \ + CHECKFN(result, RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_XR_OP_RRp(FUNC, RESIN, SRC1, SRC2, PRED, RES, USR_RES) \ +TEST_Xx_OP_xxp(uint32_t, check32, uint32_t, uint32_t, \ + FUNC, RESIN, SRC1, SRC2, PRED, RES, USR_RES) + +#define TEST_CMP_xx(SRC1TYPE, SRC2TYPE, \ + FUNC, SRC1, SRC2, RES, USR_RES) \ + do { \ + uint32_t result; \ + SRC1TYPE src1 = SRC1; \ + SRC2TYPE src2 = SRC2; \ + uint32_t usr_result; \ + result = FUNC(src1, src2, &usr_result); \ + check(result, RES); \ + check(usr_result, USR_RES); \ + } while (0) + +#define TEST_CMP_RR(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_CMP_xx(uint32_t, uint32_t, FUNC, SRC1, SRC2, RES, USR_RES) + +#define TEST_CMP_PP(FUNC, SRC1, SRC2, RES, USR_RES) \ +TEST_CMP_xx(uint64_t, uint64_t, FUNC, SRC1, SRC2, RES, USR_RES) + +int main() +{ + TEST_R_OP_R(satub, 0, 0, USR_CLEAR); + TEST_R_OP_R(satub, 0xff, 0xff, USR_CLEAR); + TEST_R_OP_R(satub, 0xfff, 0xff, USR_OVF); + TEST_R_OP_R(satub, -1, 0, USR_OVF); + + TEST_P_OP_PP(vaddubs, 0xfeLL, 0x01LL, 0xffLL, USR_CLEAR); + TEST_P_OP_PP(vaddubs, 0xffLL, 0xffLL, 0xffLL, USR_OVF); + + TEST_P_OP_PP(vadduhs, 0xfffeLL, 0x1LL, 0xffffLL, USR_CLEAR); + TEST_P_OP_PP(vadduhs, 0xffffLL, 0x1LL, 0xffffLL, USR_OVF); + + TEST_P_OP_PP(vsububs, 0x0807060504030201LL, 0x0101010101010101LL, + 0x0706050403020100LL, USR_CLEAR); + TEST_P_OP_PP(vsububs, 0x0807060504030201LL, 0x0202020202020202LL, + 0x0605040302010000LL, USR_OVF); + + TEST_P_OP_PP(vsubuhs, 0x0004000300020001LL, 0x0001000100010001LL, + 0x0003000200010000LL, USR_CLEAR); + TEST_P_OP_PP(vsubuhs, 0x0004000300020001LL, 0x0002000200020002LL, + 0x0002000100000000LL, USR_OVF); + + TEST_R_OP_PP(vaddhubs, 0x0004000300020001LL, 0x0001000100010001LL, + 0x05040302, USR_CLEAR); + TEST_R_OP_PP(vaddhubs, 0x7fff000300020001LL, 0x0002000200020002LL, + 0xff050403, USR_OVF); + + TEST_R_OP_P(vsathub, 0x0001000300020001LL, 0x01030201, USR_CLEAR); + TEST_R_OP_P(vsathub, 0x010000700080ffffLL, 0xff708000, USR_OVF); + + TEST_R_OP_P(vsatwuh, 0x0000ffff00000001LL, 0xffff0001, USR_CLEAR); + TEST_R_OP_P(vsatwuh, 0x800000000000ffffLL, 0x0000ffff, USR_OVF); + + TEST_P_OP_P(vsatwuh_nopack, 0x0000ffff00000001LL, 0x0000ffff00000001LL, + USR_CLEAR); + TEST_P_OP_P(vsatwuh_nopack, 0x800000000000ffffLL, 0x000000000000ffffLL, + USR_OVF); + + TEST_R_OP_R(svsathub, 0x00020001, 0x0201, USR_CLEAR); + TEST_R_OP_R(svsathub, 0x0080ffff, 0x8000, USR_OVF); + + TEST_R_OP_PI(asrhub_sat, 0x004f003f002f001fLL, 3, 0x09070503, + USR_CLEAR); + TEST_R_OP_PI(asrhub_sat, 0x004fffff8fff001fLL, 3, 0x09000003, + USR_OVF); + + TEST_R_OP_PI(asrhub_rnd_sat, 0x004f003f002f001fLL, 2, 0x0a080604, + USR_CLEAR); + TEST_R_OP_PI(asrhub_rnd_sat, 0x004fffff8fff001fLL, 2, 0x0a000004, + USR_OVF); + + TEST_R_OP_RR(addsat, 1, 2, 3, + USR_CLEAR); + TEST_R_OP_RR(addsat, 0x7fffffff, 0x00000010, 0x7fffffff, + USR_OVF); + TEST_R_OP_RR(addsat, 0x80000000, 0x80000006, 0x80000000, + USR_OVF); + + TEST_P_OP_PP(addpsat, 1LL, 2LL, 3LL, USR_CLEAR); + /* overflow to max positive */ + TEST_P_OP_PP(addpsat, 0x7ffffffffffffff0LL, 0x0000000000000010LL, + 0x7fffffffffffffffLL, USR_OVF); + /* overflow to min negative */ + TEST_P_OP_PP(addpsat, 0x8000000000000003LL, 0x8000000000000006LL, + 0x8000000000000000LL, USR_OVF); + + TEST_XR_OP_RR(mpy_acc_sat_hh_s0, 0x7fffffff, 0xffff0000, 0x11110000, + 0x7fffeeee, USR_CLEAR); + TEST_XR_OP_RR(mpy_acc_sat_hh_s0, 0x7fffffff, 0x7fff0000, 0x7fff0000, + 0x7fffffff, USR_OVF); + + TEST_R_OP_RR(mpy_sat_hh_s1, 0xffff0000, 0x11110000, 0xffffddde, + USR_CLEAR); + TEST_R_OP_RR(mpy_sat_hh_s1, 0x7fff0000, 0x7fff0000, 0x7ffe0002, + USR_CLEAR); + TEST_R_OP_RR(mpy_sat_hh_s1, 0x80000000, 0x80000000, 0x7fffffff, + USR_OVF); + + TEST_R_OP_RR(mpy_sat_rnd_hh_s1, 0xffff0000, 0x11110000, 0x00005dde, + USR_CLEAR); + TEST_R_OP_RR(mpy_sat_rnd_hh_s1, 0x7fff0000, 0x7fff0000, 0x7ffe8002, + USR_CLEAR); + TEST_R_OP_RR(mpy_sat_rnd_hh_s1, 0x80000000, 0x80000000, 0x7fffffff, + USR_OVF); + + TEST_R_OP_RR(mpy_up_s1_sat, 0xffff0000, 0x11110000, 0xffffddde, + USR_CLEAR); + TEST_R_OP_RR(mpy_up_s1_sat, 0x7fff0000, 0x7fff0000, 0x7ffe0002, + USR_CLEAR); + TEST_R_OP_RR(mpy_up_s1_sat, 0x80000000, 0x80000000, 0x7fffffff, + USR_OVF); + + TEST_P_OP_RR(vmpy2s_s1, 0x7fff0000, 0x7fff0000, 0x7ffe000200000000LL, + USR_CLEAR); + TEST_P_OP_RR(vmpy2s_s1, 0x80000000, 0x80000000, 0x7fffffff00000000LL, + USR_OVF); + + TEST_P_OP_RR(vmpy2su_s1, 0x7fff0000, 0x7fff0000, 0x7ffe000200000000LL, + USR_CLEAR); + TEST_P_OP_RR(vmpy2su_s1, 0xffffbd97, 0xffffffff, 0xfffe000280000000LL, + USR_OVF); + + TEST_R_OP_RR(vmpy2s_s1pack, 0x7fff0000, 0x7fff0000, 0x7ffe0000, + USR_CLEAR); + TEST_R_OP_RR(vmpy2s_s1pack, 0x80008000, 0x80008000, 0x7fff7fff, + USR_OVF); + + TEST_P_OP_PP(vmpy2es_s1, 0x7fff7fff7fff7fffLL, 0x1fff1fff1fff1fffLL, + 0x1ffec0021ffec002LL, USR_CLEAR); + TEST_P_OP_PP(vmpy2es_s1, 0x8000800080008000LL, 0x8000800080008000LL, + 0x7fffffff7fffffffLL, USR_OVF); + + TEST_R_OP_PP(vdmpyrs_s1, 0x7fff7fff7fff7fffLL, 0x1fff1fff1fff1fffLL, + 0x3ffe3ffe, USR_CLEAR); + TEST_R_OP_PP(vdmpyrs_s1, 0x8000800080008000LL, 0x8000800080008000LL, + 0x7fff7fffLL, USR_OVF); + + TEST_XP_OP_PP(vdmacs_s0, 0x0fffffffULL, 0x00ff00ff00ff00ffLL, + 0x00ff00ff00ff00ffLL, 0x0001fc021001fc01LL, USR_CLEAR); + TEST_XP_OP_PP(vdmacs_s0, 0x01111111ULL, 0x8000800080001000LL, + 0x8000800080008000LL, 0x7fffffff39111111LL, USR_OVF); + + TEST_R_OP_RR(cmpyrs_s0, 0x7fff0000, 0x7fff0000, 0x0000c001, + USR_CLEAR); + TEST_R_OP_RR(cmpyrs_s0, 0x80008000, 0x80008000, 0x7fff0000, + USR_OVF); + + TEST_XP_OP_RR(cmacs_s0, 0x0fffffff, 0x7fff0000, 0x7fff0000, + 0x00000000d000fffeLL, USR_CLEAR); + TEST_XP_OP_RR(cmacs_s0, 0x0fff1111, 0x80008000, 0x80008000, + 0x7fffffff0fff1111LL, USR_OVF); + + TEST_XP_OP_RR(cnacs_s0, 0x000000108fffffffULL, 0x7fff0000, 0x7fff0000, + 0x00000010cfff0000ULL, USR_CLEAR); + TEST_XP_OP_RR(cnacs_s0, 0x000000108ff1111fULL, 0x00002001, 0x00007ffd, + 0x0000001080000000ULL, USR_OVF); + + TEST_P_OP_PP(vrcmpys_s1_h, 0x00ff00ff00ff00ffLL, 0x00ff00ff00ff00ffLL, + 0x0003f8040003f804LL, USR_CLEAR); + TEST_P_OP_PP(vrcmpys_s1_h, 0x8000800080008000LL, 0x8000800080008000LL, + 0x7fffffff7fffffffLL, USR_OVF); + + TEST_XP_OP_PP(mmacls_s0, 0x6fffffff, 0x00ff00ff00ff00ffLL, + 0x00ff00ff00ff00ffLL, 0x0000fe017000fe00LL, USR_CLEAR); + TEST_XP_OP_PP(mmacls_s0, 0x6f1111ff, 0x8000800080008000LL, + 0x1000100080008000LL, 0xf80008007fffffffLL, USR_OVF); + + TEST_R_OP_RR(hmmpyl_rs1, 0x7fff0000, 0x7fff0001, 0x0000fffe, + USR_CLEAR); + TEST_R_OP_RR(hmmpyl_rs1, 0x80000000, 0x80008000, 0x7fffffff, + USR_OVF); + + TEST_XP_OP_PP(mmaculs_s0, 0x000000007fffffffULL, 0xffff800080008000LL, + 0xffff800080008000LL, 0xffffc00040003fffLL, USR_CLEAR); + TEST_XP_OP_PP(mmaculs_s0, 0x000011107fffffffULL, 0x00ff00ff00ff00ffLL, + 0x00ff00ff001100ffLL, 0x00010f117fffffffLL, USR_OVF); + + TEST_R_OP_PR(cmpyi_wh, 0x7fff000000000000LL, 0x7fff0001, 0x0000fffe, + USR_CLEAR); + TEST_R_OP_PR(cmpyi_wh, 0x8000000000000000LL, 0x80008000, 0x7fffffff, + USR_OVF); + + TEST_P_OP_PP(vcmpy_s0_sat_i, 0x00ff00ff00ff00ffLL, 0x00ff00ff00ff00ffLL, + 0x0001fc020001fc02LL, USR_CLEAR); + TEST_P_OP_PP(vcmpy_s0_sat_i, 0x8000800080008000LL, 0x8000800080008000LL, + 0x7fffffff7fffffffLL, USR_OVF); + + TEST_P_OP_PR(vcrotate, 0x8000000000000000LL, 0x00000002, + 0x8000000000000000LL, USR_CLEAR); + TEST_P_OP_PR(vcrotate, 0x7fff80007fff8000LL, 0x00000001, + 0x7fff80007fff7fffLL, USR_OVF); + + TEST_P_OP_PR(vcnegh, 0x8000000000000000LL, 0x00000002, + 0x8000000000000000LL, USR_CLEAR); + TEST_P_OP_PR(vcnegh, 0x7fff80007fff8000LL, 0x00000001, + 0x7fff80007fff7fffLL, USR_OVF); + +#if CORE_HAS_AUDIO + TEST_R_OP_PP(wcmpyrw, 0x8765432101234567LL, 0x00000002ffffffffLL, + 0x00000001, USR_CLEAR); + TEST_R_OP_PP(wcmpyrw, 0x800000007fffffffLL, 0x000000ff7fffffffLL, + 0x7fffffff, USR_OVF); + TEST_R_OP_PP(wcmpyrw, 0x7fffffff80000000LL, 0x7fffffff000000ffLL, + 0x80000000, USR_OVF); +#else + printf("Audio instructions skipped\n"); +#endif + + TEST_R_OP_RR(addh_l16_sat_ll, 0x0000ffff, 0x00000002, 0x00000001, + USR_CLEAR); + TEST_R_OP_RR(addh_l16_sat_ll, 0x00007fff, 0x00000005, 0x00007fff, + USR_OVF); + TEST_R_OP_RR(addh_l16_sat_ll, 0x00008000, 0x00008000, 0xffff8000, + USR_OVF); + + TEST_P_OP_P(vconj, 0x0000ffff00000001LL, 0x0000ffff00000001LL, USR_CLEAR); + TEST_P_OP_P(vconj, 0x800000000000ffffLL, 0x7fff00000000ffffLL, USR_OVF); + + TEST_P_OP_PP(vxaddsubw, 0x8765432101234567LL, 0x00000002ffffffffLL, + 0x8765432201234569LL, USR_CLEAR); + TEST_P_OP_PP(vxaddsubw, 0x7fffffff7fffffffLL, 0xffffffffffffffffLL, + 0x7fffffff7ffffffeLL, USR_OVF); + TEST_P_OP_PP(vxaddsubw, 0x800000000fffffffLL, 0x0000000a00000008LL, + 0x8000000010000009LL, USR_OVF); + + TEST_P_OP_P(vabshsat, 0x0001000afffff800LL, 0x0001000a00010800LL, + USR_CLEAR); + TEST_P_OP_P(vabshsat, 0x8000000b000c000aLL, 0x7fff000b000c000aLL, + USR_OVF); + + TEST_P_OP_PP(vnavgwr, 0x8765432101234567LL, 0x00000002ffffffffLL, + 0xc3b2a1900091a2b4LL, USR_CLEAR); + TEST_P_OP_PP(vnavgwr, 0x7fffffff8000000aLL, 0x80000000ffffffffLL, + 0x7fffffffc0000006LL, USR_OVF); + + TEST_R_OP_RI(round_ri_sat, 0x0000ffff, 2, 0x00004000, USR_CLEAR); + TEST_R_OP_RI(round_ri_sat, 0x7fffffff, 2, 0x1fffffff, USR_OVF); + + TEST_R_OP_RR(asr_r_r_sat, 0x0000ffff, 0x00000002, 0x00003fff, + USR_CLEAR); + TEST_R_OP_RR(asr_r_r_sat, 0x00ffffff, 0xfffffff5, 0x7fffffff, + USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0xfffffff5, 0x80000000, + USR_OVF); + + TEST_XPp_OP_PP(ACS, 0x0004000300020001ULL, 0x0001000200030004ULL, + 0x0000000000000000ULL, 0x0004000300030004ULL, 0xf0, + USR_CLEAR); + TEST_XPp_OP_PP(ACS, 0x0004000300020001ULL, 0x0001000200030004ULL, + 0x000affff000d0000ULL, 0x000e0003000f0004ULL, 0xcc, + USR_CLEAR); + TEST_XPp_OP_PP(ACS, 0x00047fff00020001ULL, 0x00017fff00030004ULL, + 0x000a0fff000d0000ULL, 0x000e7fff000f0004ULL, 0xfc, + USR_OVF); + TEST_XPp_OP_PP(ACS, 0x00047fff00020001ULL, 0x00017fff00030004ULL, + 0x000a0fff000d0000ULL, 0x000e7fff000f0004ULL, 0xf0, + USR_OVF); + + /* Floating point */ + TEST_R_OP_RR(sfmin, SF_one, SF_small_neg, SF_small_neg, USR_CLEAR); + TEST_R_OP_RR(sfmin, SF_one, SF_SNaN, SF_one, USR_FPINVF); + TEST_R_OP_RR(sfmin, SF_SNaN, SF_one, SF_one, USR_FPINVF); + TEST_R_OP_RR(sfmin, SF_one, SF_QNaN, SF_one, USR_CLEAR); + TEST_R_OP_RR(sfmin, SF_QNaN, SF_one, SF_one, USR_CLEAR); + TEST_R_OP_RR(sfmin, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfmin, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_R_OP_RR(sfmax, SF_one, SF_small_neg, SF_one, USR_CLEAR); + TEST_R_OP_RR(sfmax, SF_one, SF_SNaN, SF_one, USR_FPINVF); + TEST_R_OP_RR(sfmax, SF_SNaN, SF_one, SF_one, USR_FPINVF); + TEST_R_OP_RR(sfmax, SF_one, SF_QNaN, SF_one, USR_CLEAR); + TEST_R_OP_RR(sfmax, SF_QNaN, SF_one, SF_one, USR_CLEAR); + TEST_R_OP_RR(sfmax, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfmax, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_R_OP_RR(sfadd, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_RR(sfadd, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfadd, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfadd, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_R_OP_RR(sfsub, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_RR(sfsub, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfsub, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfsub, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_R_OP_RR(sfmpy, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_RR(sfmpy, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfmpy, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfmpy, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_XR_OP_RR(sffma, SF_one, SF_one, SF_one, SF_two, USR_CLEAR); + TEST_XR_OP_RR(sffma, SF_zero, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_XR_OP_RR(sffma, SF_zero, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_XR_OP_RR(sffma, SF_zero, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_XR_OP_RR(sffma, SF_zero, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_XR_OP_RR(sffms, SF_one, SF_one, SF_one, SF_zero, USR_CLEAR); + TEST_XR_OP_RR(sffms, SF_zero, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_XR_OP_RR(sffms, SF_zero, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_XR_OP_RR(sffms, SF_zero, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_XR_OP_RR(sffms, SF_zero, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_CMP_RR(sfcmpuo, SF_one, SF_large_pos, 0x00, USR_CLEAR); + TEST_CMP_RR(sfcmpuo, SF_INF, SF_large_pos, 0x00, USR_CLEAR); + TEST_CMP_RR(sfcmpuo, SF_QNaN, SF_large_pos, 0xff, USR_CLEAR); + TEST_CMP_RR(sfcmpuo, SF_QNaN_neg, SF_large_pos, 0xff, USR_CLEAR); + TEST_CMP_RR(sfcmpuo, SF_SNaN, SF_large_pos, 0xff, USR_FPINVF); + TEST_CMP_RR(sfcmpuo, SF_SNaN_neg, SF_large_pos, 0xff, USR_FPINVF); + TEST_CMP_RR(sfcmpuo, SF_QNaN, SF_QNaN, 0xff, USR_CLEAR); + TEST_CMP_RR(sfcmpuo, SF_QNaN, SF_SNaN, 0xff, USR_FPINVF); + + TEST_CMP_RR(sfcmpeq, SF_one, SF_QNaN, 0x00, USR_CLEAR); + TEST_CMP_RR(sfcmpeq, SF_one, SF_SNaN, 0x00, USR_FPINVF); + TEST_CMP_RR(sfcmpgt, SF_one, SF_QNaN, 0x00, USR_CLEAR); + TEST_CMP_RR(sfcmpgt, SF_one, SF_SNaN, 0x00, USR_FPINVF); + TEST_CMP_RR(sfcmpge, SF_one, SF_QNaN, 0x00, USR_CLEAR); + TEST_CMP_RR(sfcmpge, SF_one, SF_SNaN, 0x00, USR_FPINVF); + + TEST_P_OP_PP(dfadd, DF_any, DF_QNaN, DF_HEX_NaN, USR_CLEAR); + TEST_P_OP_PP(dfadd, DF_any, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfadd, DF_QNaN, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfadd, DF_SNaN, DF_QNaN, DF_HEX_NaN, USR_FPINVF); + + TEST_P_OP_PP(dfsub, DF_any, DF_QNaN, DF_HEX_NaN, USR_CLEAR); + TEST_P_OP_PP(dfsub, DF_any, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfsub, DF_QNaN, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfsub, DF_SNaN, DF_QNaN, DF_HEX_NaN, USR_FPINVF); + +#if CORE_IS_V67 + TEST_P_OP_PP(dfmin, DF_any, DF_small_neg, DF_small_neg, USR_CLEAR); + TEST_P_OP_PP(dfmin, DF_any, DF_SNaN, DF_any, USR_FPINVF); + TEST_P_OP_PP(dfmin, DF_SNaN, DF_any, DF_any, USR_FPINVF); + TEST_P_OP_PP(dfmin, DF_any, DF_QNaN, DF_any, USR_CLEAR); + TEST_P_OP_PP(dfmin, DF_QNaN, DF_any, DF_any, USR_CLEAR); + TEST_P_OP_PP(dfmin, DF_SNaN, DF_QNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfmin, DF_QNaN, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + + TEST_P_OP_PP(dfmax, DF_any, DF_small_neg, DF_any, USR_CLEAR); + TEST_P_OP_PP(dfmax, DF_any, DF_SNaN, DF_any, USR_FPINVF); + TEST_P_OP_PP(dfmax, DF_SNaN, DF_any, DF_any, USR_FPINVF); + TEST_P_OP_PP(dfmax, DF_any, DF_QNaN, DF_any, USR_CLEAR); + TEST_P_OP_PP(dfmax, DF_QNaN, DF_any, DF_any, USR_CLEAR); + TEST_P_OP_PP(dfmax, DF_SNaN, DF_QNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfmax, DF_QNaN, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + + TEST_XP_OP_PP(dfmpyhh, DF_one, DF_one, DF_one, DF_one_hh, USR_CLEAR); + TEST_XP_OP_PP(dfmpyhh, DF_zero, DF_any, DF_QNaN, DF_HEX_NaN, USR_CLEAR); + TEST_XP_OP_PP(dfmpyhh, DF_zero, DF_any, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_XP_OP_PP(dfmpyhh, DF_zero, DF_QNaN, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_XP_OP_PP(dfmpyhh, DF_zero, DF_SNaN, DF_QNaN, DF_HEX_NaN, USR_FPINVF); +#else + printf("v67 instructions skipped\n"); +#endif + + TEST_CMP_PP(dfcmpuo, DF_small_neg, DF_any, 0x00, USR_CLEAR); + TEST_CMP_PP(dfcmpuo, DF_large_pos, DF_any, 0x00, USR_CLEAR); + TEST_CMP_PP(dfcmpuo, DF_QNaN, DF_any, 0xff, USR_CLEAR); + TEST_CMP_PP(dfcmpuo, DF_QNaN_neg, DF_any, 0xff, USR_CLEAR); + TEST_CMP_PP(dfcmpuo, DF_SNaN, DF_any, 0xff, USR_FPINVF); + TEST_CMP_PP(dfcmpuo, DF_SNaN_neg, DF_any, 0xff, USR_FPINVF); + TEST_CMP_PP(dfcmpuo, DF_QNaN, DF_QNaN, 0xff, USR_CLEAR); + TEST_CMP_PP(dfcmpuo, DF_QNaN, DF_SNaN, 0xff, USR_FPINVF); + + TEST_CMP_PP(dfcmpeq, DF_any, DF_QNaN, 0x00, USR_CLEAR); + TEST_CMP_PP(dfcmpeq, DF_any, DF_SNaN, 0x00, USR_FPINVF); + TEST_CMP_PP(dfcmpgt, DF_any, DF_QNaN, 0x00, USR_CLEAR); + TEST_CMP_PP(dfcmpgt, DF_any, DF_SNaN, 0x00, USR_FPINVF); + TEST_CMP_PP(dfcmpge, DF_any, DF_QNaN, 0x00, USR_CLEAR); + TEST_CMP_PP(dfcmpge, DF_any, DF_SNaN, 0x00, USR_FPINVF); + + TEST_P_OP_R(conv_sf2df, SF_QNaN, DF_HEX_NaN, USR_CLEAR); + TEST_P_OP_R(conv_sf2df, SF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_R_OP_R(conv_sf2uw, SF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_R(conv_sf2uw, SF_SNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_R(conv_sf2w, SF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_R(conv_sf2w, SF_SNaN, 0xffffffff, USR_FPINVF); + TEST_P_OP_R(conv_sf2ud, SF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_R(conv_sf2ud, SF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_R(conv_sf2d, SF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_R(conv_sf2d, SF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_R_OP_R(conv_sf2uw_chop, SF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_R(conv_sf2uw_chop, SF_SNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_R(conv_sf2w_chop, SF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_R(conv_sf2w_chop, SF_SNaN, 0xffffffff, USR_FPINVF); + TEST_P_OP_R(conv_sf2ud_chop, SF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_R(conv_sf2ud_chop, SF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_R(conv_sf2d_chop, SF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_R(conv_sf2d_chop, SF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + + TEST_R_OP_P(conv_df2sf, DF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_P(conv_df2sf, DF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_P(conv_df2uw, DF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_P(conv_df2uw, DF_SNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_P(conv_df2w, DF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_P(conv_df2w, DF_SNaN, 0xffffffff, USR_FPINVF); + TEST_P_OP_P(conv_df2ud, DF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_P(conv_df2ud, DF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_P(conv_df2d, DF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_P(conv_df2d, DF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_R_OP_P(conv_df2uw_chop, DF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_P(conv_df2uw_chop, DF_SNaN, 0xffffffff, USR_FPINVF); + + /* Test for typo in HELPER(conv_df2uw_chop) */ + TEST_R_OP_P(conv_df2uw_chop, 0xffffff7f00000001ULL, 0xffffffff, USR_FPINVF); + + TEST_R_OP_P(conv_df2w_chop, DF_QNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_P(conv_df2w_chop, DF_SNaN, 0xffffffff, USR_FPINVF); + TEST_P_OP_P(conv_df2ud_chop, DF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_P(conv_df2ud_chop, DF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_P(conv_df2d_chop, DF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_P_OP_P(conv_df2d_chop, DF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + + TEST_R_OP_R(conv_uw2sf, 0x00000001, SF_one, USR_CLEAR); + TEST_R_OP_R(conv_uw2sf, 0x010020a5, 0x4b801052, USR_FPINPF); + TEST_R_OP_R(conv_w2sf, 0x00000001, SF_one, USR_CLEAR); + TEST_R_OP_R(conv_w2sf, 0x010020a5, 0x4b801052, USR_FPINPF); + TEST_R_OP_P(conv_ud2sf, 0x0000000000000001ULL, SF_one, USR_CLEAR); + TEST_R_OP_P(conv_ud2sf, 0x00000000010020a5ULL, 0x4b801052, USR_FPINPF); + TEST_R_OP_P(conv_d2sf, 0x0000000000000001ULL, SF_one, USR_CLEAR); + TEST_R_OP_P(conv_d2sf, 0x00000000010020a5ULL, 0x4b801052, USR_FPINPF); + + TEST_XR_OP_RRp(sffma_sc, SF_one, SF_one, SF_one, 1, SF_four, + USR_CLEAR); + TEST_XR_OP_RRp(sffma_sc, SF_QNaN, SF_one, SF_one, 1, SF_HEX_NaN, + USR_CLEAR); + TEST_XR_OP_RRp(sffma_sc, SF_one, SF_QNaN, SF_one, 1, SF_HEX_NaN, + USR_CLEAR); + TEST_XR_OP_RRp(sffma_sc, SF_one, SF_one, SF_QNaN, 1, SF_HEX_NaN, + USR_CLEAR); + TEST_XR_OP_RRp(sffma_sc, SF_SNaN, SF_one, SF_one, 1, SF_HEX_NaN, + USR_FPINVF); + TEST_XR_OP_RRp(sffma_sc, SF_one, SF_SNaN, SF_one, 1, SF_HEX_NaN, + USR_FPINVF); + TEST_XR_OP_RRp(sffma_sc, SF_one, SF_one, SF_SNaN, 1, SF_HEX_NaN, + USR_FPINVF); + + TEST_Rp_OP_RR(sfrecipa, SF_one, SF_one, SF_one_recip, 0x00, + USR_CLEAR); + TEST_Rp_OP_RR(sfrecipa, SF_QNaN, SF_one, SF_HEX_NaN, 0x00, + USR_CLEAR); + TEST_Rp_OP_RR(sfrecipa, SF_one, SF_QNaN, SF_HEX_NaN, 0x00, + USR_CLEAR); + TEST_Rp_OP_RR(sfrecipa, SF_one, SF_SNaN, SF_HEX_NaN, 0x00, + USR_FPINVF); + TEST_Rp_OP_RR(sfrecipa, SF_SNaN, SF_one, SF_HEX_NaN, 0x00, + USR_FPINVF); + + TEST_R_OP_RR(sffixupn, SF_one, SF_one, SF_one, USR_CLEAR); + TEST_R_OP_RR(sffixupn, SF_QNaN, SF_one, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_RR(sffixupn, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_RR(sffixupn, SF_SNaN, SF_one, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sffixupn, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_R_OP_RR(sffixupd, SF_one, SF_one, SF_one, USR_CLEAR); + TEST_R_OP_RR(sffixupd, SF_QNaN, SF_one, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_RR(sffixupd, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_RR(sffixupd, SF_SNaN, SF_one, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sffixupd, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_R_OP_R(sffixupr, SF_one, SF_one, USR_CLEAR); + TEST_R_OP_R(sffixupr, SF_QNaN, SF_HEX_NaN, USR_CLEAR); + TEST_R_OP_R(sffixupr, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + + TEST_Rp_OP_R(sfinvsqrta, SF_one, SF_one_invsqrta, 0x00, USR_CLEAR); + TEST_Rp_OP_R(sfinvsqrta, SF_zero, SF_one, 0x00, USR_CLEAR); + TEST_Rp_OP_R(sfinvsqrta, SF_QNaN, SF_HEX_NaN, 0x00, USR_CLEAR); + TEST_Rp_OP_R(sfinvsqrta, SF_small_neg, SF_HEX_NaN, 0x00, USR_FPINVF); + TEST_Rp_OP_R(sfinvsqrta, SF_SNaN, SF_HEX_NaN, 0x00, USR_FPINVF); + + puts(err ? "FAIL" : "PASS"); + return err; +} diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index 38c10379af..bd73c96d0d 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -30,7 +30,7 @@ hello-i386: LDFLAGS+=-nostdlib # test-386 includes a couple of additional objects that need to be # linked together, we also need a no-pie capable compiler due to the # non-pic calls into 16-bit mode -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_I386_NOPIE),) +ifneq ($(CROSS_CC_HAS_I386_NOPIE),) test-i386: CFLAGS += -fno-pie test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386-shift.h test-i386-muldiv.h @@ -71,3 +71,12 @@ TESTS=$(MULTIARCH_TESTS) $(I386_TESTS) # On i386 and x86_64 Linux only supports 4k pages (large pages are a different hack) EXTRA_RUNS+=run-test-mmap-4096 + +sha512-sse: CFLAGS=-msse4.1 -O3 +sha512-sse: sha512.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-sha512-sse: QEMU_OPTS+=-cpu max +run-plugin-sha512-sse-with-%: QEMU_OPTS+=-cpu max + +TESTS+=sha512-sse diff --git a/tests/tcg/i386/float_convd.conf b/tests/tcg/i386/float_convd.conf new file mode 100644 index 0000000000..7f4040cef8 --- /dev/null +++ b/tests/tcg/i386/float_convd.conf @@ -0,0 +1,988 @@ +### Rounding to nearest +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to single: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to single: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to single: f64(-inf:0x00fff0000000000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to single: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to single: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.00000000000000000000p+1:0xc0000000) + to single: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from single: f32(-0x1.00000000000000000000p+0:0xbf800000) + to single: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from single: f32(-0x0.00000000000000000000p+0:0x80000000) + to single: f64(-0x0.00000000000000000000p+0:0x008000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to single: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to single: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to single: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to single: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to single: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to single: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to single: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to single: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to single: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to single: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to single: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to single: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to single: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to single: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to single: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to single: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.00000000000000000000p+31:0x4f000000) + to single: f64(0x1.00000000000000000000p+31:0x0041e0000000000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to single: f64(inf:0x007ff0000000000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to single: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding upwards +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to single: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to single: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) + to single: f64(-0x1.1874b000000000000000p+103:0x00c661874b00000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) + to single: f64(-0x1.c0bab400000000000000p+99:0x00c62c0bab40000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.00000000000000000000p+1:0xc0000000) + to single: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from single: f32(-0x1.00000000000000000000p+0:0xbf800000) + to single: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from single: f32(-0x0.00000000000000000000p+0:0x80000000) + to single: f64(-0x0.00000000000000000000p+0:0x008000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to single: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to single: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000200000000000000p-25:0x33000001) + to single: f64(0x1.00000200000000000000p-25:0x003e60000020000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe800000000000000p-25:0x337ffff4) + to single: f64(0x1.ffffe800000000000000p-25:0x003e6ffffe80000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801c00000000000000p-15:0x387fc00e) + to single: f64(0x1.ff801c00000000000000p-15:0x003f0ff801c0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000e00000000000000p-14:0x38800007) + to single: f64(0x1.00000e00000000000000p-14:0x003f100000e0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to single: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p-149:0x00000001) + to single: f64(0x1.00000000000000000000p-149:0x0036a0000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-149:0x00000001) + to single: f64(0x1.00000000000000000000p-149:0x0036a0000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-149:0x00000001) + to single: f64(0x1.00000000000000000000p-149:0x0036a0000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to single: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0aa00000000000000p+1:0x402df855) + to single: f64(0x1.5bf0aa00000000000000p+1:0x004005bf0aa0000000) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to single: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to single: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to single: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to single: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to single: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to single: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to single: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.00000000000000000000p+31:0x4f000000) + to single: f64(0x1.00000000000000000000p+31:0x0041e0000000000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to single: f64(inf:0x007ff0000000000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to single: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding downwards +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to single: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to single: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to single: f64(-inf:0x00fff0000000000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to single: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to single: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.00000000000000000000p+1:0xc0000000) + to single: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from single: f32(-0x1.00000000000000000000p+0:0xbf800000) + to single: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from single: f32(-0x1.00000000000000000000p-149:0x80000001) + to single: f64(-0x1.00000000000000000000p-149:0x00b6a0000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to single: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to single: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to single: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to single: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to single: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to single: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to single: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to single: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to single: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb400000000000000p+1:0x40490fda) + to single: f64(0x1.921fb400000000000000p+1:0x00400921fb40000000) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to single: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to single: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to single: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to single: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to single: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to single: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.fffffe00000000000000p+30:0x4effffff) + to single: f64(0x1.fffffe00000000000000p+30:0x0041dfffffe0000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to single: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding to zero +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to single: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to single: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to single: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) + to single: f64(-0x1.1874b000000000000000p+103:0x00c661874b00000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) + to single: f64(-0x1.c0bab400000000000000p+99:0x00c62c0bab40000000) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.00000000000000000000p+1:0xc0000000) + to single: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from single: f32(-0x1.00000000000000000000p+0:0xbf800000) + to single: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from single: f32(-0x0.00000000000000000000p+0:0x80000000) + to single: f64(-0x0.00000000000000000000p+0:0x008000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to single: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to single: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to single: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to single: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to single: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to single: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to single: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to single: f64(0x0.00000000000000000000p+0:00000000000000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to single: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to single: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to single: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb400000000000000p+1:0x40490fda) + to single: f64(0x1.921fb400000000000000p+1:0x00400921fb40000000) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to single: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to single: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to single: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to single: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to single: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to single: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.fffffe00000000000000p+30:0x4effffff) + to single: f64(0x1.fffffe00000000000000p+30:0x0041dfffffe0000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to single: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to single: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fc00000) + to single: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to single: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) diff --git a/tests/tcg/i386/float_convs.ref b/tests/tcg/i386/float_convs.ref new file mode 100644 index 0000000000..9de86910a8 --- /dev/null +++ b/tests/tcg/i386/float_convs.ref @@ -0,0 +1,748 @@ +### Rounding to nearest +from single: f32(-nan:0xffe00000) + to double: f64(-nan:0x00fffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to double: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding upwards +from single: f32(-nan:0xffe00000) + to double: f64(-nan:0x00fffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to double: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding downwards +from single: f32(-nan:0xffe00000) + to double: f64(-nan:0x00fffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to double: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding to zero +from single: f32(-nan:0xffe00000) + to double: f64(-nan:0x00fffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fe00000) + to double: f64(nan:0x007ffc000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index dec401e67f..6bba523729 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -10,7 +10,7 @@ MULTIARCH_SRC=$(SRC_PATH)/tests/tcg/multiarch # Set search path for all sources VPATH += $(MULTIARCH_SRC) MULTIARCH_SRCS = $(notdir $(wildcard $(MULTIARCH_SRC)/*.c)) -ifneq ($(CONFIG_LINUX_USER),) +ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET)) VPATH += $(MULTIARCH_SRC)/linux MULTIARCH_SRCS += $(notdir $(wildcard $(MULTIARCH_SRC)/linux/*.c)) endif diff --git a/tests/tcg/multiarch/float_convd.c b/tests/tcg/multiarch/float_convd.c new file mode 100644 index 0000000000..0a1f0f93dc --- /dev/null +++ b/tests/tcg/multiarch/float_convd.c @@ -0,0 +1,106 @@ +/* + * Floating Point Convert Doubles to Various + * + * Copyright (c) 2019 Linaro + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include + + +#include "float_helpers.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +typedef struct { + int flag; + char *desc; +} float_mapping; + +float_mapping round_flags[] = { + { FE_TONEAREST, "to nearest" }, +#ifdef FE_UPWARD + { FE_UPWARD, "upwards" }, +#endif +#ifdef FE_DOWNWARD + { FE_DOWNWARD, "downwards" }, +#endif +#ifdef FE_TOWARDZERO + { FE_TOWARDZERO, "to zero" } +#endif +}; + +static void print_input(double input) +{ + char *in_fmt = fmt_f64(input); + printf("from double: %s\n", in_fmt); + free(in_fmt); +} + +static void convert_double_to_single(double input) +{ + float output; + char *out_fmt, *flag_fmt; + + feclearexcept(FE_ALL_EXCEPT); + + output = input; + + flag_fmt = fmt_flags(); + out_fmt = fmt_f32(output); + printf(" to single: %s (%s)\n", out_fmt, flag_fmt); + free(out_fmt); + free(flag_fmt); +} + +#define xstr(a) str(a) +#define str(a) #a + +#define CONVERT_DOUBLE_TO_INT(TYPE, FMT) \ + static void convert_double_to_ ## TYPE(double input) \ + { \ + TYPE ## _t output; \ + char *flag_fmt; \ + const char to[] = "to " xstr(TYPE); \ + feclearexcept(FE_ALL_EXCEPT); \ + output = input; \ + flag_fmt = fmt_flags(); \ + printf("%11s: %" FMT " (%s)\n", to, output, flag_fmt); \ + free(flag_fmt); \ + } + +CONVERT_DOUBLE_TO_INT( int32, PRId32) +CONVERT_DOUBLE_TO_INT(uint32, PRId32) +CONVERT_DOUBLE_TO_INT( int64, PRId64) +CONVERT_DOUBLE_TO_INT(uint64, PRId64) + +int main(int argc, char *argv[argc]) +{ + int i, j, nums; + + nums = get_num_f64(); + + for (i = 0; i < ARRAY_SIZE(round_flags); ++i) { + if (fesetround(round_flags[i].flag) != 0) { + printf("### Rounding %s skipped\n", round_flags[i].desc); + continue; + } + printf("### Rounding %s\n", round_flags[i].desc); + for (j = 0; j < nums; j++) { + double input = get_f64(j); + print_input(input); + convert_double_to_single(input); + convert_double_to_int32(input); + convert_double_to_int64(input); + convert_double_to_uint32(input); + convert_double_to_uint64(input); + } + } + + return 0; +} diff --git a/tests/tcg/multiarch/sha512.c b/tests/tcg/multiarch/sha512.c new file mode 100644 index 0000000000..e1729828b9 --- /dev/null +++ b/tests/tcg/multiarch/sha512.c @@ -0,0 +1,990 @@ +/* + * sha512 test based on CCAN: https://ccodearchive.net/info/crypto/sha512.html + * + * src/crypto/sha512.cpp commit f914f1a746d7f91951c1da262a4a749dd3ebfa71 + * Copyright (c) 2014 The Bitcoin Core developers + * Distributed under the MIT software license, see: + * http://www.opensource.org/licenses/mit-license.php. + * + * SPDX-License-Identifier: MIT CC0-1.0 + */ +#define _GNU_SOURCE /* See feature_test_macros(7) */ + +#include +#include +#include +#include +#include +#include + +/* Required portions from endian.h */ + +/** + * BSWAP_64 - reverse bytes in a constant uint64_t value. + * @val: constantvalue whose bytes to swap. + * + * Designed to be usable in constant-requiring initializers. + * + * Example: + * struct mystruct { + * char buf[BSWAP_64(0xff00000000000000ULL)]; + * }; + */ +#define BSWAP_64(val) \ + ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \ + | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \ + | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \ + | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \ + | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \ + | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \ + | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \ + | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56)) + + +typedef uint64_t beint64_t; + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + +/** + * CPU_TO_BE64 - convert a constant uint64_t value to big-endian + * @native: constant to convert + */ +#define CPU_TO_BE64(native) ((beint64_t)(native)) +/** + * BE64_TO_CPU - convert a big-endian uint64_t constant + * @le_val: big-endian constant to convert + */ +#define BE64_TO_CPU(le_val) ((uint64_t)(le_val)) + +#else /* ... HAVE_LITTLE_ENDIAN */ +#define CPU_TO_BE64(native) ((beint64_t)BSWAP_64(native)) +#define BE64_TO_CPU(le_val) BSWAP_64((uint64_t)le_val) +#endif /* HAVE_LITTE_ENDIAN */ + +/** + * cpu_to_be64 - convert a uint64_t value to big endian. + * @native: value to convert + */ +static inline beint64_t cpu_to_be64(uint64_t native) +{ + return CPU_TO_BE64(native); +} + +/** + * be64_to_cpu - convert a big-endian uint64_t value + * @be_val: big-endian value to convert + */ +static inline uint64_t be64_to_cpu(beint64_t be_val) +{ + return BE64_TO_CPU(be_val); +} + +/* From compiler.h */ + +#ifndef UNUSED +/** + * UNUSED - a parameter is unused + * + * Some compilers (eg. gcc with -W or -Wunused) warn about unused + * function parameters. This suppresses such warnings and indicates + * to the reader that it's deliberate. + * + * Example: + * // This is used as a callback, so needs to have this prototype. + * static int some_callback(void *unused UNUSED) + * { + * return 0; + * } + */ +#define UNUSED __attribute__((__unused__)) +#endif + +/* From sha512.h */ + +/** + * struct sha512 - structure representing a completed SHA512. + * @u.u8: an unsigned char array. + * @u.u64: a 64-bit integer array. + * + * Other fields may be added to the union in future. + */ +struct sha512 { + union { + uint64_t u64[8]; + unsigned char u8[64]; + } u; +}; + +/** + * sha512 - return sha512 of an object. + * @sha512: the sha512 to fill in + * @p: pointer to memory, + * @size: the number of bytes pointed to by @p + * + * The bytes pointed to by @p is SHA512 hashed into @sha512. This is + * equivalent to sha512_init(), sha512_update() then sha512_done(). + */ +void sha512(struct sha512 *sha, const void *p, size_t size); + +/** + * struct sha512_ctx - structure to store running context for sha512 + */ +struct sha512_ctx { + uint64_t s[8]; + union { + uint64_t u64[16]; + unsigned char u8[128]; + } buf; + size_t bytes; +}; + +/** + * sha512_init - initialize an SHA512 context. + * @ctx: the sha512_ctx to initialize + * + * This must be called before sha512_update or sha512_done, or + * alternately you can assign SHA512_INIT. + * + * If it was already initialized, this forgets anything which was + * hashed before. + * + * Example: + * static void hash_all(const char **arr, struct sha512 *hash) + * { + * size_t i; + * struct sha512_ctx ctx; + * + * sha512_init(&ctx); + * for (i = 0; arr[i]; i++) + * sha512_update(&ctx, arr[i], strlen(arr[i])); + * sha512_done(&ctx, hash); + * } + */ +void sha512_init(struct sha512_ctx *ctx); + +/** + * SHA512_INIT - initializer for an SHA512 context. + * + * This can be used to statically initialize an SHA512 context (instead + * of sha512_init()). + * + * Example: + * static void hash_all(const char **arr, struct sha512 *hash) + * { + * size_t i; + * struct sha512_ctx ctx = SHA512_INIT; + * + * for (i = 0; arr[i]; i++) + * sha512_update(&ctx, arr[i], strlen(arr[i])); + * sha512_done(&ctx, hash); + * } + */ +#define SHA512_INIT \ + { { 0x6a09e667f3bcc908ull, 0xbb67ae8584caa73bull, \ + 0x3c6ef372fe94f82bull, 0xa54ff53a5f1d36f1ull, \ + 0x510e527fade682d1ull, 0x9b05688c2b3e6c1full, \ + 0x1f83d9abfb41bd6bull, 0x5be0cd19137e2179ull }, \ + { { 0 } }, 0 } + +/** + * sha512_update - include some memory in the hash. + * @ctx: the sha512_ctx to use + * @p: pointer to memory, + * @size: the number of bytes pointed to by @p + * + * You can call this multiple times to hash more data, before calling + * sha512_done(). + */ +void sha512_update(struct sha512_ctx *ctx, const void *p, size_t size); + +/** + * sha512_done - finish SHA512 and return the hash + * @ctx: the sha512_ctx to complete + * @res: the hash to return. + * + * Note that @ctx is *destroyed* by this, and must be reinitialized. + * To avoid that, pass a copy instead. + */ +void sha512_done(struct sha512_ctx *sha512, struct sha512 *res); + +/* From sha512.c */ + +/* + * SHA512 core code translated from the Bitcoin project's C++: + * + * src/crypto/sha512.cpp commit f914f1a746d7f91951c1da262a4a749dd3ebfa71 + * Copyright (c) 2014 The Bitcoin Core developers + * Distributed under the MIT software license, see the accompanying + * file COPYING or http://www.opensource.org/licenses/mit-license.php. + */ +/* #include */ +/* #include */ +#include +#include +#include + +static void invalidate_sha512(struct sha512_ctx *ctx) +{ + ctx->bytes = (size_t)-1; +} + +static void check_sha512(struct sha512_ctx *ctx UNUSED) +{ + assert(ctx->bytes != (size_t)-1); +} + +static uint64_t Ch(uint64_t x, uint64_t y, uint64_t z) +{ + return z ^ (x & (y ^ z)); +} +static uint64_t Maj(uint64_t x, uint64_t y, uint64_t z) +{ + return (x & y) | (z & (x | y)); +} +static uint64_t Sigma0(uint64_t x) +{ + return (x >> 28 | x << 36) ^ (x >> 34 | x << 30) ^ (x >> 39 | x << 25); +} +static uint64_t Sigma1(uint64_t x) +{ + return (x >> 14 | x << 50) ^ (x >> 18 | x << 46) ^ (x >> 41 | x << 23); +} +static uint64_t sigma0(uint64_t x) +{ + return (x >> 1 | x << 63) ^ (x >> 8 | x << 56) ^ (x >> 7); +} +static uint64_t sigma1(uint64_t x) +{ + return (x >> 19 | x << 45) ^ (x >> 61 | x << 3) ^ (x >> 6); +} + +/** One round of SHA-512. */ +static void Round(uint64_t a, uint64_t b, uint64_t c, uint64_t *d, uint64_t e, uint64_t f, uint64_t g, uint64_t *h, uint64_t k, uint64_t w) +{ + uint64_t t1 = *h + Sigma1(e) + Ch(e, f, g) + k + w; + uint64_t t2 = Sigma0(a) + Maj(a, b, c); + *d += t1; + *h = t1 + t2; +} + +/** Perform one SHA-512 transformation, processing a 128-byte chunk. */ +static void Transform(uint64_t *s, const uint64_t *chunk) +{ + uint64_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint64_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, &d, e, f, g, &h, 0x428a2f98d728ae22ull, w0 = be64_to_cpu(chunk[0])); + Round(h, a, b, &c, d, e, f, &g, 0x7137449123ef65cdull, w1 = be64_to_cpu(chunk[1])); + Round(g, h, a, &b, c, d, e, &f, 0xb5c0fbcfec4d3b2full, w2 = be64_to_cpu(chunk[2])); + Round(f, g, h, &a, b, c, d, &e, 0xe9b5dba58189dbbcull, w3 = be64_to_cpu(chunk[3])); + Round(e, f, g, &h, a, b, c, &d, 0x3956c25bf348b538ull, w4 = be64_to_cpu(chunk[4])); + Round(d, e, f, &g, h, a, b, &c, 0x59f111f1b605d019ull, w5 = be64_to_cpu(chunk[5])); + Round(c, d, e, &f, g, h, a, &b, 0x923f82a4af194f9bull, w6 = be64_to_cpu(chunk[6])); + Round(b, c, d, &e, f, g, h, &a, 0xab1c5ed5da6d8118ull, w7 = be64_to_cpu(chunk[7])); + Round(a, b, c, &d, e, f, g, &h, 0xd807aa98a3030242ull, w8 = be64_to_cpu(chunk[8])); + Round(h, a, b, &c, d, e, f, &g, 0x12835b0145706fbeull, w9 = be64_to_cpu(chunk[9])); + Round(g, h, a, &b, c, d, e, &f, 0x243185be4ee4b28cull, w10 = be64_to_cpu(chunk[10])); + Round(f, g, h, &a, b, c, d, &e, 0x550c7dc3d5ffb4e2ull, w11 = be64_to_cpu(chunk[11])); + Round(e, f, g, &h, a, b, c, &d, 0x72be5d74f27b896full, w12 = be64_to_cpu(chunk[12])); + Round(d, e, f, &g, h, a, b, &c, 0x80deb1fe3b1696b1ull, w13 = be64_to_cpu(chunk[13])); + Round(c, d, e, &f, g, h, a, &b, 0x9bdc06a725c71235ull, w14 = be64_to_cpu(chunk[14])); + Round(b, c, d, &e, f, g, h, &a, 0xc19bf174cf692694ull, w15 = be64_to_cpu(chunk[15])); + + Round(a, b, c, &d, e, f, g, &h, 0xe49b69c19ef14ad2ull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, &c, d, e, f, &g, 0xefbe4786384f25e3ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, &b, c, d, e, &f, 0x0fc19dc68b8cd5b5ull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, &a, b, c, d, &e, 0x240ca1cc77ac9c65ull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, &h, a, b, c, &d, 0x2de92c6f592b0275ull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, &g, h, a, b, &c, 0x4a7484aa6ea6e483ull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, &f, g, h, a, &b, 0x5cb0a9dcbd41fbd4ull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, &e, f, g, h, &a, 0x76f988da831153b5ull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, &d, e, f, g, &h, 0x983e5152ee66dfabull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, &c, d, e, f, &g, 0xa831c66d2db43210ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, &b, c, d, e, &f, 0xb00327c898fb213full, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, &a, b, c, d, &e, 0xbf597fc7beef0ee4ull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, &h, a, b, c, &d, 0xc6e00bf33da88fc2ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, &g, h, a, b, &c, 0xd5a79147930aa725ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, &f, g, h, a, &b, 0x06ca6351e003826full, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, &e, f, g, h, &a, 0x142929670a0e6e70ull, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, &d, e, f, g, &h, 0x27b70a8546d22ffcull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, &c, d, e, f, &g, 0x2e1b21385c26c926ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, &b, c, d, e, &f, 0x4d2c6dfc5ac42aedull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, &a, b, c, d, &e, 0x53380d139d95b3dfull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, &h, a, b, c, &d, 0x650a73548baf63deull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, &g, h, a, b, &c, 0x766a0abb3c77b2a8ull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, &f, g, h, a, &b, 0x81c2c92e47edaee6ull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, &e, f, g, h, &a, 0x92722c851482353bull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, &d, e, f, g, &h, 0xa2bfe8a14cf10364ull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, &c, d, e, f, &g, 0xa81a664bbc423001ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, &b, c, d, e, &f, 0xc24b8b70d0f89791ull, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, &a, b, c, d, &e, 0xc76c51a30654be30ull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, &h, a, b, c, &d, 0xd192e819d6ef5218ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, &g, h, a, b, &c, 0xd69906245565a910ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, &f, g, h, a, &b, 0xf40e35855771202aull, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, &e, f, g, h, &a, 0x106aa07032bbd1b8ull, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, &d, e, f, g, &h, 0x19a4c116b8d2d0c8ull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, &c, d, e, f, &g, 0x1e376c085141ab53ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, &b, c, d, e, &f, 0x2748774cdf8eeb99ull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, &a, b, c, d, &e, 0x34b0bcb5e19b48a8ull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, &h, a, b, c, &d, 0x391c0cb3c5c95a63ull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, &g, h, a, b, &c, 0x4ed8aa4ae3418acbull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, &f, g, h, a, &b, 0x5b9cca4f7763e373ull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, &e, f, g, h, &a, 0x682e6ff3d6b2b8a3ull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, &d, e, f, g, &h, 0x748f82ee5defb2fcull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, &c, d, e, f, &g, 0x78a5636f43172f60ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, &b, c, d, e, &f, 0x84c87814a1f0ab72ull, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, &a, b, c, d, &e, 0x8cc702081a6439ecull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, &h, a, b, c, &d, 0x90befffa23631e28ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, &g, h, a, b, &c, 0xa4506cebde82bde9ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, &f, g, h, a, &b, 0xbef9a3f7b2c67915ull, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, &e, f, g, h, &a, 0xc67178f2e372532bull, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, &d, e, f, g, &h, 0xca273eceea26619cull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, &c, d, e, f, &g, 0xd186b8c721c0c207ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, &b, c, d, e, &f, 0xeada7dd6cde0eb1eull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, &a, b, c, d, &e, 0xf57d4f7fee6ed178ull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, &h, a, b, c, &d, 0x06f067aa72176fbaull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, &g, h, a, b, &c, 0x0a637dc5a2c898a6ull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, &f, g, h, a, &b, 0x113f9804bef90daeull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, &e, f, g, h, &a, 0x1b710b35131c471bull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, &d, e, f, g, &h, 0x28db77f523047d84ull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, &c, d, e, f, &g, 0x32caab7b40c72493ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, &b, c, d, e, &f, 0x3c9ebe0a15c9bebcull, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, &a, b, c, d, &e, 0x431d67c49c100d4cull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, &h, a, b, c, &d, 0x4cc5d4becb3e42b6ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, &g, h, a, b, &c, 0x597f299cfc657e2aull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, &f, g, h, a, &b, 0x5fcb6fab3ad6faecull, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, &e, f, g, h, &a, 0x6c44198c4a475817ull, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +static bool alignment_ok(const void *p UNUSED, size_t n UNUSED) +{ +#if HAVE_UNALIGNED_ACCESS + return true; +#else + return ((size_t)p % n == 0); +#endif +} + +static void add(struct sha512_ctx *ctx, const void *p, size_t len) +{ + const unsigned char *data = p; + size_t bufsize = ctx->bytes % 128; + + if (bufsize + len >= 128) { + /* Fill the buffer, and process it. */ + memcpy(ctx->buf.u8 + bufsize, data, 128 - bufsize); + ctx->bytes += 128 - bufsize; + data += 128 - bufsize; + len -= 128 - bufsize; + Transform(ctx->s, ctx->buf.u64); + bufsize = 0; + } + + while (len >= 128) { + /* Process full chunks directly from the source. */ + if (alignment_ok(data, sizeof(uint64_t))) + Transform(ctx->s, (const uint64_t *)data); + else { + memcpy(ctx->buf.u8, data, sizeof(ctx->buf)); + Transform(ctx->s, ctx->buf.u64); + } + ctx->bytes += 128; + data += 128; + len -= 128; + } + + if (len) { + /* Fill the buffer with what remains. */ + memcpy(ctx->buf.u8 + bufsize, data, len); + ctx->bytes += len; + } +} + +void sha512_init(struct sha512_ctx *ctx) +{ + struct sha512_ctx init = SHA512_INIT; + *ctx = init; +} + +void sha512_update(struct sha512_ctx *ctx, const void *p, size_t size) +{ + check_sha512(ctx); + add(ctx, p, size); +} + +void sha512_done(struct sha512_ctx *ctx, struct sha512 *res) +{ + static const unsigned char pad[128] = { 0x80 }; + uint64_t sizedesc[2] = { 0, 0 }; + size_t i; + + sizedesc[1] = cpu_to_be64((uint64_t)ctx->bytes << 3); + + /* Add '1' bit to terminate, then all 0 bits, up to next block - 16. */ + add(ctx, pad, 1 + ((256 - 16 - (ctx->bytes % 128) - 1) % 128)); + /* Add number of bits of data (big endian) */ + add(ctx, sizedesc, sizeof(sizedesc)); + for (i = 0; i < sizeof(ctx->s) / sizeof(ctx->s[0]); i++) + res->u.u64[i] = cpu_to_be64(ctx->s[i]); + invalidate_sha512(ctx); +} + +void sha512(struct sha512 *sha, const void *p, size_t size) +{ + struct sha512_ctx ctx; + + sha512_init(&ctx); + sha512_update(&ctx, p, size); + sha512_done(&ctx, sha); +} + +/* From hex.h */ +/** + * hex_decode - Unpack a hex string. + * @str: the hexidecimal string + * @slen: the length of @str + * @buf: the buffer to write the data into + * @bufsize: the length of @buf + * + * Returns false if there are any characters which aren't 0-9, a-f or A-F, + * of the string wasn't the right length for @bufsize. + * + * Example: + * unsigned char data[20]; + * + * if (!hex_decode(argv[1], strlen(argv[1]), data, 20)) + * printf("String is malformed!\n"); + */ +bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize); + +/** + * hex_encode - Create a nul-terminated hex string + * @buf: the buffer to read the data from + * @bufsize: the length of @buf + * @dest: the string to fill + * @destsize: the max size of the string + * + * Returns true if the string, including terminator, fit in @destsize; + * + * Example: + * unsigned char buf[] = { 0x1F, 0x2F }; + * char str[5]; + * + * if (!hex_encode(buf, sizeof(buf), str, sizeof(str))) + * abort(); + */ +bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize); + +/** + * hex_str_size - Calculate how big a nul-terminated hex string is + * @bytes: bytes of data to represent + * + * Example: + * unsigned char buf[] = { 0x1F, 0x2F }; + * char str[hex_str_size(sizeof(buf))]; + * + * hex_encode(buf, sizeof(buf), str, sizeof(str)); + */ +static inline size_t hex_str_size(size_t bytes) +{ + return 2 * bytes + 1; +} + +/* From hex.c */ +static bool char_to_hex(unsigned char *val, char c) +{ + if (c >= '0' && c <= '9') { + *val = c - '0'; + return true; + } + if (c >= 'a' && c <= 'f') { + *val = c - 'a' + 10; + return true; + } + if (c >= 'A' && c <= 'F') { + *val = c - 'A' + 10; + return true; + } + return false; +} + +bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize) +{ + unsigned char v1, v2; + unsigned char *p = buf; + + while (slen > 1) { + if (!char_to_hex(&v1, str[0]) || !char_to_hex(&v2, str[1])) + return false; + if (!bufsize) + return false; + *(p++) = (v1 << 4) | v2; + str += 2; + slen -= 2; + bufsize--; + } + return slen == 0 && bufsize == 0; +} + +static char hexchar(unsigned int val) +{ + if (val < 10) + return '0' + val; + if (val < 16) + return 'a' + val - 10; + abort(); +} + +bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize) +{ + size_t i; + + if (destsize < hex_str_size(bufsize)) + return false; + + for (i = 0; i < bufsize; i++) { + unsigned int c = ((const unsigned char *)buf)[i]; + *(dest++) = hexchar(c >> 4); + *(dest++) = hexchar(c & 0xF); + } + *dest = '\0'; + + return true; +} + +/* From tap.h */ +/** + * plan_tests - announce the number of tests you plan to run + * @tests: the number of tests + * + * This should be the first call in your test program: it allows tracing + * of failures which mean that not all tests are run. + * + * If you don't know how many tests will actually be run, assume all of them + * and use skip() if you don't actually run some tests. + * + * Example: + * plan_tests(13); + */ +void plan_tests(unsigned int tests); + +/** + * ok1 - Simple conditional test + * @e: the expression which we expect to be true. + * + * This is the simplest kind of test: if the expression is true, the + * test passes. The name of the test which is printed will simply be + * file name, line number, and the expression itself. + * + * Example: + * ok1(somefunc() == 1); + */ +# define ok1(e) ((e) ? \ + _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \ + _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) + +/** + * exit_status - the value that main should return. + * + * For maximum compatibility your test program should return a particular exit + * code (ie. 0 if all tests were run, and every test which was expected to + * succeed succeeded). + * + * Example: + * exit(exit_status()); + */ +int exit_status(void); + +/** + * tap_fail_callback - function to call when we fail + * + * This can be used to ease debugging, or exit on the first failure. + */ +void (*tap_fail_callback)(void); + +/* From tap.c */ + +static int no_plan = 0; +static int skip_all = 0; +static int have_plan = 0; +static unsigned int test_count = 0; /* Number of tests that have been run */ +static unsigned int e_tests = 0; /* Expected number of tests to run */ +static unsigned int failures = 0; /* Number of tests that failed */ +static char *todo_msg = NULL; +static const char *todo_msg_fixed = "libtap malloc issue"; +static int todo = 0; +static int test_died = 0; +static int test_pid; + +static void +_expected_tests(unsigned int tests) +{ + printf("1..%d\n", tests); + e_tests = tests; +} + +static void +diagv(const char *fmt, va_list ap) +{ + fputs("# ", stdout); + vfprintf(stdout, fmt, ap); + fputs("\n", stdout); +} + +static void +_diag(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + diagv(fmt, ap); + va_end(ap); +} + +/* + * Generate a test result. + * + * ok -- boolean, indicates whether or not the test passed. + * test_name -- the name of the test, may be NULL + * test_comment -- a comment to print afterwards, may be NULL + */ +unsigned int +_gen_result(int ok, const char *func, const char *file, unsigned int line, + const char *test_name, ...) +{ + va_list ap; + char *local_test_name = NULL; + char *c; + int name_is_digits; + + test_count++; + + /* Start by taking the test name and performing any printf() + expansions on it */ + if(test_name != NULL) { + va_start(ap, test_name); + if (vasprintf(&local_test_name, test_name, ap) < 0) + local_test_name = NULL; + va_end(ap); + + /* Make sure the test name contains more than digits + and spaces. Emit an error message and exit if it + does */ + if(local_test_name) { + name_is_digits = 1; + for(c = local_test_name; *c != '\0'; c++) { + if(!isdigit((unsigned char)*c) + && !isspace((unsigned char)*c)) { + name_is_digits = 0; + break; + } + } + + if(name_is_digits) { + _diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name); + _diag(" Very confusing."); + } + } + } + + if(!ok) { + printf("not "); + failures++; + } + + printf("ok %d", test_count); + + if(test_name != NULL) { + printf(" - "); + + /* Print the test name, escaping any '#' characters it + might contain */ + if(local_test_name != NULL) { + flockfile(stdout); + for(c = local_test_name; *c != '\0'; c++) { + if(*c == '#') + fputc('\\', stdout); + fputc((int)*c, stdout); + } + funlockfile(stdout); + } else { /* vasprintf() failed, use a fixed message */ + printf("%s", todo_msg_fixed); + } + } + + /* If we're in a todo_start() block then flag the test as being + TODO. todo_msg should contain the message to print at this + point. If it's NULL then asprintf() failed, and we should + use the fixed message. + + This is not counted as a failure, so decrement the counter if + the test failed. */ + if(todo) { + printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed); + if(!ok) + failures--; + } + + printf("\n"); + + if(!ok) + _diag(" Failed %stest (%s:%s() at line %d)", + todo ? "(TODO) " : "", file, func, line); + + free(local_test_name); + + if (!ok && tap_fail_callback) + tap_fail_callback(); + + /* We only care (when testing) that ok is positive, but here we + specifically only want to return 1 or 0 */ + return ok ? 1 : 0; +} + +/* + * Cleanup at the end of the run, produce any final output that might be + * required. + */ +static void +_cleanup(void) +{ + /* If we forked, don't do cleanup in child! */ + if (getpid() != test_pid) + return; + + /* If plan_no_plan() wasn't called, and we don't have a plan, + and we're not skipping everything, then something happened + before we could produce any output */ + if(!no_plan && !have_plan && !skip_all) { + _diag("Looks like your test died before it could output anything."); + return; + } + + if(test_died) { + _diag("Looks like your test died just after %d.", test_count); + return; + } + + + /* No plan provided, but now we know how many tests were run, and can + print the header at the end */ + if(!skip_all && (no_plan || !have_plan)) { + printf("1..%d\n", test_count); + } + + if((have_plan && !no_plan) && e_tests < test_count) { + _diag("Looks like you planned %d tests but ran %d extra.", + e_tests, test_count - e_tests); + return; + } + + if((have_plan || !no_plan) && e_tests > test_count) { + _diag("Looks like you planned %d tests but only ran %d.", + e_tests, test_count); + if(failures) { + _diag("Looks like you failed %d tests of %d run.", + failures, test_count); + } + return; + } + + if(failures) + _diag("Looks like you failed %d tests of %d.", + failures, test_count); + +} + +/* + * Initialise the TAP library. Will only do so once, however many times it's + * called. + */ +static void +_tap_init(void) +{ + static int run_once = 0; + + if(!run_once) { + test_pid = getpid(); + atexit(_cleanup); + + /* stdout needs to be unbuffered so that the output appears + in the same place relative to stderr output as it does + with Test::Harness */ +// setbuf(stdout, 0); + run_once = 1; + } +} + +/* + * Note the number of tests that will be run. + */ +void +plan_tests(unsigned int tests) +{ + + _tap_init(); + + if(have_plan != 0) { + fprintf(stderr, "You tried to plan twice!\n"); + test_died = 1; + exit(255); + } + + if(tests == 0) { + fprintf(stderr, "You said to run 0 tests! You've got to run something.\n"); + test_died = 1; + exit(255); + } + + have_plan = 1; + + _expected_tests(tests); +} + +static int +exit_status_(void) +{ + int r; + + /* If there's no plan, just return the number of failures */ + if(no_plan || !have_plan) { + return failures; + } + + /* Ran too many tests? Return the number of tests that were run + that shouldn't have been */ + if(e_tests < test_count) { + r = test_count - e_tests; + return r; + } + + /* Return the number of tests that failed + the number of tests + that weren't run */ + r = failures + e_tests - test_count; + + return r; +} + +int +exit_status(void) +{ + int r = exit_status_(); + if (r > 255) + r = 255; + return r; +} + +/* From run-test-vectors.c */ + +/* Test vectors. */ +struct test { + const char *vector; + size_t repetitions; + const char *expected; +}; + +static const char ZEROES[] = + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000"; + +static struct test tests[] = { + /* http://csrc.nist.gov/groups/STM/cavp/secure-hashing.html ShortMsg */ + { "21", 1, + "3831a6a6155e509dee59a7f451eb35324d8f8f2df6e3708894740f98fdee2388" + "9f4de5adb0c5010dfb555cda77c8ab5dc902094c52de3278f35a75ebc25f093a" }, + { "9083", 1, + "55586ebba48768aeb323655ab6f4298fc9f670964fc2e5f2731e34dfa4b0c09e" + "6e1e12e3d7286b3145c61c2047fb1a2a1297f36da64160b31fa4c8c2cddd2fb4" }, + { "0a55db", 1, + "7952585e5330cb247d72bae696fc8a6b0f7d0804577e347d99bc1b11e52f3849" + "85a428449382306a89261ae143c2f3fb613804ab20b42dc097e5bf4a96ef919b" }, + { "23be86d5", 1, + "76d42c8eadea35a69990c63a762f330614a4699977f058adb988f406fb0be8f2" + "ea3dce3a2bbd1d827b70b9b299ae6f9e5058ee97b50bd4922d6d37ddc761f8eb" }, + { "eb0ca946c1", 1, + "d39ecedfe6e705a821aee4f58bfc489c3d9433eb4ac1b03a97e321a2586b40dd" + "0522f40fa5aef36afff591a78c916bfc6d1ca515c4983dd8695b1ec7951d723e" }, + { "38667f39277b", 1, + "85708b8ff05d974d6af0801c152b95f5fa5c06af9a35230c5bea2752f031f9bd" + "84bd844717b3add308a70dc777f90813c20b47b16385664eefc88449f04f2131" }, + { "b39f71aaa8a108", 1, + "258b8efa05b4a06b1e63c7a3f925c5ef11fa03e3d47d631bf4d474983783d8c0" + "b09449009e842fc9fa15de586c67cf8955a17d790b20f41dadf67ee8cdcdfce6" }, + { "dc28484ebfd293d62ac759d5754bdf502423e4d419fa79020805134b2ce3dff7" + "38c7556c91d810adbad8dd210f041296b73c2185d4646c97fc0a5b69ed49ac8c" + "7ced0bd1cfd7e3c3cca47374d189247da6811a40b0ab097067ed4ad40ade2e47" + "91e39204e398b3204971445822a1be0dd93af8", 1, + "615115d2e8b62e345adaa4bdb95395a3b4fe27d71c4a111b86c1841463c5f03d" + "6b20d164a39948ab08ae060720d05c10f6022e5c8caf2fa3bca2e04d9c539ded" }, + { "fd2203e467574e834ab07c9097ae164532f24be1eb5d88f1af7748ceff0d2c67" + "a21f4e4097f9d3bb4e9fbf97186e0db6db0100230a52b453d421f8ab9c9a6043" + "aa3295ea20d2f06a2f37470d8a99075f1b8a8336f6228cf08b5942fc1fb4299c" + "7d2480e8e82bce175540bdfad7752bc95b577f229515394f3ae5cec870a4b2f8", + 1, + "a21b1077d52b27ac545af63b32746c6e3c51cb0cb9f281eb9f3580a6d4996d5c" + "9917d2a6e484627a9d5a06fa1b25327a9d710e027387fc3e07d7c4d14c6086cc" }, + /* http://www.di-mgt.com.au/sha_testvectors.html */ + { ZEROES, 1, + "7be9fda48f4179e611c698a73cff09faf72869431efee6eaad14de0cb44bbf66" + "503f752b7a8eb17083355f3ce6eb7d2806f236b25af96a24e22b887405c20081" } +}; + +static void *xmalloc(size_t size) +{ + char * ret; + ret = malloc(size); + if (ret == NULL) { + perror("malloc"); + abort(); + } + return ret; +} + +static bool do_test(const struct test *t) +{ + struct sha512 h; + char got[128 + 1]; + bool passed; + size_t i, vector_len = strlen(t->vector) / 2; + void *vector = xmalloc(vector_len); + + hex_decode(t->vector, vector_len * 2, vector, vector_len); + + for (i = 0; i < t->repetitions; i++) { + sha512(&h, vector, vector_len); + if (t->repetitions > 1) + memcpy(vector, &h, sizeof(h)); + } + + hex_encode(&h, sizeof(h), got, sizeof(got)); + + passed = strcmp(t->expected, got) == 0; + free(vector); + return passed; +} + +int main(void) +{ + const size_t num_tests = sizeof(tests) / sizeof(tests[0]); + size_t i; + + /* This is how many tests you plan to run */ + plan_tests(num_tests); + + for (i = 0; i < num_tests; i++) + ok1(do_test(&tests[i])); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/tests/tcg/nios2/10m50-ghrd.ld b/tests/tcg/nios2/10m50-ghrd.ld new file mode 100644 index 0000000000..7db0d59ad7 --- /dev/null +++ b/tests/tcg/nios2/10m50-ghrd.ld @@ -0,0 +1,66 @@ +/* + * Link script for the Nios2 10m50-ghrd board. + * + * Copyright Linaro Ltd 2022 + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +MEMORY +{ + tpf (rx) : ORIGIN = 0xc0000000, LENGTH = 1K + ram (rwx) : ORIGIN = 0xc8000000, LENGTH = 128M +} + +PHDRS +{ + RAM PT_LOAD; +} + +ENTRY(_start) +EXTERN(_start) +EXTERN(_interrupt) +EXTERN(_fast_tlb_miss) + +SECTIONS +{ + /* Begin at the (hardcoded) _interrupt entry point. */ + .text 0xc8000120 : { + *(.text.intr) + *(.text .text.* .gnu.linkonce.t.*) + } >ram :RAM + + .rodata : ALIGN(4) { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } > ram :RAM + + .eh_frame_hdr : ALIGN (4) { + KEEP (*(.eh_frame_hdr)) + *(.eh_frame_entry .eh_frame_entry.*) + } >ram :RAM + .eh_frame : ALIGN (4) { + KEEP (*(.eh_frame)) *(.eh_frame.*) + } >ram :RAM + + .data : ALIGN(4) { + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(4); + _gp = ABSOLUTE(. + 0x8000); + *(.got.plt) *(.got) + *(.lit8) + *(.lit4) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } >ram :RAM + + .bss : ALIGN(4) { + __bss_start = ABSOLUTE(.); + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + __bss_end = ABSOLUTE(.); + } >ram :RAM + + __stack = ORIGIN(ram) + LENGTH(ram); +} diff --git a/tests/tcg/nios2/Makefile.softmmu-target b/tests/tcg/nios2/Makefile.softmmu-target new file mode 100644 index 0000000000..c3d0594a39 --- /dev/null +++ b/tests/tcg/nios2/Makefile.softmmu-target @@ -0,0 +1,33 @@ +# +# Nios2 system tests +# +# Copyright Linaro Ltd 2022 +# SPDX-License-Identifier: GPL-2.0-or-later +# + +NIOS2_SYSTEM_SRC = $(SRC_PATH)/tests/tcg/nios2 +VPATH += $(NIOS2_SYSTEM_SRC) + +# These objects provide the basic boot code and helper functions for all tests +CRT_OBJS = boot.o intr.o $(MINILIB_OBJS) +LINK_SCRIPT = $(NIOS2_SYSTEM_SRC)/10m50-ghrd.ld + +CFLAGS += -nostdlib -g -O0 $(MINILIB_INC) +LDFLAGS += -Wl,-T$(LINK_SCRIPT) -static -nostdlib $(CRT_OBJS) -lgcc + +%.o: %.S + $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@, AS, $@) + +%.o: %.c + $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@, CC, $@) + +# Build and link the tests +%: %.o $(LINK_SCRIPT) $(CRT_OBJS) + $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS), LD, $@) + +# FIXME: nios2 semihosting writes to stdout, not a chardev +QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting >$@.out -kernel + +memory: CFLAGS+=-DCHECK_UNALIGNED=0 +TESTS += $(MULTIARCH_TESTS) +TESTS += test-shadow-1 diff --git a/tests/tcg/nios2/Makefile.target b/tests/tcg/nios2/Makefile.target deleted file mode 100644 index b38e2352b7..0000000000 --- a/tests/tcg/nios2/Makefile.target +++ /dev/null @@ -1,11 +0,0 @@ -# nios2 specific test tweaks - -# Currently nios2 signal handling is broken -run-signals: signals - $(call skip-test, $<, "BROKEN") -run-plugin-signals-with-%: - $(call skip-test, $<, "BROKEN") -run-linux-test: linux-test - $(call skip-test, $<, "BROKEN") -run-plugin-linux-test-with-%: - $(call skip-test, $<, "BROKEN") diff --git a/tests/tcg/nios2/boot.S b/tests/tcg/nios2/boot.S new file mode 100644 index 0000000000..f6771cbc81 --- /dev/null +++ b/tests/tcg/nios2/boot.S @@ -0,0 +1,218 @@ +/* + * Minimal Nios2 system boot code. + * + * Copyright Linaro Ltd 2022 + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "semicall.h" + + .text + .set noat + +_start: + /* Linker script defines stack at end of ram. */ + movia sp, __stack + + /* Install trampoline to _fast_tlb_miss at hardcoded vector. */ + movia r4, 0xc0000100 + movia r5, _ftm_tramp + movi r6, .L__ftm_end - _ftm_tramp + call memcpy + + /* Zero the bss to satisfy C. */ + movia r4, __bss_start + movia r6, __bss_end + sub r6, r6, r4 + movi r5, 0 + call memset + + /* Test! */ + call main + + /* Exit with main's return value. */ + movi r4, HOSTED_EXIT + mov r5, r2 + semihosting_call + + .globl _start + .type _start, @function + .size _start, . - _start + +_ftm_tramp: + movia et, _fast_tlb_miss + jmp et +.L__ftm_end: + + .type _ftm_tramp, @function + .size _ftm_tramp, . - _ftm_tramp + +#define dst r4 +#define src r5 +#define len r6 + +memcpy: + /* Store return value right away, per API */ + mov r2, dst + + /* Check for both dst and src aligned. */ + or at, dst, src + andi at, at, 3 + bne at, zero, .L_mc_test1 + + /* Copy blocks of 8. */ + + movi at, 8 + bltu len, at, .L_mc_test4 + +.L_mc_loop8: + ldw r8, 0(src) + ldw r9, 4(src) + addi src, src, 8 + addi dst, dst, 8 + subi len, len, 8 + stw r8, -8(dst) + stw r9, -4(dst) + bgeu len, at, .L_mc_loop8 + + /* Copy final aligned block of 4. */ + +.L_mc_test4: + movi at, 4 + bltu len, at, .L_mc_test1 + + ldw r8, 0(src) + addi src, src, 4 + addi dst, dst, 4 + subi len, len, 4 + stw r8, -4(dst) + + /* Copy single bytes to finish. */ + +.L_mc_test1: + beq len, zero, .L_mc_done + +.L_mc_loop1: + ldb r8, 0(src) + addi src, src, 1 + addi dst, dst, 1 + subi len, len, 1 + stb r8, -1(dst) + bne len, zero, .L_mc_loop1 + +.L_mc_done: + ret + +#undef dst +#undef src +#undef len + + .global memcpy + .type memcpy, @function + .size memcpy, . - memcpy + +#define dst r4 +#define val r5 +#define len r6 + +memset: + /* Store return value right away, per API */ + mov r2, dst + + /* Check for small blocks; fall back to bytewise. */ + movi r3, 8 + bltu len, r3, .L_ms_test1 + + /* Replicate the byte across the word. */ + andi val, val, 0xff + slli at, val, 8 + or val, val, at + slli at, val, 16 + or val, val, at + + /* Check for destination alignment; realign if needed. */ + andi at, dst, 3 + bne at, zero, .L_ms_align + + /* Set blocks of 8. */ + +.L_ms_loop8: + stw val, 0(dst) + stw val, 4(dst) + addi dst, dst, 8 + subi len, len, 8 + bgeu len, r3, .L_ms_loop8 + + /* Set final aligned block of 4. */ + +.L_ms_test4: + movi at, 4 + bltu len, at, .L_ms_test1 + + stw r8, 0(dst) + addi dst, dst, 4 + subi len, len, 4 + stw r8, -4(dst) + + /* Set single bytes to finish. */ + +.L_ms_test1: + beq len, zero, .L_ms_done + +.L_ms_loop1: + stb r8, 0(dst) + addi dst, dst, 1 + subi len, len, 1 + bne len, zero, .L_ms_loop1 + +.L_ms_done: + ret + + /* Realign for a large block, len >= 8. */ +.L_ms_align: + andi at, dst, 1 + beq at, zero, 2f + + stb val, 0(dst) + addi dst, dst, 1 + subi len, len, 1 + +2: andi at, dst, 2 + beq at, zero, 4f + + sth val, 0(dst) + addi dst, dst, 2 + subi len, len, 2 + +4: bgeu len, r3, .L_ms_loop8 + br .L_ms_test4 + +#undef dst +#undef val +#undef len + + .global memset + .type memset, @function + .size memset, . - memset + +/* + * void __sys_outc(char c); + */ +__sys_outc: + subi sp, sp, 16 + stb r4, 0(sp) /* buffer[0] = c */ + movi at, 1 + stw at, 4(sp) /* STDOUT_FILENO */ + stw sp, 8(sp) /* buffer */ + stw at, 12(sp) /* len */ + + movi r4, HOSTED_WRITE + addi r5, sp, 4 + semihosting_call + + addi sp, sp, 16 + ret + + .global __sys_outc + .type __sys_outc, @function + .size __sys_outc, . - __sys_outc diff --git a/tests/tcg/nios2/intr.S b/tests/tcg/nios2/intr.S new file mode 100644 index 0000000000..c1730692ba --- /dev/null +++ b/tests/tcg/nios2/intr.S @@ -0,0 +1,31 @@ +/* + * Minimal Nios2 system boot code -- exit on interrupt. + * + * Copyright Linaro Ltd 2022 + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "semicall.h" + + .section .text.intr, "ax" + .global _interrupt + .type _interrupt, @function + +_interrupt: + rdctl r5, exception /* extract exception.CAUSE */ + srli r5, r5, 2 + movi r4, HOSTED_EXIT + semihosting_call + + .size _interrupt, . - _interrupt + + .text + .global _fast_tlb_miss + .type _fast_tlb_miss, @function + +_fast_tlb_miss: + movi r5, 32 + movi r4, HOSTED_EXIT + semihosting_call + + .size _fast_tlb_miss, . - _fast_tlb_miss diff --git a/tests/tcg/nios2/semicall.h b/tests/tcg/nios2/semicall.h new file mode 100644 index 0000000000..6ad4978099 --- /dev/null +++ b/tests/tcg/nios2/semicall.h @@ -0,0 +1,28 @@ +/* + * Nios2 semihosting interface. + * + * Copyright Linaro Ltd 2022 + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEMICALL_H +#define SEMICALL_H + +#define HOSTED_EXIT 0 +#define HOSTED_INIT_SIM 1 +#define HOSTED_OPEN 2 +#define HOSTED_CLOSE 3 +#define HOSTED_READ 4 +#define HOSTED_WRITE 5 +#define HOSTED_LSEEK 6 +#define HOSTED_RENAME 7 +#define HOSTED_UNLINK 8 +#define HOSTED_STAT 9 +#define HOSTED_FSTAT 10 +#define HOSTED_GETTIMEOFDAY 11 +#define HOSTED_ISATTY 12 +#define HOSTED_SYSTEM 13 + +#define semihosting_call break 1 + +#endif /* SEMICALL_H */ diff --git a/tests/tcg/nios2/test-shadow-1.S b/tests/tcg/nios2/test-shadow-1.S new file mode 100644 index 0000000000..79ef69db12 --- /dev/null +++ b/tests/tcg/nios2/test-shadow-1.S @@ -0,0 +1,40 @@ +/* + * Regression test for TCG indirect global lowering. + * + * Copyright Linaro Ltd 2022 + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "semicall.h" + + .text + .set noat + .align 2 + .globl main + .type main, @function + +main: + /* Initialize r0 in shadow register set 1. */ + movhi at, 1 /* PRS=1, CRS=0, RSIE=0, PIE=0 */ + wrctl status, at + wrprs zero, zero + + /* Change current register set to 1. */ + movi at, 1 << 10 /* PRS=0, CRS=1, RSIE=0, PIE=0 */ + wrctl estatus, at + movia ea, 1f + eret + + /* Load address for callr, then end TB. */ +1: movia at, 3f + br 2f + + /* Test case! TCG abort on indirect lowering across brcond. */ +2: callr at + + /* exit(0) */ +3: movi r4, HOSTED_EXIT + movi r5, 0 + semihosting_call + + .size main, . - main diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index 0368007028..babd209573 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -5,25 +5,28 @@ VPATH += $(SRC_PATH)/tests/tcg/ppc64 VPATH += $(SRC_PATH)/tests/tcg/ppc64le -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),) +ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) PPC64_TESTS=bcdsub non_signalling_xscv endif $(PPC64_TESTS): CFLAGS += -mpower8-vector -PPC64_TESTS += byte_reverse PPC64_TESTS += mtfsf -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER10),) + +ifneq ($(CROSS_CC_HAS_POWER10),) +PPC64_TESTS += byte_reverse sha512-vector +endif +byte_reverse: CFLAGS += -mcpu=power10 run-byte_reverse: QEMU_OPTS+=-cpu POWER10 run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10 -else -byte_reverse: - $(call skip-test, "BUILD of $@", "missing compiler support") -run-byte_reverse: - $(call skip-test, "RUN of byte_reverse", "not built") -run-plugin-byte_reverse-with-%: - $(call skip-test, "RUN of byte_reverse ($*)", "not built") -endif + +sha512-vector: CFLAGS +=-mcpu=power10 -O3 +sha512-vector: sha512.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-sha512-vector: QEMU_OPTS+=-cpu POWER10 +run-plugin-sha512-vector-with-%: QEMU_OPTS+=-cpu POWER10 PPC64_TESTS += signal_save_restore_xer +PPC64_TESTS += xxspltw TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target index 480ff0898d..5b0eb5e870 100644 --- a/tests/tcg/ppc64le/Makefile.target +++ b/tests/tcg/ppc64le/Makefile.target @@ -4,19 +4,27 @@ VPATH += $(SRC_PATH)/tests/tcg/ppc64le -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),) +ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) PPC64LE_TESTS=bcdsub non_signalling_xscv endif $(PPC64LE_TESTS): CFLAGS += -mpower8-vector -ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER10),) -PPC64LE_TESTS += byte_reverse +ifneq ($(CROSS_CC_HAS_POWER10),) +PPC64LE_TESTS += byte_reverse sha512-vector endif byte_reverse: CFLAGS += -mcpu=power10 run-byte_reverse: QEMU_OPTS+=-cpu POWER10 run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10 +sha512-vector: CFLAGS +=-mcpu=power10 -O3 +sha512-vector: sha512.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-sha512-vector: QEMU_OPTS+=-cpu POWER10 +run-plugin-sha512-vector-with-%: QEMU_OPTS+=-cpu POWER10 + PPC64LE_TESTS += mtfsf PPC64LE_TESTS += signal_save_restore_xer +PPC64LE_TESTS += xxspltw TESTS += $(PPC64LE_TESTS) diff --git a/tests/tcg/ppc64le/bcdsub.c b/tests/tcg/ppc64le/bcdsub.c index 8c188cae6d..87c8c44a44 100644 --- a/tests/tcg/ppc64le/bcdsub.c +++ b/tests/tcg/ppc64le/bcdsub.c @@ -1,6 +1,7 @@ #include #include #include +#include #define CRF_LT (1 << 3) #define CRF_GT (1 << 2) @@ -8,24 +9,50 @@ #define CRF_SO (1 << 0) #define UNDEF 0 -#define BCDSUB(vra, vrb, ps) \ - asm ("bcdsub. %1,%2,%3,%4;" \ - "mfocrf %0,0b10;" \ - : "=r" (cr), "=v" (vrt) \ - : "v" (vra), "v" (vrb), "i" (ps) \ - : ); +#ifdef __has_builtin +#if !__has_builtin(__builtin_bcdsub) +#define NO_BUILTIN_BCDSUB +#endif +#endif -#define TEST(vra, vrb, ps, exp_res, exp_cr6) \ - do { \ - __int128 vrt = 0; \ - int cr = 0; \ - BCDSUB(vra, vrb, ps); \ - if (exp_res) \ - assert(vrt == exp_res); \ - assert((cr >> 4) == exp_cr6); \ +#ifdef NO_BUILTIN_BCDSUB +#define BCDSUB(T, A, B, PS) \ + ".long 4 << 26 | (" #T ") << 21 | (" #A ") << 16 | (" #B ") << 11" \ + " | 1 << 10 | (" #PS ") << 9 | 65\n\t" +#else +#define BCDSUB(T, A, B, PS) "bcdsub. " #T ", " #A ", " #B ", " #PS "\n\t" +#endif + +#define TEST(AH, AL, BH, BL, PS, TH, TL, CR6) \ + do { \ + int cr = 0; \ + uint64_t th, tl; \ + /* \ + * Use GPR pairs to load the VSR values and place the resulting VSR and\ + * CR6 in th, tl, and cr. Note that we avoid newer instructions (e.g., \ + * mtvsrdd/mfvsrld) so we can run this test on POWER8 machines. \ + */ \ + asm ("mtvsrd 32, %3\n\t" \ + "mtvsrd 33, %4\n\t" \ + "xxmrghd 32, 32, 33\n\t" \ + "mtvsrd 33, %5\n\t" \ + "mtvsrd 34, %6\n\t" \ + "xxmrghd 33, 33, 34\n\t" \ + BCDSUB(0, 0, 1, PS) \ + "mfocrf %0, 0b10\n\t" \ + "mfvsrd %1, 32\n\t" \ + "xxswapd 32, 32\n\t" \ + "mfvsrd %2, 32\n\t" \ + : "=r" (cr), "=r" (th), "=r" (tl) \ + : "r" (AH), "r" (AL), "r" (BH), "r" (BL) \ + : "v0", "v1", "v2"); \ + if (TH != UNDEF || TL != UNDEF) { \ + assert(tl == TL); \ + assert(th == TH); \ + } \ + assert((cr >> 4) == CR6); \ } while (0) - /* * Unbounded result is equal to zero: * sign = (PS) ? 0b1111 : 0b1100 @@ -33,13 +60,13 @@ */ void test_bcdsub_eq(void) { - __int128 a, b; - /* maximum positive BCD value */ - a = b = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c); - - TEST(a, b, 0, 0xc, CRF_EQ); - TEST(a, b, 1, 0xf, CRF_EQ); + TEST(0x9999999999999999, 0x999999999999999c, + 0x9999999999999999, 0x999999999999999c, + 0, 0x0, 0xc, CRF_EQ); + TEST(0x9999999999999999, 0x999999999999999c, + 0x9999999999999999, 0x999999999999999c, + 1, 0x0, 0xf, CRF_EQ); } /* @@ -49,21 +76,16 @@ void test_bcdsub_eq(void) */ void test_bcdsub_gt(void) { - __int128 a, b, c; + /* maximum positive and negative one BCD values */ + TEST(0x9999999999999999, 0x999999999999999c, 0x0, 0x1d, 0, + 0x0, 0xc, (CRF_GT | CRF_SO)); + TEST(0x9999999999999999, 0x999999999999999c, 0x0, 0x1d, 1, + 0x0, 0xf, (CRF_GT | CRF_SO)); - /* maximum positive BCD value */ - a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c); - - /* negative one BCD value */ - b = (__int128) 0x1d; - - TEST(a, b, 0, 0xc, (CRF_GT | CRF_SO)); - TEST(a, b, 1, 0xf, (CRF_GT | CRF_SO)); - - c = (((__int128) 0x9999999999999999) << 64 | 0x999999999999998c); - - TEST(c, b, 0, a, CRF_GT); - TEST(c, b, 1, (a | 0x3), CRF_GT); + TEST(0x9999999999999999, 0x999999999999998c, 0x0, 0x1d, 0, + 0x9999999999999999, 0x999999999999999c, CRF_GT); + TEST(0x9999999999999999, 0x999999999999998c, 0x0, 0x1d, 1, + 0x9999999999999999, 0x999999999999999f, CRF_GT); } /* @@ -73,45 +95,27 @@ void test_bcdsub_gt(void) */ void test_bcdsub_lt(void) { - __int128 a, b; + /* positive zero and positive one BCD values */ + TEST(0x0, 0xc, 0x0, 0x1c, 0, 0x0, 0x1d, CRF_LT); + TEST(0x0, 0xc, 0x0, 0x1c, 1, 0x0, 0x1d, CRF_LT); - /* positive zero BCD value */ - a = (__int128) 0xc; - - /* positive one BCD value */ - b = (__int128) 0x1c; - - TEST(a, b, 0, 0x1d, CRF_LT); - TEST(a, b, 1, 0x1d, CRF_LT); - - /* maximum negative BCD value */ - a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999d); - - /* positive one BCD value */ - b = (__int128) 0x1c; - - TEST(a, b, 0, 0xd, (CRF_LT | CRF_SO)); - TEST(a, b, 1, 0xd, (CRF_LT | CRF_SO)); + /* maximum negative and positive one BCD values */ + TEST(0x9999999999999999, 0x999999999999999d, 0x0, 0x1c, 0, + 0x0, 0xd, (CRF_LT | CRF_SO)); + TEST(0x9999999999999999, 0x999999999999999d, 0x0, 0x1c, 1, + 0x0, 0xd, (CRF_LT | CRF_SO)); } void test_bcdsub_invalid(void) { - __int128 a, b; + TEST(0x0, 0x1c, 0x0, 0xf00, 0, UNDEF, UNDEF, CRF_SO); + TEST(0x0, 0x1c, 0x0, 0xf00, 1, UNDEF, UNDEF, CRF_SO); - /* positive one BCD value */ - a = (__int128) 0x1c; - b = 0xf00; + TEST(0x0, 0xf00, 0x0, 0x1c, 0, UNDEF, UNDEF, CRF_SO); + TEST(0x0, 0xf00, 0x0, 0x1c, 1, UNDEF, UNDEF, CRF_SO); - TEST(a, b, 0, UNDEF, CRF_SO); - TEST(a, b, 1, UNDEF, CRF_SO); - - TEST(b, a, 0, UNDEF, CRF_SO); - TEST(b, a, 1, UNDEF, CRF_SO); - - a = 0xbad; - - TEST(a, b, 0, UNDEF, CRF_SO); - TEST(a, b, 1, UNDEF, CRF_SO); + TEST(0x0, 0xbad, 0x0, 0xf00, 0, UNDEF, UNDEF, CRF_SO); + TEST(0x0, 0xbad, 0x0, 0xf00, 1, UNDEF, UNDEF, CRF_SO); } int main(void) diff --git a/tests/tcg/ppc64le/mtfsf.c b/tests/tcg/ppc64le/mtfsf.c index b3d31f3637..bed5b1afa4 100644 --- a/tests/tcg/ppc64le/mtfsf.c +++ b/tests/tcg/ppc64le/mtfsf.c @@ -1,8 +1,12 @@ #include +#include #include #include #include +#define MTFSF(FLM, FRB) asm volatile ("mtfsf %0, %1" :: "i" (FLM), "f" (FRB)) +#define MFFS(FRT) asm("mffs %0" : "=f" (FRT)) + #define FPSCR_VE 7 /* Floating-point invalid operation exception enable */ #define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */ #define FPSCR_FI 17 /* Floating-point fraction inexact */ @@ -21,10 +25,7 @@ void sigfpe_handler(int sig, siginfo_t *si, void *ucontext) int main(void) { - union { - double d; - long long ll; - } fpscr; + uint64_t fpscr; struct sigaction sa = { .sa_sigaction = sigfpe_handler, @@ -40,10 +41,9 @@ int main(void) prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE); /* First test if the FI bit is being set correctly */ - fpscr.ll = FP_FI; - __builtin_mtfsf(0b11111111, fpscr.d); - fpscr.d = __builtin_mffs(); - assert((fpscr.ll & FP_FI) != 0); + MTFSF(0b11111111, FP_FI); + MFFS(fpscr); + assert((fpscr & FP_FI) != 0); /* Then test if the deferred exception is being called correctly */ sigaction(SIGFPE, &sa, NULL); @@ -54,8 +54,7 @@ int main(void) * But if a different exception is chosen si_code check should * change accordingly. */ - fpscr.ll = FP_VE | FP_VXSOFT; - __builtin_mtfsf(0b11111111, fpscr.d); + MTFSF(0b11111111, FP_VE | FP_VXSOFT); return 1; } diff --git a/tests/tcg/ppc64le/non_signalling_xscv.c b/tests/tcg/ppc64le/non_signalling_xscv.c index 91e25cad46..836df71ef0 100644 --- a/tests/tcg/ppc64le/non_signalling_xscv.c +++ b/tests/tcg/ppc64le/non_signalling_xscv.c @@ -6,16 +6,16 @@ #define TEST(INSN, B_HI, B_LO, T_HI, T_LO) \ do { \ uint64_t th, tl, bh = B_HI, bl = B_LO; \ - asm("mtvsrd 0, %2\n\t" \ - "mtvsrd 1, %3\n\t" \ - "xxmrghd 0, 0, 1\n\t" \ - INSN " 0, 0\n\t" \ - "mfvsrd %0, 0\n\t" \ - "xxswapd 0, 0\n\t" \ - "mfvsrd %1, 0\n\t" \ + asm("mtvsrd 32, %2\n\t" \ + "mtvsrd 33, %3\n\t" \ + "xxmrghd 32, 32, 33\n\t" \ + INSN " 32, 32\n\t" \ + "mfvsrd %0, 32\n\t" \ + "xxswapd 32, 32\n\t" \ + "mfvsrd %1, 32\n\t" \ : "=r" (th), "=r" (tl) \ : "r" (bh), "r" (bl) \ - : "vs0", "vs1"); \ + : "v0", "v1"); \ printf(INSN "(0x%016" PRIx64 "%016" PRIx64 ") = 0x%016" PRIx64 \ "%016" PRIx64 "\n", bh, bl, th, tl); \ assert(th == T_HI && tl == T_LO); \ diff --git a/tests/tcg/ppc64le/signal_save_restore_xer.c b/tests/tcg/ppc64le/signal_save_restore_xer.c index e4f8a07dd7..9227f4f455 100644 --- a/tests/tcg/ppc64le/signal_save_restore_xer.c +++ b/tests/tcg/ppc64le/signal_save_restore_xer.c @@ -11,7 +11,7 @@ uint64_t saved; -void sigill_handler(int sig, siginfo_t *si, void *ucontext) +void sigtrap_handler(int sig, siginfo_t *si, void *ucontext) { ucontext_t *uc = ucontext; uc->uc_mcontext.regs->nip += 4; @@ -23,14 +23,14 @@ int main(void) { uint64_t initial = XER_CA | XER_CA32, restored; struct sigaction sa = { - .sa_sigaction = sigill_handler, + .sa_sigaction = sigtrap_handler, .sa_flags = SA_SIGINFO }; - sigaction(SIGILL, &sa, NULL); + sigaction(SIGTRAP, &sa, NULL); asm("mtspr 1, %1\n\t" - ".long 0x0\n\t" + "trap\n\t" "mfspr %0, 1\n\t" : "=r" (restored) : "r" (initial)); diff --git a/tests/tcg/ppc64le/xxspltw.c b/tests/tcg/ppc64le/xxspltw.c new file mode 100644 index 0000000000..4cff78bfdc --- /dev/null +++ b/tests/tcg/ppc64le/xxspltw.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#define WORD_A 0xAAAAAAAAUL +#define WORD_B 0xBBBBBBBBUL +#define WORD_C 0xCCCCCCCCUL +#define WORD_D 0xDDDDDDDDUL + +#define DWORD_HI (WORD_A << 32 | WORD_B) +#define DWORD_LO (WORD_C << 32 | WORD_D) + +#define TEST(HI, LO, UIM, RES) \ + do { \ + union { \ + uint64_t u; \ + double f; \ + } h = { .u = HI }, l = { .u = LO }; \ + /* \ + * Use a pair of FPRs to load the VSR avoiding insns \ + * newer than xxswapd. \ + */ \ + asm("xxmrghd 32, %0, %1\n\t" \ + "xxspltw 32, 32, %2\n\t" \ + "xxmrghd %0, 32, %0\n\t" \ + "xxswapd 32, 32\n\t" \ + "xxmrghd %1, 32, %1\n\t" \ + : "+f" (h.f), "+f" (l.f) \ + : "i" (UIM) \ + : "v0"); \ + printf("xxspltw(0x%016" PRIx64 "%016" PRIx64 ", %d) =" \ + " %016" PRIx64 "%016" PRIx64 "\n", HI, LO, UIM, \ + h.u, l.u); \ + assert(h.u == (RES)); \ + assert(l.u == (RES)); \ + } while (0) + +int main(void) +{ + TEST(DWORD_HI, DWORD_LO, 0, WORD_A << 32 | WORD_A); + TEST(DWORD_HI, DWORD_LO, 1, WORD_B << 32 | WORD_B); + TEST(DWORD_HI, DWORD_LO, 2, WORD_C << 32 | WORD_C); + TEST(DWORD_HI, DWORD_LO, 3, WORD_D << 32 | WORD_D); + return 0; +} diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 1a7238b4eb..3124172736 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -7,11 +7,23 @@ TESTS+=ipm TESTS+=exrl-trt TESTS+=exrl-trtr TESTS+=pack +TESTS+=mie3-compl +TESTS+=mie3-mvcrl +TESTS+=mie3-sel TESTS+=mvo TESTS+=mvc TESTS+=shift TESTS+=trap TESTS+=signals-s390x +TESTS+=branch-relative-long + +VECTOR_TESTS=vxeh2_vs +VECTOR_TESTS+=vxeh2_vcvt +VECTOR_TESTS+=vxeh2_vlstr +$(VECTOR_TESTS): CFLAGS+=-march=z15 -O2 + +TESTS+=$(if $(shell $(CC) -march=z15 -S -o /dev/null -xc /dev/null \ + >/dev/null 2>&1 && echo OK),$(VECTOR_TESTS)) ifneq ($(HAVE_GDB_BIN),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py @@ -25,3 +37,10 @@ run-gdbstub-signals-s390x: signals-s390x EXTRA_RUNS += run-gdbstub-signals-s390x endif + +# MVX versions of sha512 +sha512-mvx: CFLAGS=-march=z13 -mvx -O3 +sha512-mvx: sha512.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +TESTS+=sha512-mvx diff --git a/tests/tcg/s390x/branch-relative-long.c b/tests/tcg/s390x/branch-relative-long.c new file mode 100644 index 0000000000..8ce9f1c2e5 --- /dev/null +++ b/tests/tcg/s390x/branch-relative-long.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#define DEFINE_ASM(_name, _code) \ + extern const char _name[]; \ + extern const char _name ## _end[]; \ + asm(" .globl " #_name "\n" \ + #_name ":\n" \ + " " _code "\n" \ + " .globl " #_name "_end\n" \ + #_name "_end:\n"); + +DEFINE_ASM(br_r14, "br %r14"); +DEFINE_ASM(brasl_r0, "brasl %r0,-0x100000000"); +DEFINE_ASM(brcl_0xf, "brcl 0xf,-0x100000000"); + +struct test { + const char *code; + const char *code_end; +}; + +static const struct test tests[] = { + { + .code = brasl_r0, + .code_end = brasl_r0_end, + }, + { + .code = brcl_0xf, + .code_end = brcl_0xf_end, + }, +}; + +int main(void) +{ + unsigned char *buf; + size_t length = 0; + size_t i; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + size_t test_length = 0x100000000 + (tests[i].code_end - tests[i].code); + + if (test_length > length) { + length = test_length; + } + } + + buf = mmap(NULL, length, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + if (buf == MAP_FAILED) { + perror("SKIP: mmap() failed"); + return 0; + } + + memcpy(buf, br_r14, br_r14_end - br_r14); + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + void (*code)(void) = (void *)(buf + 0x100000000); + + memcpy(code, tests[i].code, tests[i].code_end - tests[i].code); + code(); + memset(code, 0, tests[i].code_end - tests[i].code); + } + + munmap(buf, length); + + return 0; +} diff --git a/tests/tcg/s390x/exrl-trt.c b/tests/tcg/s390x/exrl-trt.c index 16711a3181..451f777b9d 100644 --- a/tests/tcg/s390x/exrl-trt.c +++ b/tests/tcg/s390x/exrl-trt.c @@ -5,8 +5,8 @@ int main(void) { char op1[] = "hello"; char op2[256]; - uint64_t r1 = 0xffffffffffffffffull; - uint64_t r2 = 0xffffffffffffffffull; + register uint64_t r1 asm("r1") = 0xffffffffffffffffull; + register uint64_t r2 asm("r2") = 0xffffffffffffffffull; uint64_t cc; int i; @@ -21,8 +21,6 @@ int main(void) " j 2f\n" "1: trt 0(1,%[op1]),%[op2]\n" "2: exrl %[op1_len],1b\n" - " lgr %[r1],%%r1\n" - " lgr %[r2],%%r2\n" " ipm %[cc]\n" : [r1] "+r" (r1), [r2] "+r" (r2), @@ -30,7 +28,7 @@ int main(void) : [op1] "a" (&op1), [op1_len] "a" (5), [op2] "Q" (op2) - : "r1", "r2", "cc"); + : "cc"); cc = (cc >> 28) & 3; if (cc != 2) { write(1, "bad cc\n", 7); diff --git a/tests/tcg/s390x/exrl-trtr.c b/tests/tcg/s390x/exrl-trtr.c index 5f30cda6bd..422f7f385a 100644 --- a/tests/tcg/s390x/exrl-trtr.c +++ b/tests/tcg/s390x/exrl-trtr.c @@ -5,8 +5,8 @@ int main(void) { char op1[] = {0, 1, 2, 3}; char op2[256]; - uint64_t r1 = 0xffffffffffffffffull; - uint64_t r2 = 0xffffffffffffffffull; + register uint64_t r1 asm("r1") = 0xffffffffffffffffull; + register uint64_t r2 asm("r2") = 0xffffffffffffffffull; uint64_t cc; int i; @@ -21,8 +21,6 @@ int main(void) " j 2f\n" "1: trtr 3(1,%[op1]),%[op2]\n" "2: exrl %[op1_len],1b\n" - " lgr %[r1],%%r1\n" - " lgr %[r2],%%r2\n" " ipm %[cc]\n" : [r1] "+r" (r1), [r2] "+r" (r2), @@ -30,7 +28,7 @@ int main(void) : [op1] "a" (&op1), [op1_len] "a" (3), [op2] "Q" (op2) - : "r1", "r2", "cc"); + : "cc"); cc = (cc >> 28) & 3; if (cc != 1) { write(1, "bad cc\n", 7); diff --git a/tests/tcg/s390x/mie3-compl.c b/tests/tcg/s390x/mie3-compl.c new file mode 100644 index 0000000000..35649f3b02 --- /dev/null +++ b/tests/tcg/s390x/mie3-compl.c @@ -0,0 +1,48 @@ +#include + +#define FbinOp(S, ASM) uint64_t S(uint64_t a, uint64_t b) \ +{ \ + uint64_t res = 0; \ + asm ("llihf %[res],801\n" ASM \ + : [res]"=&r"(res) : [a]"r"(a), [b]"r"(b) : "cc"); \ + return res; \ +} + +/* AND WITH COMPLEMENT */ +FbinOp(_ncrk, ".insn rrf, 0xB9F50000, %[res], %[b], %[a], 0\n") +FbinOp(_ncgrk, ".insn rrf, 0xB9E50000, %[res], %[b], %[a], 0\n") + +/* NAND */ +FbinOp(_nnrk, ".insn rrf, 0xB9740000, %[res], %[b], %[a], 0\n") +FbinOp(_nngrk, ".insn rrf, 0xB9640000, %[res], %[b], %[a], 0\n") + +/* NOT XOR */ +FbinOp(_nxrk, ".insn rrf, 0xB9770000, %[res], %[b], %[a], 0\n") +FbinOp(_nxgrk, ".insn rrf, 0xB9670000, %[res], %[b], %[a], 0\n") + +/* NOR */ +FbinOp(_nork, ".insn rrf, 0xB9760000, %[res], %[b], %[a], 0\n") +FbinOp(_nogrk, ".insn rrf, 0xB9660000, %[res], %[b], %[a], 0\n") + +/* OR WITH COMPLEMENT */ +FbinOp(_ocrk, ".insn rrf, 0xB9750000, %[res], %[b], %[a], 0\n") +FbinOp(_ocgrk, ".insn rrf, 0xB9650000, %[res], %[b], %[a], 0\n") + +int main(int argc, char *argv[]) +{ + if (_ncrk(0xFF88, 0xAA11) != 0x0000032100000011ull || + _nnrk(0xFF88, 0xAA11) != 0x00000321FFFF55FFull || + _nork(0xFF88, 0xAA11) != 0x00000321FFFF0066ull || + _nxrk(0xFF88, 0xAA11) != 0x00000321FFFFAA66ull || + _ocrk(0xFF88, 0xAA11) != 0x00000321FFFFAA77ull || + _ncgrk(0xFF88, 0xAA11) != 0x0000000000000011ull || + _nngrk(0xFF88, 0xAA11) != 0xFFFFFFFFFFFF55FFull || + _nogrk(0xFF88, 0xAA11) != 0xFFFFFFFFFFFF0066ull || + _nxgrk(0xFF88, 0xAA11) != 0xFFFFFFFFFFFFAA66ull || + _ocgrk(0xFF88, 0xAA11) != 0xFFFFFFFFFFFFAA77ull) + { + return 1; + } + + return 0; +} diff --git a/tests/tcg/s390x/mie3-mvcrl.c b/tests/tcg/s390x/mie3-mvcrl.c new file mode 100644 index 0000000000..93c7b0a290 --- /dev/null +++ b/tests/tcg/s390x/mie3-mvcrl.c @@ -0,0 +1,29 @@ +#include +#include + + +static inline void mvcrl_8(const char *dst, const char *src) +{ + asm volatile ( + "llill %%r0, 8\n" + ".insn sse, 0xE50A00000000, 0(%[dst]), 0(%[src])" + : : [dst] "d" (dst), [src] "d" (src) + : "r0", "memory"); +} + + +int main(int argc, char *argv[]) +{ + const char *alpha = "abcdefghijklmnop"; + + /* array missing 'i' */ + char tstr[17] = "abcdefghjklmnop\0" ; + + /* mvcrl reference use: 'open a hole in an array' */ + mvcrl_8(tstr + 9, tstr + 8); + + /* place missing 'i' */ + tstr[8] = 'i'; + + return strncmp(alpha, tstr, 16ul); +} diff --git a/tests/tcg/s390x/mie3-sel.c b/tests/tcg/s390x/mie3-sel.c new file mode 100644 index 0000000000..0dfd532ed4 --- /dev/null +++ b/tests/tcg/s390x/mie3-sel.c @@ -0,0 +1,33 @@ +#include + + +#define Fi3(S, ASM) uint64_t S(uint64_t a, uint64_t b, uint64_t c) \ +{ \ +asm volatile ( \ + "ltgr %[c], %[c]\n" \ + ASM \ + : [c] "+r" (c) \ + : [a] "r" (a) \ + , [b] "r" (b) \ +); \ + return c; \ +} + +Fi3 (_selre, ".insn rrf, 0xB9F00000, %[c], %[b], %[a], 8\n") +Fi3 (_selgrz, ".insn rrf, 0xB9E30000, %[c], %[b], %[a], 8\n") +Fi3 (_selfhrnz, ".insn rrf, 0xB9C00000, %[c], %[b], %[a], 7\n") + + +int main(int argc, char *argv[]) +{ + uint64_t a = ~0, b = ~0, c = ~0; + + a = _selre(0x066600000066ull, 0x066600000006ull, a); + b = _selgrz(0xF00D00000005ull, 0xF00D00000055ull, b); + c = _selfhrnz(0x043200000044ull, 0x065400000004ull, c); + + return (int) ( + (0xFFFFFFFF00000066ull != a) || + (0x0000F00D00000005ull != b) || + (0x00000654FFFFFFFFull != c)); +} diff --git a/tests/tcg/s390x/mvc.c b/tests/tcg/s390x/mvc.c index aa552d52e5..7ae4c44550 100644 --- a/tests/tcg/s390x/mvc.c +++ b/tests/tcg/s390x/mvc.c @@ -20,8 +20,8 @@ static inline void mvc_256(const char *dst, const char *src) asm volatile ( " mvc 0(256,%[dst]),0(%[src])\n" : - : [dst] "d" (dst), - [src] "d" (src) + : [dst] "a" (dst), + [src] "a" (src) : "memory"); } diff --git a/tests/tcg/s390x/mvo.c b/tests/tcg/s390x/mvo.c index 5546fe2a97..0c3ecdde2e 100644 --- a/tests/tcg/s390x/mvo.c +++ b/tests/tcg/s390x/mvo.c @@ -11,8 +11,8 @@ int main(void) asm volatile ( " mvo 0(4,%[dest]),0(3,%[src])\n" : - : [dest] "d" (dest + 1), - [src] "d" (src + 1) + : [dest] "a" (dest + 1), + [src] "a" (src + 1) : "memory"); for (i = 0; i < sizeof(expected); i++) { diff --git a/tests/tcg/s390x/pack.c b/tests/tcg/s390x/pack.c index 4be36f29a7..55e7e214e8 100644 --- a/tests/tcg/s390x/pack.c +++ b/tests/tcg/s390x/pack.c @@ -9,7 +9,7 @@ int main(void) asm volatile( " pack 2(4,%[data]),2(4,%[data])\n" : - : [data] "r" (&data[0]) + : [data] "a" (&data[0]) : "memory"); for (i = 0; i < 8; i++) { if (data[i] != exp[i]) { diff --git a/tests/tcg/s390x/vx.h b/tests/tcg/s390x/vx.h new file mode 100644 index 0000000000..02e7fd518a --- /dev/null +++ b/tests/tcg/s390x/vx.h @@ -0,0 +1,19 @@ +#ifndef QEMU_TESTS_S390X_VX_H +#define QEMU_TESTS_S390X_VX_H + +typedef union S390Vector { + uint64_t d[2]; /* doubleword */ + uint32_t w[4]; /* word */ + uint16_t h[8]; /* halfword */ + uint8_t b[16]; /* byte */ + float f[4]; /* float32 */ + double fd[2]; /* float64 */ + __uint128_t v; +} S390Vector; + +#define ES8 0 +#define ES16 1 +#define ES32 2 +#define ES64 3 + +#endif /* QEMU_TESTS_S390X_VX_H */ diff --git a/tests/tcg/s390x/vxeh2_vcvt.c b/tests/tcg/s390x/vxeh2_vcvt.c new file mode 100644 index 0000000000..d6e551c16e --- /dev/null +++ b/tests/tcg/s390x/vxeh2_vcvt.c @@ -0,0 +1,88 @@ +/* + * vxeh2_vcvt: vector-enhancements facility 2 vector convert * + */ +#include +#include "vx.h" + +#define M_S 8 +#define M4_XxC 4 +#define M4_def M4_XxC + +static inline void vcfps(S390Vector *v1, S390Vector *v2, + const uint8_t m3, const uint8_t m4, const uint8_t m5) +{ + asm volatile("vcfps %[v1], %[v2], %[m3], %[m4], %[m5]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [m3] "i" (m3) + , [m4] "i" (m4) + , [m5] "i" (m5)); +} + +static inline void vcfpl(S390Vector *v1, S390Vector *v2, + const uint8_t m3, const uint8_t m4, const uint8_t m5) +{ + asm volatile("vcfpl %[v1], %[v2], %[m3], %[m4], %[m5]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [m3] "i" (m3) + , [m4] "i" (m4) + , [m5] "i" (m5)); +} + +static inline void vcsfp(S390Vector *v1, S390Vector *v2, + const uint8_t m3, const uint8_t m4, const uint8_t m5) +{ + asm volatile("vcsfp %[v1], %[v2], %[m3], %[m4], %[m5]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [m3] "i" (m3) + , [m4] "i" (m4) + , [m5] "i" (m5)); +} + +static inline void vclfp(S390Vector *v1, S390Vector *v2, + const uint8_t m3, const uint8_t m4, const uint8_t m5) +{ + asm volatile("vclfp %[v1], %[v2], %[m3], %[m4], %[m5]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [m3] "i" (m3) + , [m4] "i" (m4) + , [m5] "i" (m5)); +} + +int main(int argc, char *argv[]) +{ + S390Vector vd; + S390Vector vs_i32 = { .w[0] = 1, .w[1] = 64, .w[2] = 1024, .w[3] = -10 }; + S390Vector vs_u32 = { .w[0] = 2, .w[1] = 32, .w[2] = 4096, .w[3] = 8888 }; + S390Vector vs_f32 = { .f[0] = 3.987, .f[1] = 5.123, + .f[2] = 4.499, .f[3] = 0.512 }; + + vd.d[0] = vd.d[1] = 0; + vcfps(&vd, &vs_i32, 2, M4_def, 0); + if (1 != vd.f[0] || 1024 != vd.f[2] || 64 != vd.f[1] || -10 != vd.f[3]) { + return 1; + } + + vd.d[0] = vd.d[1] = 0; + vcfpl(&vd, &vs_u32, 2, M4_def, 0); + if (2 != vd.f[0] || 4096 != vd.f[2] || 32 != vd.f[1] || 8888 != vd.f[3]) { + return 1; + } + + vd.d[0] = vd.d[1] = 0; + vcsfp(&vd, &vs_f32, 2, M4_def, 0); + if (4 != vd.w[0] || 4 != vd.w[2] || 5 != vd.w[1] || 1 != vd.w[3]) { + return 1; + } + + vd.d[0] = vd.d[1] = 0; + vclfp(&vd, &vs_f32, 2, M4_def, 0); + if (4 != vd.w[0] || 4 != vd.w[2] || 5 != vd.w[1] || 1 != vd.w[3]) { + return 1; + } + + return 0; +} diff --git a/tests/tcg/s390x/vxeh2_vlstr.c b/tests/tcg/s390x/vxeh2_vlstr.c new file mode 100644 index 0000000000..cf971150cf --- /dev/null +++ b/tests/tcg/s390x/vxeh2_vlstr.c @@ -0,0 +1,139 @@ +/* + * vxeh2_vlstr: vector-enhancements facility 2 vector load/store reversed * + */ +#include +#include "vx.h" + +#define vtst(v1, v2) \ + if (v1.d[0] != v2.d[0] || v1.d[1] != v2.d[1]) { \ + return 1; \ + } + +static inline void vler(S390Vector *v1, const void *va, uint8_t m3) +{ + asm volatile("vler %[v1], 0(%[va]), %[m3]\n" + : [v1] "+v" (v1->v) + : [va] "a" (va) + , [m3] "i" (m3) + : "memory"); +} + +static inline void vster(S390Vector *v1, const void *va, uint8_t m3) +{ + asm volatile("vster %[v1], 0(%[va]), %[m3]\n" + : [va] "+a" (va) + : [v1] "v" (v1->v) + , [m3] "i" (m3) + : "memory"); +} + +static inline void vlbr(S390Vector *v1, void *va, const uint8_t m3) +{ + asm volatile("vlbr %[v1], 0(%[va]), %[m3]\n" + : [v1] "+v" (v1->v) + : [va] "a" (va) + , [m3] "i" (m3) + : "memory"); +} + +static inline void vstbr(S390Vector *v1, void *va, const uint8_t m3) +{ + asm volatile("vstbr %[v1], 0(%[va]), %[m3]\n" + : [va] "+a" (va) + : [v1] "v" (v1->v) + , [m3] "i" (m3) + : "memory"); +} + + +static inline void vlebrh(S390Vector *v1, void *va, const uint8_t m3) +{ + asm volatile("vlebrh %[v1], 0(%[va]), %[m3]\n" + : [v1] "+v" (v1->v) + : [va] "a" (va) + , [m3] "i" (m3) + : "memory"); +} + +static inline void vstebrh(S390Vector *v1, void *va, const uint8_t m3) +{ + asm volatile("vstebrh %[v1], 0(%[va]), %[m3]\n" + : [va] "+a" (va) + : [v1] "v" (v1->v) + , [m3] "i" (m3) + : "memory"); +} + +static inline void vllebrz(S390Vector *v1, void *va, const uint8_t m3) +{ + asm volatile("vllebrz %[v1], 0(%[va]), %[m3]\n" + : [v1] "+v" (v1->v) + : [va] "a" (va) + , [m3] "i" (m3) + : "memory"); +} + +static inline void vlbrrep(S390Vector *v1, void *va, const uint8_t m3) +{ + asm volatile("vlbrrep %[v1], 0(%[va]), %[m3]\n" + : [v1] "+v" (v1->v) + : [va] "a" (va) + , [m3] "i" (m3) + : "memory"); +} + +int main(int argc, char *argv[]) +{ + S390Vector vd = { .d[0] = 0, .d[1] = 0 }; + S390Vector vs = { .d[0] = 0x8FEEDDCCBBAA9988ull, + .d[1] = 0x7766554433221107ull }; + + const S390Vector vt_v_er16 = { + .h[0] = 0x1107, .h[1] = 0x3322, .h[2] = 0x5544, .h[3] = 0x7766, + .h[4] = 0x9988, .h[5] = 0xBBAA, .h[6] = 0xDDCC, .h[7] = 0x8FEE }; + + const S390Vector vt_v_br16 = { + .h[0] = 0xEE8F, .h[1] = 0xCCDD, .h[2] = 0xAABB, .h[3] = 0x8899, + .h[4] = 0x6677, .h[5] = 0x4455, .h[6] = 0x2233, .h[7] = 0x0711 }; + + int ix; + uint64_t ss64 = 0xFEEDFACE0BADBEEFull, sd64 = 0; + + vler(&vd, &vs, ES16); + vtst(vd, vt_v_er16); + + vster(&vs, &vd, ES16); + vtst(vd, vt_v_er16); + + vlbr(&vd, &vs, ES16); + vtst(vd, vt_v_br16); + + vstbr(&vs, &vd, ES16); + vtst(vd, vt_v_br16); + + vlebrh(&vd, &ss64, 5); + if (0xEDFE != vd.h[5]) { + return 1; + } + + vstebrh(&vs, (uint8_t *)&sd64 + 4, 7); + if (0x0000000007110000ull != sd64) { + return 1; + } + + vllebrz(&vd, (uint8_t *)&ss64 + 3, 2); + for (ix = 0; ix < 4; ix++) { + if (vd.w[ix] != (ix != 1 ? 0 : 0xBEAD0BCE)) { + return 1; + } + } + + vlbrrep(&vd, (uint8_t *)&ss64 + 4, 1); + for (ix = 0; ix < 8; ix++) { + if (0xAD0B != vd.h[ix]) { + return 1; + } + } + + return 0; +} diff --git a/tests/tcg/s390x/vxeh2_vs.c b/tests/tcg/s390x/vxeh2_vs.c new file mode 100644 index 0000000000..b7ef419d79 --- /dev/null +++ b/tests/tcg/s390x/vxeh2_vs.c @@ -0,0 +1,93 @@ +/* + * vxeh2_vs: vector-enhancements facility 2 vector shift + */ +#include +#include "vx.h" + +#define vtst(v1, v2) \ + if (v1.d[0] != v2.d[0] || v1.d[1] != v2.d[1]) { \ + return 1; \ + } + +static inline void vsl(S390Vector *v1, S390Vector *v2, S390Vector *v3) +{ + asm volatile("vsl %[v1], %[v2], %[v3]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [v3] "v" (v3->v)); +} + +static inline void vsra(S390Vector *v1, S390Vector *v2, S390Vector *v3) +{ + asm volatile("vsra %[v1], %[v2], %[v3]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [v3] "v" (v3->v)); +} + +static inline void vsrl(S390Vector *v1, S390Vector *v2, S390Vector *v3) +{ + asm volatile("vsrl %[v1], %[v2], %[v3]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [v3] "v" (v3->v)); +} + +static inline void vsld(S390Vector *v1, S390Vector *v2, + S390Vector *v3, const uint8_t I) +{ + asm volatile("vsld %[v1], %[v2], %[v3], %[I]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [v3] "v" (v3->v) + , [I] "i" (I & 7)); +} + +static inline void vsrd(S390Vector *v1, S390Vector *v2, + S390Vector *v3, const uint8_t I) +{ + asm volatile("vsrd %[v1], %[v2], %[v3], %[I]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [v3] "v" (v3->v) + , [I] "i" (I & 7)); +} + +int main(int argc, char *argv[]) +{ + const S390Vector vt_vsl = { .d[0] = 0x7FEDBB32D5AA311Dull, + .d[1] = 0xBB65AA10912220C0ull }; + const S390Vector vt_vsra = { .d[0] = 0xF1FE6E7399AA5466ull, + .d[1] = 0x0E762A5188221044ull }; + const S390Vector vt_vsrl = { .d[0] = 0x11FE6E7399AA5466ull, + .d[1] = 0x0E762A5188221044ull }; + const S390Vector vt_vsld = { .d[0] = 0x7F76EE65DD54CC43ull, + .d[1] = 0xBB32AA2199108838ull }; + const S390Vector vt_vsrd = { .d[0] = 0x0E060802040E000Aull, + .d[1] = 0x0C060802040E000Aull }; + S390Vector vs = { .d[0] = 0x8FEEDDCCBBAA9988ull, + .d[1] = 0x7766554433221107ull }; + S390Vector vd = { .d[0] = 0, .d[1] = 0 }; + S390Vector vsi = { .d[0] = 0, .d[1] = 0 }; + + for (int ix = 0; ix < 16; ix++) { + vsi.b[ix] = (1 + (5 ^ ~ix)) & 7; + } + + vsl(&vd, &vs, &vsi); + vtst(vd, vt_vsl); + + vsra(&vd, &vs, &vsi); + vtst(vd, vt_vsra); + + vsrl(&vd, &vs, &vsi); + vtst(vd, vt_vsrl); + + vsld(&vd, &vs, &vsi, 3); + vtst(vd, vt_vsld); + + vsrd(&vd, &vs, &vsi, 15); + vtst(vd, vt_vsrd); + + return 0; +} diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 620ccc23c1..35ebe6b4e3 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -20,5 +20,7 @@ run-plugin-linux-test-with-%: $(call skip-test, $<, "BROKEN") # This test is currently unreliable: https://gitlab.com/qemu-project/qemu/-/issues/856 +run-threadcount: + $(call skip-test, $<, "BROKEN") run-plugin-threadcount-with-%: $(call skip-test, $<, "BROKEN") diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index 4a8a464c57..b71a6bcd5e 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -8,7 +8,7 @@ include $(SRC_PATH)/tests/tcg/i386/Makefile.target -ifneq ($(CONFIG_LINUX_USER),) +ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET)) X86_64_TESTS += vsyscall TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 else diff --git a/tests/tcg/x86_64/float_convd.ref b/tests/tcg/x86_64/float_convd.ref new file mode 100644 index 0000000000..a71bff42cc --- /dev/null +++ b/tests/tcg/x86_64/float_convd.ref @@ -0,0 +1,988 @@ +### Rounding to nearest +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding upwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000200000000000000p-25:0x33000001) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe800000000000000p-25:0x337ffff4) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801c00000000000000p-15:0x387fc00e) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000e00000000000000p-14:0x38800007) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0aa00000000000000p+1:0x402df855) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding downwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x1.00000000000000000000p-149:0x80000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding to zero +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) diff --git a/tests/tcg/x86_64/float_convs.ref b/tests/tcg/x86_64/float_convs.ref new file mode 100644 index 0000000000..54a094f795 --- /dev/null +++ b/tests/tcg/x86_64/float_convs.ref @@ -0,0 +1,748 @@ +### Rounding to nearest +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding upwards +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding downwards +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +### Rounding to zero +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: -9223372036854775808 (INVALID) diff --git a/tests/tcg/xtensa/Makefile.softmmu-target b/tests/tcg/xtensa/Makefile.softmmu-target index 9530cac2ad..973e55298e 100644 --- a/tests/tcg/xtensa/Makefile.softmmu-target +++ b/tests/tcg/xtensa/Makefile.softmmu-target @@ -2,7 +2,7 @@ # Xtensa softmmu tests # -ifneq ($(TARGET_WORDS_BIGENDIAN),y) +ifneq ($(TARGET_BIG_ENDIAN),y) XTENSA_SRC = $(SRC_PATH)/tests/tcg/xtensa XTENSA_ALL = $(filter-out $(XTENSA_SRC)/linker.ld.S,$(wildcard $(XTENSA_SRC)/*.S)) diff --git a/tests/unit/check-qjson.c b/tests/unit/check-qjson.c index c845f91d43..c4e0f851bf 100644 --- a/tests/unit/check-qjson.c +++ b/tests/unit/check-qjson.c @@ -21,7 +21,6 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/unicode.h" -#include "qemu-common.h" static QString *from_json_str(const char *jstr, bool single, Error **errp) { diff --git a/tests/unit/check-qnull.c b/tests/unit/check-qnull.c index ebf21db83c..5ceacc65d7 100644 --- a/tests/unit/check-qnull.c +++ b/tests/unit/check-qnull.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qnull.h" -#include "qemu-common.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qapi/error.h" diff --git a/tests/unit/check-qnum.c b/tests/unit/check-qnum.c index b85fca2302..bf7fe45bac 100644 --- a/tests/unit/check-qnum.c +++ b/tests/unit/check-qnum.c @@ -15,7 +15,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qnum.h" -#include "qemu-common.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qobject.c b/tests/unit/check-qobject.c index c1713d15af..022b7c74fe 100644 --- a/tests/unit/check-qobject.c +++ b/tests/unit/check-qobject.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "block/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" @@ -16,7 +15,6 @@ #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" -#include "qemu-common.h" #include @@ -75,21 +73,6 @@ static void do_test_equality(bool expected, int _, ...) #define check_unequal(...) \ do_test_equality(false, 0, __VA_ARGS__, &test_equality_end_of_arguments) -static void do_free_all(int _, ...) -{ - va_list ap; - QObject *obj; - - va_start(ap, _); - while ((obj = va_arg(ap, QObject *)) != NULL) { - qobject_unref(obj); - } - va_end(ap); -} - -#define free_all(...) \ - do_free_all(0, __VA_ARGS__, NULL) - static void qobject_is_equal_null_test(void) { check_unequal(qnull(), NULL); @@ -97,15 +80,14 @@ static void qobject_is_equal_null_test(void) static void qobject_is_equal_num_test(void) { - QNum *u0, *i0, *d0, *dnan, *um42, *im42, *dm42; + g_autoptr(QNum) u0 = qnum_from_uint(0u); + g_autoptr(QNum) i0 = qnum_from_int(0); + g_autoptr(QNum) d0 = qnum_from_double(0.0); + g_autoptr(QNum) dnan = qnum_from_double(NAN); + g_autoptr(QNum) um42 = qnum_from_uint((uint64_t)-42); + g_autoptr(QNum) im42 = qnum_from_int(-42); + g_autoptr(QNum) dm42 = qnum_from_double(-42.0); - u0 = qnum_from_uint(0u); - i0 = qnum_from_int(0); - d0 = qnum_from_double(0.0); - dnan = qnum_from_double(NAN); - um42 = qnum_from_uint((uint64_t)-42); - im42 = qnum_from_int(-42); - dm42 = qnum_from_double(-42.0); /* Integers representing a mathematically equal number should * compare equal */ @@ -122,60 +104,45 @@ static void qobject_is_equal_num_test(void) check_unequal(um42, im42); check_unequal(um42, dm42); check_unequal(im42, dm42); - - free_all(u0, i0, d0, dnan, um42, im42, dm42); } static void qobject_is_equal_bool_test(void) { - QBool *btrue_0, *btrue_1, *bfalse_0, *bfalse_1; - - btrue_0 = qbool_from_bool(true); - btrue_1 = qbool_from_bool(true); - bfalse_0 = qbool_from_bool(false); - bfalse_1 = qbool_from_bool(false); + g_autoptr(QBool) btrue_0 = qbool_from_bool(true); + g_autoptr(QBool) btrue_1 = qbool_from_bool(true); + g_autoptr(QBool) bfalse_0 = qbool_from_bool(false); + g_autoptr(QBool) bfalse_1 = qbool_from_bool(false); check_equal(btrue_0, btrue_1); check_equal(bfalse_0, bfalse_1); check_unequal(btrue_0, bfalse_0); - - free_all(btrue_0, btrue_1, bfalse_0, bfalse_1); } static void qobject_is_equal_string_test(void) { - QString *str_base, *str_whitespace_0, *str_whitespace_1, *str_whitespace_2; - QString *str_whitespace_3, *str_case, *str_built; - - str_base = qstring_from_str("foo"); - str_whitespace_0 = qstring_from_str(" foo"); - str_whitespace_1 = qstring_from_str("foo "); - str_whitespace_2 = qstring_from_str("foo\b"); - str_whitespace_3 = qstring_from_str("fooo\b"); - str_case = qstring_from_str("Foo"); - + g_autoptr(QString) str_base = qstring_from_str("foo"); + g_autoptr(QString) str_whitespace_0 = qstring_from_str(" foo"); + g_autoptr(QString) str_whitespace_1 = qstring_from_str("foo "); + g_autoptr(QString) str_whitespace_2 = qstring_from_str("foo\b"); + g_autoptr(QString) str_whitespace_3 = qstring_from_str("fooo\b"); + g_autoptr(QString) str_case = qstring_from_str("Foo"); /* Should yield "foo" */ - str_built = qstring_from_substr("buffoon", 3, 6); + g_autoptr(QString) str_built = qstring_from_substr("buffoon", 3, 6); check_unequal(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_2, str_whitespace_3, str_case); check_equal(str_base, str_built); - - free_all(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_2, - str_whitespace_3, str_case, str_built); } static void qobject_is_equal_list_test(void) { - QList *list_0, *list_1, *list_cloned; - QList *list_reordered, *list_longer, *list_shorter; - - list_0 = qlist_new(); - list_1 = qlist_new(); - list_reordered = qlist_new(); - list_longer = qlist_new(); - list_shorter = qlist_new(); + g_autoptr(QList) list_0 = qlist_new(); + g_autoptr(QList) list_1 = qlist_new(); + g_autoptr(QList) list_reordered = qlist_new(); + g_autoptr(QList) list_longer = qlist_new(); + g_autoptr(QList) list_shorter = qlist_new(); + g_autoptr(QList) list_cloned = NULL; qlist_append_int(list_0, 1); qlist_append_int(list_0, 2); @@ -206,26 +173,19 @@ static void qobject_is_equal_list_test(void) * itself */ qlist_append(list_0, qnum_from_double(NAN)); g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false); - - free_all(list_0, list_1, list_cloned, list_reordered, list_longer, - list_shorter); } static void qobject_is_equal_dict_test(void) { - QDict *dict_0, *dict_1, *dict_cloned; - QDict *dict_different_key, *dict_different_value, *dict_different_null_key; - QDict *dict_longer, *dict_shorter, *dict_nested; - QDict *dict_crumpled; - - dict_0 = qdict_new(); - dict_1 = qdict_new(); - dict_different_key = qdict_new(); - dict_different_value = qdict_new(); - dict_different_null_key = qdict_new(); - dict_longer = qdict_new(); - dict_shorter = qdict_new(); - dict_nested = qdict_new(); + g_autoptr(QDict) dict_cloned = NULL; + g_autoptr(QDict) dict_0 = qdict_new(); + g_autoptr(QDict) dict_1 = qdict_new(); + g_autoptr(QDict) dict_different_key = qdict_new(); + g_autoptr(QDict) dict_different_value = qdict_new(); + g_autoptr(QDict) dict_different_null_key = qdict_new(); + g_autoptr(QDict) dict_longer = qdict_new(); + g_autoptr(QDict) dict_shorter = qdict_new(); + g_autoptr(QDict) dict_nested = qdict_new(); qdict_put_int(dict_0, "f.o", 1); qdict_put_int(dict_0, "bar", 2); @@ -275,41 +235,25 @@ static void qobject_is_equal_dict_test(void) dict_different_null_key, dict_longer, dict_shorter, dict_nested); - dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &error_abort)); - check_equal(dict_crumpled, dict_nested); - - qdict_flatten(dict_nested); - check_equal(dict_0, dict_nested); - /* Containing an NaN value will make this dict compare unequal to * itself */ qdict_put(dict_0, "NaN", qnum_from_double(NAN)); g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false); - - free_all(dict_0, dict_1, dict_cloned, dict_different_key, - dict_different_value, dict_different_null_key, dict_longer, - dict_shorter, dict_nested, dict_crumpled); } static void qobject_is_equal_conversion_test(void) { - QNum *u0, *i0, *d0; - QString *s0, *s_empty; - QBool *bfalse; - - u0 = qnum_from_uint(0u); - i0 = qnum_from_int(0); - d0 = qnum_from_double(0.0); - s0 = qstring_from_str("0"); - s_empty = qstring_new(); - bfalse = qbool_from_bool(false); + g_autoptr(QNum) u0 = qnum_from_uint(0u); + g_autoptr(QNum) i0 = qnum_from_int(0); + g_autoptr(QNum) d0 = qnum_from_double(0.0); + g_autoptr(QString) s0 = qstring_from_str("0"); + g_autoptr(QString) s_empty = qstring_new(); + g_autoptr(QBool) bfalse = qbool_from_bool(false); /* No automatic type conversion */ check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL); check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL); check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL); - - free_all(u0, i0, d0, s0, s_empty, bfalse); } int main(int argc, char **argv) diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index ed341088d3..79d4a8b89d 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -27,6 +27,7 @@ #include "qom/object.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/keyval.h" #include "qemu/config-file.h" #include "qom/object_interfaces.h" diff --git a/tests/unit/check-qstring.c b/tests/unit/check-qstring.c index 4bf9772093..bd861f4f8b 100644 --- a/tests/unit/check-qstring.c +++ b/tests/unit/check-qstring.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qstring.h" -#include "qemu-common.h" /* * Public Interface test-cases diff --git a/tests/unit/crypto-tls-psk-helpers.c b/tests/unit/crypto-tls-psk-helpers.c index 7f8a488961..4bea7c6fa2 100644 --- a/tests/unit/crypto-tls-psk-helpers.c +++ b/tests/unit/crypto-tls-psk-helpers.c @@ -30,7 +30,7 @@ void test_tls_psk_init(const char *pskfile) fp = fopen(pskfile, "w"); if (fp == NULL) { - g_critical("Failed to create pskfile %s", pskfile); + g_critical("Failed to create pskfile %s: %s", pskfile, strerror(errno)); abort(); } /* Don't do this in real applications! Use psktool. */ diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 96b295263e..264f2bc0c8 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -148,13 +148,13 @@ if have_system endif tests += { - 'test-qdev-global-props': [qom, hwcore, testqapi] + 'test-qdev-global-props': [qom, hwcore] } endif endif -if have_ga and targetos == 'linux' and 'CONFIG_TSAN' not in config_host - tests += {'test-qga': ['../qtest/libqtest.c']} +if have_ga and targetos == 'linux' + tests += {'test-qga': ['../qtest/libqmp.c']} test_deps += {'test-qga': qga} endif diff --git a/tests/unit/ptimer-test-stubs.c b/tests/unit/ptimer-test-stubs.c index 2a3ef58799..f5e75a96b6 100644 --- a/tests/unit/ptimer-test-stubs.c +++ b/tests/unit/ptimer-test-stubs.c @@ -12,7 +12,6 @@ #include "qemu/main-loop.h" #include "sysemu/replay.h" #include "migration/vmstate.h" -#include "sysemu/cpu-timers.h" #include "ptimer-test.h" diff --git a/tests/unit/rcutorture.c b/tests/unit/rcutorture.c index de6f649058..495a4e6f42 100644 --- a/tests/unit/rcutorture.c +++ b/tests/unit/rcutorture.c @@ -122,7 +122,7 @@ static void *rcu_read_perf_test(void *arg) rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; + *(struct rcu_reader_data **)arg = get_ptr_rcu_reader(); qatomic_inc(&nthreadsrunning); while (goflag == GOFLAG_INIT) { g_usleep(1000); @@ -148,7 +148,7 @@ static void *rcu_update_perf_test(void *arg) rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; + *(struct rcu_reader_data **)arg = get_ptr_rcu_reader(); qatomic_inc(&nthreadsrunning); while (goflag == GOFLAG_INIT) { g_usleep(1000); @@ -253,7 +253,7 @@ static void *rcu_read_stress_test(void *arg) rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; + *(struct rcu_reader_data **)arg = get_ptr_rcu_reader(); while (goflag == GOFLAG_INIT) { g_usleep(1000); } @@ -304,7 +304,7 @@ static void *rcu_update_stress_test(void *arg) struct rcu_stress *cp = qatomic_read(&rcu_stress_current); rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; + *(struct rcu_reader_data **)arg = get_ptr_rcu_reader(); while (goflag == GOFLAG_INIT) { g_usleep(1000); @@ -347,7 +347,7 @@ static void *rcu_fake_update_stress_test(void *arg) { rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; + *(struct rcu_reader_data **)arg = get_ptr_rcu_reader(); while (goflag == GOFLAG_INIT) { g_usleep(1000); } diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index f704fd1a69..5af4de513b 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -19,7 +19,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/sockets.h" #include "socket-helpers.h" @@ -89,7 +88,7 @@ static int socket_can_bind_connect(const char *hostname, int family) goto cleanup; } - qemu_set_nonblock(cfd); + qemu_socket_set_nonblock(cfd); if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) { if (errno == EINPROGRESS) { check_soerr = true; @@ -105,7 +104,7 @@ static int socket_can_bind_connect(const char *hostname, int family) } if (check_soerr) { - if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) { + if (getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) { goto cleanup; } if (soerr) { diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index aea660aeed..94718c9319 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -279,10 +279,10 @@ static void test_sync_op_check(BdrvChild *c) g_assert_cmpint(ret, ==, -ENOTSUP); } -static void test_sync_op_invalidate_cache(BdrvChild *c) +static void test_sync_op_activate(BdrvChild *c) { /* Early success: Image is not inactive */ - bdrv_invalidate_cache(c->bs, NULL); + bdrv_activate(c->bs, NULL); } @@ -325,8 +325,8 @@ const SyncOpTest sync_op_tests[] = { .name = "/sync-op/check", .fn = test_sync_op_check, }, { - .name = "/sync-op/invalidate_cache", - .fn = test_sync_op_invalidate_cache, + .name = "/sync-op/activate", + .fn = test_sync_op_activate, }, }; diff --git a/tests/unit/test-clone-visitor.c b/tests/unit/test-clone-visitor.c index 5d48e125b8..ce67585305 100644 --- a/tests/unit/test-clone-visitor.c +++ b/tests/unit/test-clone-visitor.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/clone-visitor.h" #include "test-qapi-visit.h" diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index 5f0da9192c..a266dc32da 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -90,8 +90,8 @@ static void test_crypto_tls_session_psk(void) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qemu_set_nonblock(channel[0]); - qemu_set_nonblock(channel[1]); + qemu_socket_set_nonblock(channel[0]); + qemu_socket_set_nonblock(channel[1]); clientCreds = test_tls_creds_psk_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, @@ -244,8 +244,8 @@ static void test_crypto_tls_session_x509(const void *opaque) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qemu_set_nonblock(channel[0]); - qemu_set_nonblock(channel[1]); + qemu_socket_set_nonblock(channel[0]); + qemu_socket_set_nonblock(channel[1]); #define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" #define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" diff --git a/tests/unit/test-forward-visitor.c b/tests/unit/test-forward-visitor.c index 348f7e4e81..eea8ffc072 100644 --- a/tests/unit/test-forward-visitor.c +++ b/tests/unit/test-forward-visitor.c @@ -9,14 +9,13 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/forward-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" #include "qapi/qmp/qobject.h" #include "qapi/qmp/qdict.h" #include "test-qapi-visit.h" -#include "qemu/option.h" +#include "qemu/keyval.h" typedef bool GenericVisitor (Visitor *, const char *, void **, Error **); #define CAST_VISIT_TYPE(fn) ((GenericVisitor *)(fn)) diff --git a/tests/unit/test-hbitmap.c b/tests/unit/test-hbitmap.c index b6726cf76b..a4fe067917 100644 --- a/tests/unit/test-hbitmap.c +++ b/tests/unit/test-hbitmap.c @@ -113,7 +113,7 @@ static void hbitmap_test_truncate_impl(TestHBitmapData *data, n = hbitmap_test_array_size(size); m = hbitmap_test_array_size(data->old_size); - data->bits = g_realloc(data->bits, sizeof(unsigned long) * n); + data->bits = g_renew(unsigned long, data->bits, n); if (n > m) { memset(&data->bits[m], 0x00, sizeof(unsigned long) * (n - m)); } diff --git a/tests/unit/test-int128.c b/tests/unit/test-int128.c index b86a3c76e6..25db2455e8 100644 --- a/tests/unit/test-int128.c +++ b/tests/unit/test-int128.c @@ -206,6 +206,55 @@ static void test_rshift(void) test_rshift_one(0xFFFE8000U, 0, 0xFFFFFFFFFFFFFFFEULL, 0x8000000000000000ULL); } +static void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE +test_urshift_one(uint32_t x, int n, uint64_t h, uint64_t l) +{ + Int128 a = expand(x); + Int128 r = int128_urshift(a, n); + g_assert_cmpuint(int128_getlo(r), ==, l); + g_assert_cmpuint(int128_gethi(r), ==, h); +} + +static void test_urshift(void) +{ + test_urshift_one(0x00010000U, 64, 0x0000000000000000ULL, + 0x0000000000000001ULL); + test_urshift_one(0x80010000U, 64, 0x0000000000000000ULL, + 0x8000000000000001ULL); + test_urshift_one(0x7FFE0000U, 64, 0x0000000000000000ULL, + 0x7FFFFFFFFFFFFFFEULL); + test_urshift_one(0xFFFE0000U, 64, 0x0000000000000000ULL, + 0xFFFFFFFFFFFFFFFEULL); + test_urshift_one(0x00010000U, 60, 0x0000000000000000ULL, + 0x0000000000000010ULL); + test_urshift_one(0x80010000U, 60, 0x0000000000000008ULL, + 0x0000000000000010ULL); + test_urshift_one(0x00018000U, 60, 0x0000000000000000ULL, + 0x0000000000000018ULL); + test_urshift_one(0x80018000U, 60, 0x0000000000000008ULL, + 0x0000000000000018ULL); + test_urshift_one(0x7FFE0000U, 60, 0x0000000000000007ULL, + 0xFFFFFFFFFFFFFFE0ULL); + test_urshift_one(0xFFFE0000U, 60, 0x000000000000000FULL, + 0xFFFFFFFFFFFFFFE0ULL); + test_urshift_one(0x7FFE8000U, 60, 0x0000000000000007ULL, + 0xFFFFFFFFFFFFFFE8ULL); + test_urshift_one(0xFFFE8000U, 60, 0x000000000000000FULL, + 0xFFFFFFFFFFFFFFE8ULL); + test_urshift_one(0x00018000U, 0, 0x0000000000000001ULL, + 0x8000000000000000ULL); + test_urshift_one(0x80018000U, 0, 0x8000000000000001ULL, + 0x8000000000000000ULL); + test_urshift_one(0x7FFE0000U, 0, 0x7FFFFFFFFFFFFFFEULL, + 0x0000000000000000ULL); + test_urshift_one(0xFFFE0000U, 0, 0xFFFFFFFFFFFFFFFEULL, + 0x0000000000000000ULL); + test_urshift_one(0x7FFE8000U, 0, 0x7FFFFFFFFFFFFFFEULL, + 0x8000000000000000ULL); + test_urshift_one(0xFFFE8000U, 0, 0xFFFFFFFFFFFFFFFEULL, + 0x8000000000000000ULL); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -219,5 +268,6 @@ int main(int argc, char **argv) g_test_add_func("/int128/int128_ge", test_ge); g_test_add_func("/int128/int128_gt", test_gt); g_test_add_func("/int128/int128_rshift", test_rshift); + g_test_add_func("/int128/int128_urshift", test_urshift); return g_test_run(); } diff --git a/tests/unit/test-io-channel-file.c b/tests/unit/test-io-channel-file.c index 29038e67b6..1977006ce9 100644 --- a/tests/unit/test-io-channel-file.c +++ b/tests/unit/test-io-channel-file.c @@ -109,7 +109,7 @@ static void test_io_channel_pipe(bool async) QIOChannelTest *test; int fd[2]; - if (pipe(fd) < 0) { + if (!g_unix_open_pipe(fd, FD_CLOEXEC, NULL)) { perror("pipe"); abort(); } diff --git a/tests/unit/test-iov.c b/tests/unit/test-iov.c index 5371066fb6..93bda00f0e 100644 --- a/tests/unit/test-iov.c +++ b/tests/unit/test-iov.c @@ -1,5 +1,4 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/iov.h" #include "qemu/sockets.h" @@ -187,7 +186,7 @@ static void test_io(void) close(sv[0]); FD_SET(sv[1], &fds); - fcntl(sv[1], F_SETFL, O_RDWR|O_NONBLOCK); + g_unix_set_fd_nonblocking(sv[1], true, NULL); r = g_test_rand_int_range(sz / 2, sz); setsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &r, sizeof(r)); @@ -221,7 +220,7 @@ static void test_io(void) close(sv[1]); FD_SET(sv[0], &fds); - fcntl(sv[0], F_SETFL, O_RDWR|O_NONBLOCK); + g_unix_set_fd_nonblocking(sv[0], true, NULL); r = g_test_rand_int_range(sz / 2, sz); setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &r, sizeof(r)); usleep(500000); diff --git a/tests/unit/test-keyval.c b/tests/unit/test-keyval.c index af0581ae6c..4dc52c7a1a 100644 --- a/tests/unit/test-keyval.c +++ b/tests/unit/test-keyval.c @@ -19,7 +19,7 @@ #include "qapi/qobject-input-visitor.h" #include "test-qapi-visit.h" #include "qemu/cutils.h" -#include "qemu/option.h" +#include "qemu/keyval.h" static void test_keyval_parse(void) { diff --git a/tests/unit/test-logging.c b/tests/unit/test-logging.c index ccb819f193..66dbc82a56 100644 --- a/tests/unit/test-logging.c +++ b/tests/unit/test-logging.c @@ -27,9 +27,9 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/log.h" +#include "qemu/rcu.h" static void test_parse_range(void) { @@ -110,12 +110,10 @@ static void test_parse_path(gconstpointer data) static void test_logfile_write(gconstpointer data) { - QemuLogFile *logfile; - QemuLogFile *logfile2; + FILE *logfile0, *logfile1; gchar const *dir = data; - g_autofree gchar *file_path = NULL; + g_autofree gchar *file_path0 = NULL; g_autofree gchar *file_path1 = NULL; - FILE *orig_fd; /* * Before starting test, set log flags, to ensure the file gets @@ -123,30 +121,29 @@ static void test_logfile_write(gconstpointer data) * In cases where a logging backend other than log is used, * this is needed. */ - qemu_set_log(CPU_LOG_TB_OUT_ASM); - file_path = g_build_filename(dir, "qemu_test_log_write0.log", NULL); + qemu_set_log(CPU_LOG_TB_OUT_ASM, &error_abort); + file_path0 = g_build_filename(dir, "qemu_test_log_write0.log", NULL); file_path1 = g_build_filename(dir, "qemu_test_log_write1.log", NULL); /* * Test that even if an open file handle is changed, * our handle remains valid due to RCU. */ - qemu_set_log_filename(file_path, &error_abort); - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - orig_fd = logfile->fd; - g_assert(logfile && logfile->fd); - fprintf(logfile->fd, "%s 1st write to file\n", __func__); - fflush(logfile->fd); + qemu_set_log_filename(file_path0, &error_abort); + logfile0 = qemu_log_trylock(); + g_assert(logfile0); + fprintf(logfile0, "%s 1st write to file\n", __func__); + fflush(logfile0); /* Change the logfile and ensure that the handle is still valid. */ qemu_set_log_filename(file_path1, &error_abort); - logfile2 = qatomic_rcu_read(&qemu_logfile); - g_assert(logfile->fd == orig_fd); - g_assert(logfile2->fd != logfile->fd); - fprintf(logfile->fd, "%s 2nd write to file\n", __func__); - fflush(logfile->fd); - rcu_read_unlock(); + logfile1 = qemu_log_trylock(); + g_assert(logfile1); + g_assert(logfile0 != logfile1); + fprintf(logfile0, "%s 2nd write to file\n", __func__); + fflush(logfile0); + qemu_log_unlock(logfile0); + qemu_log_unlock(logfile1); } static void test_logfile_lock(gconstpointer data) @@ -163,7 +160,7 @@ static void test_logfile_lock(gconstpointer data) * our handle remains valid for use due to RCU. */ qemu_set_log_filename(file_path, &error_abort); - logfile = qemu_log_lock(); + logfile = qemu_log_trylock(); g_assert(logfile); fprintf(logfile, "%s 1st write to file\n", __func__); fflush(logfile); @@ -172,7 +169,7 @@ static void test_logfile_lock(gconstpointer data) * Initiate a close file and make sure our handle remains * valid since we still have the logfile lock. */ - qemu_log_close(); + qemu_set_log_filename_flags(NULL, 0, &error_abort); fprintf(logfile, "%s 2nd write to file\n", __func__); fflush(logfile); qemu_log_unlock(logfile); @@ -210,7 +207,7 @@ int main(int argc, char **argv) tmp_path, test_logfile_lock); rc = g_test_run(); - qemu_log_close(); + qemu_set_log_filename_flags(NULL, 0, &error_abort); drain_call_rcu(); rmdir_full(tmp_path); diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c index 5cb140d1b5..d6df1ee92e 100644 --- a/tests/unit/test-qga.c +++ b/tests/unit/test-qga.c @@ -4,7 +4,7 @@ #include #include -#include "../qtest/libqos/libqtest.h" +#include "../qtest/libqtest.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" @@ -969,6 +969,13 @@ int main(int argc, char **argv) TestFixture fix; int ret; +#ifdef QEMU_SANITIZE_THREAD + { + g_test_skip("tsan enabled, https://github.com/google/sanitizers/issues/1116"); + return 0; + } +#endif + setlocale (LC_ALL, ""); g_test_init(&argc, &argv, NULL); fixture_setup(&fix, NULL, NULL); diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c index faa858624a..6085c09995 100644 --- a/tests/unit/test-qmp-cmds.c +++ b/tests/unit/test-qmp-cmds.c @@ -82,8 +82,8 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, Error **errp) { UserDefTwo *ret; - UserDefOne *ud1c = g_malloc0(sizeof(UserDefOne)); - UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne)); + UserDefOne *ud1c = g_new0(UserDefOne, 1); + UserDefOne *ud1d = g_new0(UserDefOne, 1); ud1c->string = strdup(ud1a->string); ud1c->integer = ud1a->integer; @@ -344,23 +344,23 @@ static void test_dealloc_types(void) UserDefOne *ud1test, *ud1a, *ud1b; UserDefOneList *ud1list; - ud1test = g_malloc0(sizeof(UserDefOne)); + ud1test = g_new0(UserDefOne, 1); ud1test->integer = 42; ud1test->string = g_strdup("hi there 42"); qapi_free_UserDefOne(ud1test); - ud1a = g_malloc0(sizeof(UserDefOne)); + ud1a = g_new0(UserDefOne, 1); ud1a->integer = 43; ud1a->string = g_strdup("hi there 43"); - ud1b = g_malloc0(sizeof(UserDefOne)); + ud1b = g_new0(UserDefOne, 1); ud1b->integer = 44; ud1b->string = g_strdup("hi there 44"); - ud1list = g_malloc0(sizeof(UserDefOneList)); + ud1list = g_new0(UserDefOneList, 1); ud1list->value = ud1a; - ud1list->next = g_malloc0(sizeof(UserDefOneList)); + ud1list->next = g_new0(UserDefOneList, 1); ud1list->next->value = ud1b; qapi_free_UserDefOneList(ud1list); diff --git a/tests/unit/test-qmp-event.c b/tests/unit/test-qmp-event.c index d58c3b78f2..7d961d716a 100644 --- a/tests/unit/test-qmp-event.c +++ b/tests/unit/test-qmp-event.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/compat-policy.h" #include "qapi/error.h" #include "qapi/qmp/qbool.h" diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c index 6f59a7f432..14329dabcf 100644 --- a/tests/unit/test-qobject-input-visitor.c +++ b/tests/unit/test-qobject-input-visitor.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qapi/qapi-visit-introspect.h" #include "qapi/qobject-input-visitor.h" @@ -64,7 +63,7 @@ static Visitor *test_init_internal(TestInputVisitorData *data, bool keyval, return data->qiv; } -static GCC_FMT_ATTR(3, 4) +static G_GNUC_PRINTF(3, 4) Visitor *visitor_input_test_init_full(TestInputVisitorData *data, bool keyval, const char *json_string, ...) @@ -79,7 +78,7 @@ Visitor *visitor_input_test_init_full(TestInputVisitorData *data, return v; } -static GCC_FMT_ATTR(2, 3) +static G_GNUC_PRINTF(2, 3) Visitor *visitor_input_test_init(TestInputVisitorData *data, const char *json_string, ...) { @@ -776,6 +775,7 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, AltEnumNum *aen; AltNumEnum *ans; AltEnumInt *asi; + AltListInt *ali; /* Parsing an int */ @@ -802,6 +802,12 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, g_assert_cmpint(asi->u.i, ==, 42); qapi_free_AltEnumInt(asi); + v = visitor_input_test_init(data, "42"); + visit_type_AltListInt(v, NULL, &ali, &error_abort); + g_assert_cmpint(ali->type, ==, QTYPE_QNUM); + g_assert_cmpint(ali->u.i, ==, 42); + qapi_free_AltListInt(ali); + /* Parsing a double */ v = visitor_input_test_init(data, "42.5"); @@ -827,6 +833,37 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, qapi_free_AltEnumInt(asi); } +static void test_visitor_in_alternate_list(TestInputVisitorData *data, + const void *unused) +{ + intList *item; + Visitor *v; + AltListInt *ali; + int i; + + v = visitor_input_test_init(data, "[ 42, 43, 44 ]"); + visit_type_AltListInt(v, NULL, &ali, &error_abort); + g_assert(ali != NULL); + + g_assert_cmpint(ali->type, ==, QTYPE_QLIST); + for (i = 0, item = ali->u.l; item; item = item->next, i++) { + g_assert_cmpint(item->value, ==, 42 + i); + } + + qapi_free_AltListInt(ali); + ali = NULL; + + /* An empty list is valid */ + v = visitor_input_test_init(data, "[]"); + visit_type_AltListInt(v, NULL, &ali, &error_abort); + g_assert(ali != NULL); + + g_assert_cmpint(ali->type, ==, QTYPE_QLIST); + g_assert(!ali->u.l); + qapi_free_AltListInt(ali); + ali = NULL; +} + static void input_visitor_test_add(const char *testpath, const void *user_data, void (*test_func)(TestInputVisitorData *data, @@ -1188,6 +1225,8 @@ int main(int argc, char **argv) NULL, test_visitor_in_wrong_type); input_visitor_test_add("/visitor/input/alternate-number", NULL, test_visitor_in_alternate_number); + input_visitor_test_add("/visitor/input/alternate-list", + NULL, test_visitor_in_alternate_list); input_visitor_test_add("/visitor/input/fail/struct", NULL, test_visitor_in_fail_struct); input_visitor_test_add("/visitor/input/fail/struct-nested", diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c index 34d67a439a..66b27fad66 100644 --- a/tests/unit/test-qobject-output-visitor.c +++ b/tests/unit/test-qobject-output-visitor.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qapi/qobject-output-visitor.h" #include "test-qapi-visit.h" @@ -338,7 +337,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, { QDict *qdict; - UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); + UserDefFlatUnion *tmp = g_new0(UserDefFlatUnion, 1); tmp->enum1 = ENUM_ONE_VALUE1; tmp->string = g_strdup("str"); tmp->integer = 41; diff --git a/tests/unit/test-rcu-list.c b/tests/unit/test-rcu-list.c index 49641e1936..64b81ae058 100644 --- a/tests/unit/test-rcu-list.c +++ b/tests/unit/test-rcu-list.c @@ -171,7 +171,7 @@ static void *rcu_q_reader(void *arg) rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; + *(struct rcu_reader_data **)arg = get_ptr_rcu_reader(); qatomic_inc(&nthreadsrunning); while (qatomic_read(&goflag) == GOFLAG_INIT) { g_usleep(1000); @@ -206,7 +206,7 @@ static void *rcu_q_updater(void *arg) long long n_removed_local = 0; struct list_element *el, *prev_el; - *(struct rcu_reader_data **)arg = &rcu_reader; + *(struct rcu_reader_data **)arg = get_ptr_rcu_reader(); qatomic_inc(&nthreadsrunning); while (qatomic_read(&goflag) == GOFLAG_INIT) { g_usleep(1000); diff --git a/tests/unit/test-string-input-visitor.c b/tests/unit/test-string-input-visitor.c index 249faafc9d..25094d3ffc 100644 --- a/tests/unit/test-string-input-visitor.c +++ b/tests/unit/test-string-input-visitor.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qapi/string-input-visitor.h" #include "test-qapi-visit.h" diff --git a/tests/unit/test-string-output-visitor.c b/tests/unit/test-string-output-visitor.c index e2bedc5c7c..7ef305361e 100644 --- a/tests/unit/test-string-output-visitor.c +++ b/tests/unit/test-string-output-visitor.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qapi/string-output-visitor.h" #include "test-qapi-visit.h" diff --git a/tests/unit/test-thread-pool.c b/tests/unit/test-thread-pool.c index 70dc6314a1..6020e65d69 100644 --- a/tests/unit/test-thread-pool.c +++ b/tests/unit/test-thread-pool.c @@ -1,5 +1,4 @@ #include "qemu/osdep.h" -#include "qemu-common.h" #include "block/aio.h" #include "block/thread-pool.h" #include "block/block.h" diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index 896247e3ed..63909ccb2b 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -19,7 +19,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/sockets.h" #include "qapi/error.h" #include "socket-helpers.h" diff --git a/tests/unit/test-visitor-serialization.c b/tests/unit/test-visitor-serialization.c index 4629958647..907263d030 100644 --- a/tests/unit/test-visitor-serialization.c +++ b/tests/unit/test-visitor-serialization.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" #include "test-qapi-visit.h" #include "qapi/error.h" #include "qapi/qmp/qjson.h" diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index 4688c03ea7..6a417bb102 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -1002,22 +1002,22 @@ static TestGTreeDomain *create_first_domain(void) TestGTreeMapping *map_a, *map_b; TestGTreeInterval *a, *b; - domain = g_malloc0(sizeof(TestGTreeDomain)); + domain = g_new0(TestGTreeDomain, 1); domain->id = 6; - a = g_malloc0(sizeof(TestGTreeInterval)); + a = g_new0(TestGTreeInterval, 1); a->low = 0x1000; a->high = 0x1FFF; - b = g_malloc0(sizeof(TestGTreeInterval)); + b = g_new0(TestGTreeInterval, 1); b->low = 0x4000; b->high = 0x4FFF; - map_a = g_malloc0(sizeof(TestGTreeMapping)); + map_a = g_new0(TestGTreeMapping, 1); map_a->phys_addr = 0xa000; map_a->flags = 1; - map_b = g_malloc0(sizeof(TestGTreeMapping)); + map_b = g_new0(TestGTreeMapping, 1); map_b->phys_addr = 0xe0000; map_b->flags = 2; @@ -1120,7 +1120,7 @@ static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2) static void test_gtree_load_domain(void) { - TestGTreeDomain *dest_domain = g_malloc0(sizeof(TestGTreeDomain)); + TestGTreeDomain *dest_domain = g_new0(TestGTreeDomain, 1); TestGTreeDomain *orig_domain = create_first_domain(); QEMUFile *fload, *fsave; char eof; @@ -1185,7 +1185,7 @@ uint8_t iommu_dump[] = { static TestGTreeIOMMU *create_iommu(void) { - TestGTreeIOMMU *iommu = g_malloc0(sizeof(TestGTreeIOMMU)); + TestGTreeIOMMU *iommu = g_new0(TestGTreeIOMMU, 1); TestGTreeDomain *first_domain = create_first_domain(); TestGTreeDomain *second_domain; TestGTreeMapping *map_c; @@ -1196,7 +1196,7 @@ static TestGTreeIOMMU *create_iommu(void) NULL, destroy_domain); - second_domain = g_malloc0(sizeof(TestGTreeDomain)); + second_domain = g_new0(TestGTreeDomain, 1); second_domain->id = 5; second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL, @@ -1206,11 +1206,11 @@ static TestGTreeIOMMU *create_iommu(void) g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain); g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain); - c = g_malloc0(sizeof(TestGTreeInterval)); + c = g_new0(TestGTreeInterval, 1); c->low = 0x1000000; c->high = 0x1FFFFFF; - map_c = g_malloc0(sizeof(TestGTreeMapping)); + map_c = g_new0(TestGTreeMapping, 1); map_c->phys_addr = 0xF000000; map_c->flags = 0x3; @@ -1235,7 +1235,7 @@ static void test_gtree_save_iommu(void) static void test_gtree_load_iommu(void) { - TestGTreeIOMMU *dest_iommu = g_malloc0(sizeof(TestGTreeIOMMU)); + TestGTreeIOMMU *dest_iommu = g_new0(TestGTreeIOMMU, 1); TestGTreeIOMMU *orig_iommu = create_iommu(); QEMUFile *fsave, *fload; char eof; @@ -1274,11 +1274,11 @@ static uint8_t qlist_dump[] = { static TestQListContainer *alloc_container(void) { - TestQListElement *a = g_malloc(sizeof(TestQListElement)); - TestQListElement *b = g_malloc(sizeof(TestQListElement)); - TestQListElement *c = g_malloc(sizeof(TestQListElement)); - TestQListElement *d = g_malloc(sizeof(TestQListElement)); - TestQListContainer *container = g_malloc(sizeof(TestQListContainer)); + TestQListElement *a = g_new(TestQListElement, 1); + TestQListElement *b = g_new(TestQListElement, 1); + TestQListElement *c = g_new(TestQListElement, 1); + TestQListElement *d = g_new(TestQListElement, 1); + TestQListContainer *container = g_new(TestQListContainer, 1); a->id = 0x0a; b->id = 0x0b00; @@ -1332,11 +1332,11 @@ static void manipulate_container(TestQListContainer *c) TestQListElement *prev = NULL, *iter = QLIST_FIRST(&c->list); TestQListElement *elem; - elem = g_malloc(sizeof(TestQListElement)); + elem = g_new(TestQListElement, 1); elem->id = 0x12; QLIST_INSERT_AFTER(iter, elem, next); - elem = g_malloc(sizeof(TestQListElement)); + elem = g_new(TestQListElement, 1); elem->id = 0x13; QLIST_INSERT_HEAD(&c->list, elem, next); @@ -1345,11 +1345,11 @@ static void manipulate_container(TestQListContainer *c) iter = QLIST_NEXT(iter, next); } - elem = g_malloc(sizeof(TestQListElement)); + elem = g_new(TestQListElement, 1); elem->id = 0x14; QLIST_INSERT_BEFORE(prev, elem, next); - elem = g_malloc(sizeof(TestQListElement)); + elem = g_new(TestQListElement, 1); elem->id = 0x15; QLIST_INSERT_AFTER(prev, elem, next); @@ -1370,7 +1370,7 @@ static void test_load_qlist(void) { QEMUFile *fsave, *fload; TestQListContainer *orig_container = alloc_container(); - TestQListContainer *dest_container = g_malloc0(sizeof(TestQListContainer)); + TestQListContainer *dest_container = g_new0(TestQListContainer, 1); char eof; QLIST_INIT(&dest_container->list); diff --git a/tests/unit/test-xbzrle.c b/tests/unit/test-xbzrle.c index 795d6f1cba..ef951b6e54 100644 --- a/tests/unit/test-xbzrle.c +++ b/tests/unit/test-xbzrle.c @@ -11,7 +11,6 @@ * */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "../migration/xbzrle.h" diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index 35088dd67f..9b1dab2f28 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -468,8 +468,8 @@ vubr_queue_set_started(VuDev *dev, int qidx, bool started) if (started && vubr->notifier.fd >= 0) { vu_set_queue_host_notifier(dev, vq, vubr->notifier.fd, - qemu_real_host_page_size, - qidx * qemu_real_host_page_size); + qemu_real_host_page_size(), + qidx * qemu_real_host_page_size()); } if (qidx % 2 == 1) { @@ -601,7 +601,7 @@ static void *notifier_thread(void *arg) { VuDev *dev = (VuDev *)arg; VubrDev *vubr = container_of(dev, VubrDev, vudev); - int pagesize = qemu_real_host_page_size; + int pagesize = qemu_real_host_page_size(); int qidx; while (true) { @@ -637,7 +637,7 @@ vubr_host_notifier_setup(VubrDev *dev) void *addr; int fd; - length = qemu_real_host_page_size * VHOST_USER_BRIDGE_MAX_QUEUES; + length = qemu_real_host_page_size() * VHOST_USER_BRIDGE_MAX_QUEUES; fd = mkstemp(template); if (fd < 0) { diff --git a/tests/vm/centos.aarch64 b/tests/vm/centos.aarch64 index 81c3004c3c..96c450f8be 100755 --- a/tests/vm/centos.aarch64 +++ b/tests/vm/centos.aarch64 @@ -24,8 +24,7 @@ DEFAULT_CONFIG = { 'cpu' : "max", 'machine' : "virt,gic-version=max", 'install_cmds' : "yum install -y make ninja-build git python3 gcc gcc-c++ flex bison, "\ - "yum install -y glib2-devel pixman-devel zlib-devel, "\ - "yum install -y perl-Test-Harness, "\ + "yum install -y glib2-devel perl pixman-devel zlib-devel, "\ "alternatives --set python /usr/bin/python3, "\ "sudo dnf config-manager "\ "--add-repo=https://download.docker.com/linux/centos/docker-ce.repo,"\ diff --git a/tests/vm/fedora b/tests/vm/fedora index b977efe4a2..92b78d6e2c 100755 --- a/tests/vm/fedora +++ b/tests/vm/fedora @@ -35,7 +35,7 @@ class FedoraVM(basevm.BaseVM): 'gcc', 'binutils', 'make', 'ninja-build', # perl - 'perl-Test-Harness', + 'perl', # libs: usb '"pkgconfig(libusb-1.0)"', diff --git a/tests/vm/haiku.x86_64 b/tests/vm/haiku.x86_64 index 2eb736dae1..936f7d2ae2 100755 --- a/tests/vm/haiku.x86_64 +++ b/tests/vm/haiku.x86_64 @@ -2,7 +2,7 @@ # # Haiku VM image # -# Copyright 2020 Haiku, Inc. +# Copyright 2020-2022 Haiku, Inc. # # Authors: # Alexander von Gluck IV @@ -48,8 +48,8 @@ class HaikuVM(basevm.BaseVM): name = "haiku" arch = "x86_64" - link = "https://app.vagrantup.com/haiku-os/boxes/r1beta2-x86_64/versions/20200702/providers/libvirt.box" - csum = "41c38b316e0cbdbc66b5dbaf3612b866700a4f35807cb1eb266a5bf83e9e68d5" + link = "https://app.vagrantup.com/haiku-os/boxes/r1beta3-x86_64/versions/20220216/providers/libvirt.box" + csum = "e67d4aacbcc687013d5cc91990ddd86cc5d70a5d28432ae2691944f8ce5d5041" poweroff = "shutdown" @@ -99,7 +99,7 @@ class HaikuVM(basevm.BaseVM): self.print_step("Extracting disk image") - subprocess.check_call(["tar", "xzf", tarball, "./box.img", "-O"], + subprocess.check_call(["tar", "xzf", tarball, "box.img", "-O"], stdout=open(img, 'wb')) self.print_step("Preparing disk image") diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 60b96470c5..a52eacf82e 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -901,10 +901,12 @@ static bool fv_socket_lock(struct fuse_session *se) { g_autofree gchar *sk_name = NULL; g_autofree gchar *pidfile = NULL; + g_autofree gchar *state = NULL; g_autofree gchar *dir = NULL; Error *local_err = NULL; - dir = qemu_get_local_state_pathname("run/virtiofsd"); + state = qemu_get_local_state_dir(); + dir = g_build_filename(state, "run", "virtiofsd", NULL); if (g_mkdir_with_parents(dir, S_IRWXU) < 0) { fuse_log(FUSE_LOG_ERR, "%s: Failed to create directory %s: %s\n", diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c index e226fc590f..f5f66f292c 100644 --- a/tools/virtiofsd/helper.c +++ b/tools/virtiofsd/helper.c @@ -188,6 +188,9 @@ void fuse_cmdline_help(void) " -o announce_submounts Announce sub-mount points to the guest\n" " -o posix_acl/no_posix_acl Enable/Disable posix_acl. (default: disabled)\n" " -o security_label/no_security_label Enable/Disable security label. (default: disabled)\n" + " -o killpriv_v2/no_killpriv_v2\n" + " Enable/Disable FUSE_HANDLE_KILLPRIV_V2.\n" + " (default: enabled as long as client supports it)\n" ); } @@ -272,7 +275,7 @@ int fuse_daemonize(int foreground) int waiter[2]; char completed; - if (pipe(waiter)) { + if (!g_unix_open_pipe(waiter, FD_CLOEXEC, NULL)) { fuse_log(FUSE_LOG_ERR, "fuse_daemonize: pipe: %s\n", strerror(errno)); return -1; diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index dfa2fc250d..b15c631ca5 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -38,7 +38,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" #include "qemu-version.h" -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "fuse_virtio.h" #include "fuse_log.h" #include "fuse_lowlevel.h" @@ -1039,7 +1039,7 @@ static int do_statx(struct lo_data *lo, int dirfd, const char *pathname, { int res; -#if defined(CONFIG_STATX) && defined(STATX_MNT_ID) +#if defined(CONFIG_STATX) && defined(CONFIG_STATX_MNT_ID) if (lo->use_statx) { struct statx statxbuf; diff --git a/trace/control.c b/trace/control.c index d5b68e846e..6c77cc6318 100644 --- a/trace/control.c +++ b/trace/control.c @@ -161,7 +161,7 @@ void trace_list_events(FILE *f) fprintf(f, "This list of names of trace points may be incomplete " "when using the DTrace/SystemTap backends.\n" "Run 'qemu-trace-stap list %s' to print the full list.\n", - error_get_progname()); + g_get_prgname()); #endif } diff --git a/ui/clipboard.c b/ui/clipboard.c index 5f15cf853d..9079ef829b 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -66,8 +66,10 @@ void qemu_clipboard_update(QemuClipboardInfo *info) notifier_list_notify(&clipboard_notifiers, ¬ify); - qemu_clipboard_info_unref(cbinfo[info->selection]); - cbinfo[info->selection] = qemu_clipboard_info_ref(info); + if (cbinfo[info->selection] != info) { + qemu_clipboard_info_unref(cbinfo[info->selection]); + cbinfo[info->selection] = qemu_clipboard_info_ref(info); + } } QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection) diff --git a/ui/cocoa.m b/ui/cocoa.m index a8f1cdaf92..09a62817f2 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -27,7 +27,8 @@ #import #include -#include "qemu-common.h" +#include "qemu/help-texts.h" +#include "qemu-main.h" #include "ui/clipboard.h" #include "ui/console.h" #include "ui/input.h" @@ -83,7 +84,7 @@ static void cocoa_switch(DisplayChangeListener *dcl, static void cocoa_refresh(DisplayChangeListener *dcl); -static NSWindow *normalWindow, *about_window; +static NSWindow *normalWindow; static const DisplayChangeListenerOps dcl_ops = { .dpy_name = "cocoa", .dpy_gfx_update = cocoa_update, @@ -95,6 +96,8 @@ static DisplayChangeListener dcl = { }; static int last_buttons; static int cursor_hide = 1; +static int left_command_key_enabled = 1; +static bool swap_opt_cmd; static int gArgc; static char **gArgv; @@ -308,11 +311,13 @@ static void handleAnyDeviceErrors(Error * err) BOOL isMouseGrabbed; BOOL isFullscreen; BOOL isAbsoluteEnabled; + CFMachPortRef eventsTap; } - (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; - (void) ungrabMouse; - (void) toggleFullScreen:(id)sender; +- (void) setFullGrab:(id)sender; - (void) handleMonitorInput:(NSEvent *)event; - (bool) handleEvent:(NSEvent *)event; - (bool) handleEventLocked:(NSEvent *)event; @@ -335,6 +340,19 @@ static void handleAnyDeviceErrors(Error * err) QemuCocoaView *cocoaView; +static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEventRef cgEvent, void *userInfo) +{ + QemuCocoaView *cocoaView = userInfo; + NSEvent *event = [NSEvent eventWithCGEvent:cgEvent]; + if ([cocoaView isMouseGrabbed] && [cocoaView handleEvent:event]) { + COCOA_DEBUG("Global events tap: qemu handled the event, capturing!\n"); + return NULL; + } + COCOA_DEBUG("Global events tap: qemu did not handle the event, letting it through...\n"); + + return cgEvent; +} + @implementation QemuCocoaView - (id)initWithFrame:(NSRect)frameRect { @@ -360,6 +378,11 @@ QemuCocoaView *cocoaView; } qkbd_state_free(kbd); + + if (eventsTap) { + CFRelease(eventsTap); + } + [super dealloc]; } @@ -522,8 +545,9 @@ QemuCocoaView *cocoaView; } } -- (void) updateUIInfo +- (void) updateUIInfoLocked { + /* Must be called with the iothread lock, i.e. via updateUIInfo */ NSSize frameSize; QemuUIInfo info; @@ -554,6 +578,25 @@ QemuCocoaView *cocoaView; dpy_set_ui_info(dcl.con, &info, TRUE); } +- (void) updateUIInfo +{ + if (!allow_events) { + /* + * Don't try to tell QEMU about UI information in the application + * startup phase -- we haven't yet registered dcl with the QEMU UI + * layer, and also trying to take the iothread lock would deadlock. + * When cocoa_display_init() does register the dcl, the UI layer + * will call cocoa_switch(), which will call updateUIInfo, so + * we don't lose any information here. + */ + return; + } + + with_iothread_lock(^{ + [self updateUIInfoLocked]; + }); +} + - (void)viewDidMoveToWindow { [self updateUIInfo]; @@ -634,6 +677,36 @@ QemuCocoaView *cocoaView; } } +- (void) setFullGrab:(id)sender +{ + COCOA_DEBUG("QemuCocoaView: setFullGrab\n"); + + CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged); + eventsTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, + mask, handleTapEvent, self); + if (!eventsTap) { + warn_report("Could not create event tap, system key combos will not be captured.\n"); + return; + } else { + COCOA_DEBUG("Global events tap created! Will capture system key combos.\n"); + } + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + if (!runLoop) { + warn_report("Could not obtain current CF RunLoop, system key combos will not be captured.\n"); + return; + } + + CFRunLoopSourceRef tapEventsSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventsTap, 0); + if (!tapEventsSrc ) { + warn_report("Could not obtain current CF RunLoop, system key combos will not be captured.\n"); + return; + } + + CFRunLoopAddSource(runLoop, tapEventsSrc, kCFRunLoopDefaultMode); + CFRelease(tapEventsSrc); +} + - (void) toggleKey: (int)keycode { qkbd_state_key_event(kbd, keycode, !qkbd_state_key_get(kbd, keycode)); } @@ -651,7 +724,7 @@ QemuCocoaView *cocoaView; /* translates Macintosh keycodes to QEMU's keysym */ - int without_control_translation[] = { + static const int without_control_translation[] = { [0 ... 0xff] = 0, // invalid key [kVK_UpArrow] = QEMU_KEY_UP, @@ -666,7 +739,7 @@ QemuCocoaView *cocoaView; [kVK_Delete] = QEMU_KEY_BACKSPACE, }; - int with_control_translation[] = { + static const int with_control_translation[] = { [0 ... 0xff] = 0, // invalid key [kVK_UpArrow] = QEMU_KEY_CTRL_UP, @@ -783,12 +856,22 @@ QemuCocoaView *cocoaView; qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL_R, false); } if (!(modifiers & NSEventModifierFlagOption)) { - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + if (swap_opt_cmd) { + qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + } else { + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + } } if (!(modifiers & NSEventModifierFlagCommand)) { - qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + if (swap_opt_cmd) { + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + } else { + qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + } } switch ([event type]) { @@ -820,28 +903,45 @@ QemuCocoaView *cocoaView; case kVK_Option: if (!!(modifiers & NSEventModifierFlagOption)) { - [self toggleKey:Q_KEY_CODE_ALT]; + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_META_L]; + } else { + [self toggleKey:Q_KEY_CODE_ALT]; + } } break; case kVK_RightOption: if (!!(modifiers & NSEventModifierFlagOption)) { - [self toggleKey:Q_KEY_CODE_ALT_R]; + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_META_R]; + } else { + [self toggleKey:Q_KEY_CODE_ALT_R]; + } } break; /* Don't pass command key changes to guest unless mouse is grabbed */ case kVK_Command: if (isMouseGrabbed && - !!(modifiers & NSEventModifierFlagCommand)) { - [self toggleKey:Q_KEY_CODE_META_L]; + !!(modifiers & NSEventModifierFlagCommand) && + left_command_key_enabled) { + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_ALT]; + } else { + [self toggleKey:Q_KEY_CODE_META_L]; + } } break; case kVK_RightCommand: if (isMouseGrabbed && !!(modifiers & NSEventModifierFlagCommand)) { - [self toggleKey:Q_KEY_CODE_META_R]; + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_ALT_R]; + } else { + [self toggleKey:Q_KEY_CODE_META_R]; + } } break; } @@ -1120,7 +1220,6 @@ QemuCocoaView *cocoaView; - (BOOL)verifyQuit; - (void)openDocumentation:(NSString *)filename; - (IBAction) do_about_menu_item: (id) sender; -- (void)make_about_window; - (void)adjustSpeed:(id)sender; @end @@ -1166,8 +1265,6 @@ QemuCocoaView *cocoaView; [pauseLabel setFont: [NSFont fontWithName: @"Helvetica" size: 90]]; [pauseLabel setTextColor: [NSColor blackColor]]; [pauseLabel sizeToFit]; - - [self make_about_window]; } return self; } @@ -1242,6 +1339,7 @@ QemuCocoaView *cocoaView; - (void) applicationWillResignActive: (NSNotification *)aNotification { COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n"); + [cocoaView ungrabMouse]; [cocoaView raiseAllKeys]; } @@ -1261,6 +1359,13 @@ QemuCocoaView *cocoaView; [cocoaView toggleFullScreen:sender]; } +- (void) setFullGrab:(id)sender +{ + COCOA_DEBUG("QemuCocoaAppController: setFullGrab\n"); + + [cocoaView setFullGrab:sender]; +} + /* Tries to find then open the specified filename */ - (void) openDocumentation: (NSString *) filename { @@ -1426,6 +1531,7 @@ QemuCocoaView *cocoaView; [file cStringUsingEncoding: NSASCIIStringEncoding], true, "raw", + true, false, false, 0, &err); }); @@ -1451,92 +1557,29 @@ QemuCocoaView *cocoaView; /* The action method for the About menu item */ - (IBAction) do_about_menu_item: (id) sender { - [about_window makeKeyAndOrderFront: nil]; -} - -/* Create and display the about dialog */ -- (void)make_about_window -{ - /* Make the window */ - int x = 0, y = 0, about_width = 400, about_height = 200; - NSRect window_rect = NSMakeRect(x, y, about_width, about_height); - about_window = [[NSWindow alloc] initWithContentRect:window_rect - styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable - backing:NSBackingStoreBuffered - defer:NO]; - [about_window setTitle: @"About"]; - [about_window setReleasedWhenClosed: NO]; - [about_window center]; - NSView *superView = [about_window contentView]; - - /* Create the dimensions of the picture */ - int picture_width = 80, picture_height = 80; - x = (about_width - picture_width)/2; - y = about_height - picture_height - 10; - NSRect picture_rect = NSMakeRect(x, y, picture_width, picture_height); - - /* Make the picture of QEMU */ - NSImageView *picture_view = [[NSImageView alloc] initWithFrame: - picture_rect]; - char *qemu_image_path_c = get_relocated_path(CONFIG_QEMU_ICONDIR "/hicolor/512x512/apps/qemu.png"); - NSString *qemu_image_path = [NSString stringWithUTF8String:qemu_image_path_c]; - g_free(qemu_image_path_c); - NSImage *qemu_image = [[NSImage alloc] initWithContentsOfFile:qemu_image_path]; - [picture_view setImage: qemu_image]; - [picture_view setImageScaling: NSImageScaleProportionallyUpOrDown]; - [superView addSubview: picture_view]; - - /* Make the name label */ - NSBundle *bundle = [NSBundle mainBundle]; - if (bundle) { - x = 0; - y = y - 25; - int name_width = about_width, name_height = 20; - NSRect name_rect = NSMakeRect(x, y, name_width, name_height); - NSTextField *name_label = [[NSTextField alloc] initWithFrame: name_rect]; - [name_label setEditable: NO]; - [name_label setBezeled: NO]; - [name_label setDrawsBackground: NO]; - [name_label setAlignment: NSTextAlignmentCenter]; - NSString *qemu_name = [[bundle executablePath] lastPathComponent]; - [name_label setStringValue: qemu_name]; - [superView addSubview: name_label]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + char *icon_path_c = get_relocated_path(CONFIG_QEMU_ICONDIR "/hicolor/512x512/apps/qemu.png"); + NSString *icon_path = [NSString stringWithUTF8String:icon_path_c]; + g_free(icon_path_c); + NSImage *icon = [[NSImage alloc] initWithContentsOfFile:icon_path]; + NSString *version = @"QEMU emulator version " QEMU_FULL_VERSION; + NSString *copyright = @QEMU_COPYRIGHT; + NSDictionary *options; + if (icon) { + options = @{ + NSAboutPanelOptionApplicationIcon : icon, + NSAboutPanelOptionApplicationVersion : version, + @"Copyright" : copyright, + }; + [icon release]; + } else { + options = @{ + NSAboutPanelOptionApplicationVersion : version, + @"Copyright" : copyright, + }; } - - /* Set the version label's attributes */ - x = 0; - y = 50; - int version_width = about_width, version_height = 20; - NSRect version_rect = NSMakeRect(x, y, version_width, version_height); - NSTextField *version_label = [[NSTextField alloc] initWithFrame: - version_rect]; - [version_label setEditable: NO]; - [version_label setBezeled: NO]; - [version_label setAlignment: NSTextAlignmentCenter]; - [version_label setDrawsBackground: NO]; - - /* Create the version string*/ - NSString *version_string; - version_string = [[NSString alloc] initWithFormat: - @"QEMU emulator version %s", QEMU_FULL_VERSION]; - [version_label setStringValue: version_string]; - [superView addSubview: version_label]; - - /* Make copyright label */ - x = 0; - y = 35; - int copyright_width = about_width, copyright_height = 20; - NSRect copyright_rect = NSMakeRect(x, y, copyright_width, copyright_height); - NSTextField *copyright_label = [[NSTextField alloc] initWithFrame: - copyright_rect]; - [copyright_label setEditable: NO]; - [copyright_label setBezeled: NO]; - [copyright_label setDrawsBackground: NO]; - [copyright_label setAlignment: NSTextAlignmentCenter]; - [copyright_label setStringValue: [NSString stringWithFormat: @"%s", - QEMU_COPYRIGHT]]; - [superView addSubview: copyright_label]; + [NSApp orderFrontStandardAboutPanelWithOptions:options]; + [pool release]; } /* Used by the Speed menu items */ @@ -1591,11 +1634,15 @@ static void create_initial_menus(void) NSMenuItem *menuItem; [NSApp setMainMenu:[[NSMenu alloc] init]]; + [NSApp setServicesMenu:[[NSMenu alloc] initWithTitle:@"Services"]]; // Application menu menu = [[NSMenu alloc] initWithTitle:@""]; [menu addItemWithTitle:@"About QEMU" action:@selector(do_about_menu_item:) keyEquivalent:@""]; // About QEMU [menu addItem:[NSMenuItem separatorItem]]; //Separator + menuItem = [menu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:[NSApp servicesMenu]]; + [menu addItem:[NSMenuItem separatorItem]]; [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; @@ -1956,8 +2003,6 @@ int main (int argc, char **argv) { static void cocoa_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); dispatch_async(dispatch_get_main_queue(), ^{ @@ -1973,20 +2018,15 @@ static void cocoa_update(DisplayChangeListener *dcl, } [cocoaView setNeedsDisplayInRect:rect]; }); - - [pool release]; } static void cocoa_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; pixman_image_t *image = surface->image; COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); - [cocoaView updateUIInfo]; - // The DisplaySurface will be freed as soon as this callback returns. // We take a reference to the underlying pixman image here so it does // not disappear from under our feet; the switchSurface method will @@ -1994,9 +2034,9 @@ static void cocoa_switch(DisplayChangeListener *dcl, pixman_image_ref(image); dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView updateUIInfo]; [cocoaView switchSurface:image]; }); - [pool release]; } static void cocoa_refresh(DisplayChangeListener *dcl) @@ -2040,16 +2080,30 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) qemu_sem_wait(&app_started_sem); COCOA_DEBUG("cocoa_display_init: app start completed\n"); + QemuCocoaAppController *controller = (QemuCocoaAppController *)[[NSApplication sharedApplication] delegate]; /* if fullscreen mode is to be used */ if (opts->has_full_screen && opts->full_screen) { dispatch_async(dispatch_get_main_queue(), ^{ [NSApp activateIgnoringOtherApps: YES]; - [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; + [controller toggleFullScreen: nil]; }); } + if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) { + dispatch_async(dispatch_get_main_queue(), ^{ + [controller setFullGrab: nil]; + }); + } + if (opts->has_show_cursor && opts->show_cursor) { cursor_hide = 0; } + if (opts->u.cocoa.has_swap_opt_cmd) { + swap_opt_cmd = opts->u.cocoa.swap_opt_cmd; + } + + if (opts->u.cocoa.has_left_command_key && !opts->u.cocoa.left_command_key) { + left_command_key_enabled = 0; + } // register vga output callbacks register_displaychangelistener(&dcl); diff --git a/ui/console-gl.c b/ui/console-gl.c index 7c9894a51d..8e3c9a3c8c 100644 --- a/ui/console-gl.c +++ b/ui/console-gl.c @@ -49,6 +49,10 @@ void surface_gl_create_texture(QemuGLShader *gls, assert(gls); assert(QEMU_IS_ALIGNED(surface_stride(surface), surface_bytes_per_pixel(surface))); + if (surface->texture) { + return; + } + switch (surface->format) { case PIXMAN_BE_b8g8r8x8: case PIXMAN_BE_b8g8r8a8: diff --git a/ui/console.c b/ui/console.c index 40eebb6d2c..15d0f6affd 100644 --- a/ui/console.c +++ b/ui/console.c @@ -37,6 +37,9 @@ #include "exec/memory.h" #include "io/channel-file.h" #include "qom/object.h" +#ifdef CONFIG_PNG +#include +#endif #define DEFAULT_BACKSCROLL 512 #define CONSOLE_CURSOR_PERIOD 500 @@ -148,6 +151,8 @@ static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); +static bool console_compatible_with(QemuConsole *con, + DisplayChangeListener *dcl, Error **errp); static void gui_update(void *opaque) { @@ -289,6 +294,89 @@ void graphic_hw_invalidate(QemuConsole *con) } } +#ifdef CONFIG_PNG +/** + * png_save: Take a screenshot as PNG + * + * Saves screendump as a PNG file + * + * Returns true for success or false for error. + * + * @fd: File descriptor for PNG file. + * @image: Image data in pixman format. + * @errp: Pointer to an error. + */ +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); + g_autofree png_struct *png_ptr = NULL; + g_autofree png_info *info_ptr = NULL; + g_autoptr(pixman_image_t) linebuf = + qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, width); + uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); + FILE *f = fdopen(fd, "wb"); + int y; + if (!f) { + error_setg_errno(errp, errno, + "Failed to create file from file descriptor"); + return false; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (!png_ptr) { + error_setg(errp, "PNG creation failed. Unable to write struct"); + fclose(f); + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) { + error_setg(errp, "PNG creation failed. Unable to write info"); + fclose(f); + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + png_init_io(png_ptr, f); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + for (y = 0; y < height; ++y) { + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); + png_write_row(png_ptr, buf); + } + qemu_pixman_image_unref(linebuf); + + png_write_end(png_ptr, NULL); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + if (fclose(f) != 0) { + error_setg_errno(errp, errno, + "PNG creation failed. Unable to close file"); + return false; + } + + return true; +} + +#else /* no png support */ + +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + error_setg(errp, "Enable PNG support with libpng for screendump"); + return false; +} + +#endif /* CONFIG_PNG */ + static bool ppm_save(int fd, pixman_image_t *image, Error **errp) { int width = pixman_image_get_width(image); @@ -327,7 +415,8 @@ static void graphic_hw_update_bh(void *con) /* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ void coroutine_fn qmp_screendump(const char *filename, bool has_device, const char *device, - bool has_head, int64_t head, Error **errp) + bool has_head, int64_t head, + bool has_format, ImageFormat format, Error **errp) { g_autoptr(pixman_image_t) image = NULL; QemuConsole *con; @@ -383,8 +472,16 @@ qmp_screendump(const char *filename, bool has_device, const char *device, * yields and releases the BQL. It could produce corrupted dump, but * it should be otherwise safe. */ - if (!ppm_save(fd, image, errp)) { - qemu_unlink(filename); + if (has_format && format == IMAGE_FORMAT_PNG) { + /* PNG format specified for screendump */ + if (!png_save(fd, image, errp)) { + qemu_unlink(filename); + } + } else { + /* PPM format specified/default for screendump */ + if (!ppm_save(fd, image, errp)) { + qemu_unlink(filename); + } } } @@ -1056,24 +1153,66 @@ static void console_putchar(QemuConsole *s, int ch) } } +static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl, + struct DisplaySurface *new_surface, + bool update) +{ + if (dcl->ops->dpy_gfx_switch) { + dcl->ops->dpy_gfx_switch(dcl, new_surface); + } + + if (update && dcl->ops->dpy_gfx_update) { + dcl->ops->dpy_gfx_update(dcl, 0, 0, + surface_width(new_surface), + surface_height(new_surface)); + } +} + +static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface) +{ + if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) { + con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface); + } +} + +static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface) +{ + if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) { + con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface); + } +} + +static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface, + int x, int y, int w, int h) +{ + if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) { + con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h); + } +} + static void displaychangelistener_display_console(DisplayChangeListener *dcl, - QemuConsole *con) + QemuConsole *con, + Error **errp) { static const char nodev[] = "This VM has no graphic display device."; static DisplaySurface *dummy; - if (!con) { - if (!dcl->ops->dpy_gfx_switch) { - return; - } + if (!con || !console_compatible_with(con, dcl, errp)) { if (!dummy) { dummy = qemu_create_placeholder_surface(640, 480, nodev); } - dcl->ops->dpy_gfx_switch(dcl, dummy); + if (con) { + dpy_gfx_create_texture(con, dummy); + } + displaychangelistener_gfx_switch(dcl, dummy, TRUE); return; } + dpy_gfx_create_texture(con, con->surface); + displaychangelistener_gfx_switch(dcl, con->surface, + con->scanout.kind == SCANOUT_SURFACE); + if (con->scanout.kind == SCANOUT_DMABUF && displaychangelistener_has_dmabuf(dcl)) { dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf); @@ -1088,14 +1227,7 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, con->scanout.texture.y, con->scanout.texture.width, con->scanout.texture.height); - } else if (con->scanout.kind == SCANOUT_SURFACE && - dcl->ops->dpy_gfx_switch) { - dcl->ops->dpy_gfx_switch(dcl, con->surface); } - - dcl->ops->dpy_gfx_update(dcl, 0, 0, - qemu_console_get_width(con, 0), - qemu_console_get_height(con, 0)); } void console_select(unsigned int index) @@ -1114,7 +1246,7 @@ void console_select(unsigned int index) if (dcl->con != NULL) { continue; } - displaychangelistener_display_console(dcl, s); + displaychangelistener_display_console(dcl, s, NULL); } } if (ds->have_text) { @@ -1475,13 +1607,20 @@ static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) return false; } -static bool dpy_compatible_with(QemuConsole *con, - DisplayChangeListener *dcl, Error **errp) +static bool console_compatible_with(QemuConsole *con, + DisplayChangeListener *dcl, Error **errp) { int flags; flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; + if (console_has_gl(con) && + !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) { + error_setg(errp, "Display %s is incompatible with the GL context", + dcl->ops->dpy_name); + return false; + } + if (flags & GRAPHIC_FLAGS_GL && !console_has_gl(con)) { error_setg(errp, "The console requires a GL context."); @@ -1509,31 +1648,12 @@ void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) con->gl = gl; } -static bool dpy_gl_compatible_with(QemuConsole *con, DisplayChangeListener *dcl) -{ - if (!con->gl) { - return true; - } - - return con->gl->ops->compatible_dcl == dcl->ops; -} - void register_displaychangelistener(DisplayChangeListener *dcl) { QemuConsole *con; assert(!dcl->ds); - if (dcl->con && !dpy_gl_compatible_with(dcl->con, dcl)) { - error_report("Display %s is incompatible with the GL context", - dcl->ops->dpy_name); - exit(1); - } - - if (dcl->con) { - dpy_compatible_with(dcl->con, dcl, &error_fatal); - } - trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); dcl->ds = get_alloc_displaystate(); QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); @@ -1544,7 +1664,7 @@ void register_displaychangelistener(DisplayChangeListener *dcl) } else { con = active_console; } - displaychangelistener_display_console(dcl, con); + displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL); text_console_update_cursor(NULL); } @@ -1638,6 +1758,7 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) if (!qemu_console_is_visible(con)) { return; } + dpy_gfx_update_texture(con, con->surface, x, y, w, h); QLIST_FOREACH(dcl, &s->listeners, next) { if (con != (dcl->con ? dcl->con : active_console)) { continue; @@ -1682,14 +1803,14 @@ void dpy_gfx_replace_surface(QemuConsole *con, con->scanout.kind = SCANOUT_SURFACE; con->surface = surface; + dpy_gfx_create_texture(con, surface); QLIST_FOREACH(dcl, &s->listeners, next) { if (con != (dcl->con ? dcl->con : active_console)) { continue; } - if (dcl->ops->dpy_gfx_switch) { - dcl->ops->dpy_gfx_switch(dcl, surface); - } + displaychangelistener_gfx_switch(dcl, surface, FALSE); } + dpy_gfx_destroy_texture(con, old_surface); qemu_free_displaysurface(old_surface); } @@ -1860,7 +1981,12 @@ void dpy_gl_scanout_disable(QemuConsole *con) con->scanout.kind = SCANOUT_NONE; } QLIST_FOREACH(dcl, &s->listeners, next) { - dcl->ops->dpy_gl_scanout_disable(dcl); + if (con != (dcl->con ? dcl->con : active_console)) { + continue; + } + if (dcl->ops->dpy_gl_scanout_disable) { + dcl->ops->dpy_gl_scanout_disable(dcl); + } } } @@ -1881,10 +2007,15 @@ void dpy_gl_scanout_texture(QemuConsole *con, x, y, width, height }; QLIST_FOREACH(dcl, &s->listeners, next) { - dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, - backing_y_0_top, - backing_width, backing_height, - x, y, width, height); + if (con != (dcl->con ? dcl->con : active_console)) { + continue; + } + if (dcl->ops->dpy_gl_scanout_texture) { + dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, + backing_y_0_top, + backing_width, backing_height, + x, y, width, height); + } } } @@ -1897,7 +2028,12 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con, con->scanout.kind = SCANOUT_DMABUF; con->scanout.dmabuf = dmabuf; QLIST_FOREACH(dcl, &s->listeners, next) { - dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); + if (con != (dcl->con ? dcl->con : active_console)) { + continue; + } + if (dcl->ops->dpy_gl_scanout_dmabuf) { + dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); + } } } @@ -1908,6 +2044,9 @@ void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, DisplayChangeListener *dcl; QLIST_FOREACH(dcl, &s->listeners, next) { + if (con != (dcl->con ? dcl->con : active_console)) { + continue; + } if (dcl->ops->dpy_gl_cursor_dmabuf) { dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf, have_hot, hot_x, hot_y); @@ -1922,6 +2061,9 @@ void dpy_gl_cursor_position(QemuConsole *con, DisplayChangeListener *dcl; QLIST_FOREACH(dcl, &s->listeners, next) { + if (con != (dcl->con ? dcl->con : active_console)) { + continue; + } if (dcl->ops->dpy_gl_cursor_position) { dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y); } @@ -1935,6 +2077,9 @@ void dpy_gl_release_dmabuf(QemuConsole *con, DisplayChangeListener *dcl; QLIST_FOREACH(dcl, &s->listeners, next) { + if (con != (dcl->con ? dcl->con : active_console)) { + continue; + } if (dcl->ops->dpy_gl_release_dmabuf) { dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf); } @@ -1951,7 +2096,12 @@ void dpy_gl_update(QemuConsole *con, graphic_hw_gl_block(con, true); QLIST_FOREACH(dcl, &s->listeners, next) { - dcl->ops->dpy_gl_update(dcl, x, y, w, h); + if (con != (dcl->con ? dcl->con : active_console)) { + continue; + } + if (dcl->ops->dpy_gl_update) { + dcl->ops->dpy_gl_update(dcl, x, y, w, h); + } } graphic_hw_gl_block(con, false); } @@ -2392,13 +2542,12 @@ static void vc_chr_open(Chardev *chr, void qemu_console_resize(QemuConsole *s, int width, int height) { - DisplaySurface *surface = qemu_console_surface(s); + DisplaySurface *surface; assert(s->console_type == GRAPHIC_CONSOLE); - if (surface && (surface->flags & QEMU_ALLOCATED_FLAG) && - pixman_image_get_width(surface->image) == width && - pixman_image_get_height(surface->image) == height) { + if (qemu_console_get_width(s, -1) == width && + qemu_console_get_height(s, -1) == height) { return; } diff --git a/ui/cursor.c b/ui/cursor.c index 1d62ddd4d0..835f0802f9 100644 --- a/ui/cursor.c +++ b/ui/cursor.c @@ -46,6 +46,8 @@ static QEMUCursor *cursor_parse_xpm(const char *xpm[]) /* parse pixel data */ c = cursor_alloc(width, height); + assert(c != NULL); + for (pixel = 0, y = 0; y < height; y++, line++) { for (x = 0; x < height; x++, pixel++) { idx = xpm[line][x]; @@ -91,7 +93,11 @@ QEMUCursor *cursor_builtin_left_ptr(void) QEMUCursor *cursor_alloc(int width, int height) { QEMUCursor *c; - int datasize = width * height * sizeof(uint32_t); + size_t datasize = width * height * sizeof(uint32_t); + + if (width > 512 || height > 512) { + return NULL; + } c = g_malloc0(sizeof(QEMUCursor) + datasize); c->width = width; diff --git a/ui/dbus-console.c b/ui/dbus-console.c index e062f721d7..898a4ac8a5 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -36,7 +36,6 @@ struct _DBusDisplayConsole { DisplayChangeListener dcl; DBusDisplay *display; - QemuConsole *con; GHashTable *listeners; QemuDBusDisplay1Console *iface; @@ -118,7 +117,7 @@ dbus_gl_scanout_update(DisplayChangeListener *dcl, { } -static const DisplayChangeListenerOps dbus_console_dcl_ops = { +const DisplayChangeListenerOps dbus_console_dcl_ops = { .dpy_name = "dbus-console", .dpy_gfx_switch = dbus_gfx_switch, .dpy_gfx_update = dbus_gfx_update, @@ -191,7 +190,7 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc, .height = arg_height, }; - if (!dpy_ui_info_supported(ddc->con)) { + if (!dpy_ui_info_supported(ddc->dcl.con)) { g_dbus_method_invocation_return_error(invocation, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_UNSUPPORTED, @@ -199,7 +198,7 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - dpy_set_ui_info(ddc->con, &info, false); + dpy_set_ui_info(ddc->dcl.con, &info, false); qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation); return DBUS_METHOD_INVOCATION_HANDLED; } @@ -335,8 +334,8 @@ dbus_mouse_rel_motion(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - qemu_input_queue_rel(ddc->con, INPUT_AXIS_X, dx); - qemu_input_queue_rel(ddc->con, INPUT_AXIS_Y, dy); + qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_X, dx); + qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_Y, dy); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_rel_motion(ddc->iface_mouse, @@ -362,8 +361,8 @@ dbus_mouse_set_pos(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - width = qemu_console_get_width(ddc->con, 0); - height = qemu_console_get_height(ddc->con, 0); + width = qemu_console_get_width(ddc->dcl.con, 0); + height = qemu_console_get_height(ddc->dcl.con, 0); if (x >= width || y >= height) { g_dbus_method_invocation_return_error( invocation, DBUS_DISPLAY_ERROR, @@ -371,8 +370,8 @@ dbus_mouse_set_pos(DBusDisplayConsole *ddc, "Invalid mouse position"); return DBUS_METHOD_INVOCATION_HANDLED; } - qemu_input_queue_abs(ddc->con, INPUT_AXIS_X, x, 0, width); - qemu_input_queue_abs(ddc->con, INPUT_AXIS_Y, y, 0, height); + qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_X, x, 0, width); + qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_Y, y, 0, height); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_set_abs_position(ddc->iface_mouse, @@ -388,7 +387,7 @@ dbus_mouse_press(DBusDisplayConsole *ddc, { trace_dbus_mouse_press(button); - qemu_input_queue_btn(ddc->con, button, true); + qemu_input_queue_btn(ddc->dcl.con, button, true); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_press(ddc->iface_mouse, invocation); @@ -403,7 +402,7 @@ dbus_mouse_release(DBusDisplayConsole *ddc, { trace_dbus_mouse_release(button); - qemu_input_queue_btn(ddc->con, button, false); + qemu_input_queue_btn(ddc->dcl.con, button, false); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_release(ddc->iface_mouse, invocation); @@ -424,7 +423,7 @@ dbus_mouse_mode_change(Notifier *notify, void *data) int dbus_display_console_get_index(DBusDisplayConsole *ddc) { - return qemu_console_get_index(ddc->con); + return qemu_console_get_index(ddc->dcl.con); } DBusDisplayConsole * @@ -446,7 +445,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) "g-object-path", path, NULL); ddc->display = display; - ddc->con = con; + ddc->dcl.con = con; /* handle errors, and skip non graphics? */ qemu_console_fill_device_address( con, device_addr, sizeof(device_addr), NULL); diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 81c119b13a..f9fc8eda51 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -42,7 +42,6 @@ struct _DBusDisplayListener { DisplayChangeListener dcl; DisplaySurface *ds; - QemuGLShader *gls; int gl_updates; }; @@ -240,10 +239,6 @@ static void dbus_gl_gfx_update(DisplayChangeListener *dcl, { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - if (ddl->ds) { - surface_gl_update_texture(ddl->gls, ddl->ds, x, y, w, h); - } - ddl->gl_updates++; } @@ -260,6 +255,26 @@ static void dbus_gfx_update(DisplayChangeListener *dcl, trace_dbus_update(x, y, w, h); + if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { + v_data = g_variant_new_from_data( + G_VARIANT_TYPE("ay"), + surface_data(ddl->ds), + surface_stride(ddl->ds) * surface_height(ddl->ds), + TRUE, + (GDestroyNotify)pixman_image_unref, + pixman_image_ref(ddl->ds->image)); + qemu_dbus_display1_listener_call_scanout( + ddl->proxy, + surface_width(ddl->ds), + surface_height(ddl->ds), + surface_stride(ddl->ds), + surface_format(ddl->ds), + v_data, + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); + return; + } + /* make a copy, since gvariant only handles linear data */ img = pixman_image_create_bits(surface_format(ddl->ds), w, h, NULL, stride); @@ -285,15 +300,11 @@ static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - if (ddl->ds) { - surface_gl_destroy_texture(ddl->gls, ddl->ds); - } ddl->ds = new_surface; if (ddl->ds) { int width = surface_width(ddl->ds); int height = surface_height(ddl->ds); - surface_gl_create_texture(ddl->gls, ddl->ds); /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, width, height, 0, 0, width, height); @@ -304,29 +315,12 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - GVariant *v_data = NULL; ddl->ds = new_surface; if (!ddl->ds) { /* why not call disable instead? */ return; } - - v_data = g_variant_new_from_data( - G_VARIANT_TYPE("ay"), - surface_data(ddl->ds), - surface_stride(ddl->ds) * surface_height(ddl->ds), - TRUE, - (GDestroyNotify)pixman_image_unref, - pixman_image_ref(ddl->ds->image)); - qemu_dbus_display1_listener_call_scanout(ddl->proxy, - surface_width(ddl->ds), - surface_height(ddl->ds), - surface_stride(ddl->ds), - surface_format(ddl->ds), - v_data, - G_DBUS_CALL_FLAGS_NONE, - DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); } static void dbus_mouse_set(DisplayChangeListener *dcl, @@ -403,7 +397,6 @@ dbus_display_listener_dispose(GObject *object) g_clear_object(&ddl->conn); g_clear_pointer(&ddl->bus_name, g_free); g_clear_object(&ddl->proxy); - g_clear_pointer(&ddl->gls, qemu_gl_fini_shader); G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); } @@ -414,7 +407,6 @@ dbus_display_listener_constructed(GObject *object) DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); if (display_opengl) { - ddl->gls = qemu_gl_init_shader(); ddl->dcl.ops = &dbus_gl_dcl_ops; } else { ddl->dcl.ops = &dbus_dcl_ops; diff --git a/ui/dbus.c b/ui/dbus.c index 0074424c1f..7a87612379 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -48,11 +48,40 @@ static QEMUGLContext dbus_create_context(DisplayGLCtx *dgc, return qemu_egl_create_context(dgc, params); } +static bool +dbus_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dbus_gl_dcl_ops || dcl->ops == &dbus_console_dcl_ops; +} + +static void +dbus_create_texture(DisplayGLCtx *ctx, DisplaySurface *surface) +{ + surface_gl_create_texture(ctx->gls, surface); +} + +static void +dbus_destroy_texture(DisplayGLCtx *ctx, DisplaySurface *surface) +{ + surface_gl_destroy_texture(ctx->gls, surface); +} + +static void +dbus_update_texture(DisplayGLCtx *ctx, DisplaySurface *surface, + int x, int y, int w, int h) +{ + surface_gl_update_texture(ctx->gls, surface, x, y, w, h); +} + static const DisplayGLCtxOps dbus_gl_ops = { - .compatible_dcl = &dbus_gl_dcl_ops, + .dpy_gl_ctx_is_compatible_dcl = dbus_is_compatible_dcl, .dpy_gl_ctx_create = dbus_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, + .dpy_gl_ctx_create_texture = dbus_create_texture, + .dpy_gl_ctx_destroy_texture = dbus_destroy_texture, + .dpy_gl_ctx_update_texture = dbus_update_texture, }; static NotifierList dbus_display_notifiers = @@ -83,6 +112,9 @@ dbus_display_init(Object *o) g_autoptr(GDBusObjectSkeleton) vm = NULL; dd->glctx.ops = &dbus_gl_ops; + if (display_opengl) { + dd->glctx.gls = qemu_gl_init_shader(); + } dd->iface = qemu_dbus_display1_vm_skeleton_new(); dd->consoles = g_ptr_array_new_with_free_func(g_object_unref); @@ -119,6 +151,7 @@ dbus_display_finalize(Object *o) g_clear_object(&dd->iface); g_free(dd->dbus_addr); g_free(dd->audiodev); + g_clear_pointer(&dd->glctx.gls, qemu_gl_fini_shader); dbus_display = NULL; } diff --git a/ui/dbus.h b/ui/dbus.h index 64c77cab44..5f5c1f759c 100644 --- a/ui/dbus.h +++ b/ui/dbus.h @@ -79,6 +79,9 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con); int dbus_display_console_get_index(DBusDisplayConsole *ddc); + +extern const DisplayChangeListenerOps dbus_console_dcl_ops; + #define DBUS_DISPLAY_TYPE_LISTENER dbus_display_listener_get_type() G_DECLARE_FINAL_TYPE(DBusDisplayListener, dbus_display_listener, diff --git a/ui/egl-headless.c b/ui/egl-headless.c index 94082a9da9..7a30fd9777 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -166,8 +166,23 @@ static const DisplayChangeListenerOps egl_ops = { .dpy_gl_update = egl_scanout_flush, }; +static bool +egl_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + if (!dcl->ops->dpy_gl_update) { + /* + * egl-headless is compatible with all 2d listeners, as it blits the GL + * updates on the 2d console surface. + */ + return true; + } + + return dcl->ops == &egl_ops; +} + static const DisplayGLCtxOps eglctx_ops = { - .compatible_dcl = &egl_ops, + .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl, .dpy_gl_ctx_create = egl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c index d58fd761ab..8d8a636fd1 100644 --- a/ui/gtk-clipboard.c +++ b/ui/gtk-clipboard.c @@ -19,7 +19,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/main-loop.h" #include "ui/gtk.h" diff --git a/ui/gtk.c b/ui/gtk.c index a8567b9ddc..c57c36749e 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -614,8 +614,15 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_has_dmabuf = gd_has_dmabuf, }; +static bool +gd_gl_area_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dcl_gl_area_ops; +} + static const DisplayGLCtxOps gl_area_ctx_ops = { - .compatible_dcl = &dcl_gl_area_ops, + .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl, .dpy_gl_ctx_create = gd_gl_area_create_context, .dpy_gl_ctx_destroy = gd_gl_area_destroy_context, .dpy_gl_ctx_make_current = gd_gl_area_make_current, @@ -641,8 +648,15 @@ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_has_dmabuf = gd_has_dmabuf, }; +static bool +gd_egl_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dcl_egl_ops; +} + static const DisplayGLCtxOps egl_ctx_ops = { - .compatible_dcl = &dcl_egl_ops, + .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl, .dpy_gl_ctx_create = gd_egl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = gd_egl_make_current, @@ -958,6 +972,10 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, return TRUE; } + if (button->type == GDK_2BUTTON_PRESS || button->type == GDK_3BUTTON_PRESS) { + return TRUE; + } + qemu_input_queue_btn(vc->gfx.dcl.con, btn, button->type == GDK_BUTTON_PRESS); qemu_input_event_sync(); diff --git a/ui/input-linux.c b/ui/input-linux.c index 05c0c98819..e572a2e905 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -316,7 +316,10 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) error_setg_file_open(errp, errno, il->evdev); return; } - qemu_set_nonblock(il->fd); + if (!g_unix_set_fd_nonblocking(il->fd, true, NULL)) { + error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + return; + } rc = ioctl(il->fd, EVIOCGVERSION, &ver); if (rc < 0) { diff --git a/ui/keymaps.c b/ui/keymaps.c index d4a647464b..6ceaa97085 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/datadir.h" #include "keymaps.h" #include "trace.h" diff --git a/ui/meson.build b/ui/meson.build index 64286ba150..eba93b41e3 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -1,5 +1,6 @@ softmmu_ss.add(pixman) specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: pixman) # for the include path +specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: opengl) # for the include path softmmu_ss.add(files( 'clipboard.c', @@ -43,7 +44,6 @@ vnc_ss.add(zlib, png, jpeg, gnutls) vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c')) softmmu_ss.add_all(when: vnc, if_true: vnc_ss) softmmu_ss.add(when: vnc, if_false: files('vnc-stubs.c')) -specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: opengl) ui_modules = {} @@ -53,17 +53,18 @@ if curses.found() ui_modules += {'curses' : curses_ss} endif -if config_host.has_key('CONFIG_OPENGL') +softmmu_ss.add(opengl) +if opengl.found() opengl_ss = ss.source_set() opengl_ss.add(gbm) - opengl_ss.add(when: [opengl, pixman, 'CONFIG_OPENGL'], + opengl_ss.add(when: [opengl, pixman], if_true: files('shader.c', 'console-gl.c', 'egl-helpers.c', 'egl-context.c')) ui_modules += {'opengl' : opengl_ss} endif -if config_host.has_key('CONFIG_OPENGL') and gbm.found() +if opengl.found() and gbm.found() egl_headless_ss = ss.source_set() - egl_headless_ss.add(when: [opengl, gbm, pixman, 'CONFIG_OPENGL'], + egl_headless_ss.add(when: [opengl, gbm, pixman], if_true: files('egl-headless.c')) ui_modules += {'egl-headless' : egl_headless_ss} endif @@ -73,14 +74,13 @@ if dbus_display dbus_display1 = custom_target('dbus-display gdbus-codegen', output: ['dbus-display1.h', 'dbus-display1.c'], input: files('dbus-display1.xml'), - command: [config_host['GDBUS_CODEGEN'], - '@INPUT@', + command: [gdbus_codegen, '@INPUT@', '--glib-min-required', '2.64', '--output-directory', meson.current_build_dir(), '--interface-prefix', 'org.qemu.', '--c-namespace', 'QemuDBus', '--generate-c-code', '@BASENAME@']) - dbus_ss.add(when: [gio, pixman, opengl, 'CONFIG_GIO'], + dbus_ss.add(when: [gio, pixman, opengl], if_true: [files( 'dbus-chardev.c', 'dbus-clipboard.c', @@ -98,8 +98,8 @@ if gtk.found() gtk_ss = ss.source_set() gtk_ss.add(gtk, vte, pixman, files('gtk.c', 'gtk-clipboard.c')) gtk_ss.add(when: x11, if_true: files('x_keymap.c')) - gtk_ss.add(when: [opengl, 'CONFIG_OPENGL'], if_true: files('gtk-gl-area.c')) - gtk_ss.add(when: [x11, opengl, 'CONFIG_OPENGL'], if_true: files('gtk-egl.c')) + gtk_ss.add(when: opengl, if_true: files('gtk-gl-area.c')) + gtk_ss.add(when: [x11, opengl], if_true: files('gtk-egl.c')) ui_modules += {'gtk' : gtk_ss} endif @@ -112,7 +112,7 @@ if sdl.found() 'sdl2-input.c', 'sdl2.c', )) - sdl_ss.add(when: [opengl, 'CONFIG_OPENGL'], if_true: files('sdl2-gl.c')) + sdl_ss.add(when: opengl, if_true: files('sdl2-gl.c')) sdl_ss.add(when: x11, if_true: files('x_keymap.c')) ui_modules += {'sdl' : sdl_ss} endif @@ -127,7 +127,7 @@ if spice.found() ui_modules += {'spice-core' : spice_core_ss} endif -if spice.found() and config_host.has_key('CONFIG_GIO') +if spice.found() and gio.found() spice_ss = ss.source_set() spice_ss.add(spice, gio, pixman, files('spice-app.c')) ui_modules += {'spice-app': spice_ss} diff --git a/ui/sdl2.c b/ui/sdl2.c index 46a252d7d9..d3741f9b75 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -788,8 +788,15 @@ static const DisplayChangeListenerOps dcl_gl_ops = { .dpy_gl_update = sdl2_gl_scanout_flush, }; +static bool +sdl2_gl_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dcl_gl_ops; +} + static const DisplayGLCtxOps gl_ctx_ops = { - .compatible_dcl = &dcl_gl_ops, + .dpy_gl_ctx_is_compatible_dcl = sdl2_gl_is_compatible_dcl, .dpy_gl_ctx_create = sdl2_gl_create_context, .dpy_gl_ctx_destroy = sdl2_gl_destroy_context, .dpy_gl_ctx_make_current = sdl2_gl_make_context_current, diff --git a/ui/shader.c b/ui/shader.c index e8b8d321b7..ab448c41d4 100644 --- a/ui/shader.c +++ b/ui/shader.c @@ -130,15 +130,17 @@ static GLuint qemu_gl_create_link_program(GLuint vert, GLuint frag) static GLuint qemu_gl_create_compile_link_program(const GLchar *vert_src, const GLchar *frag_src) { - GLuint vert_shader, frag_shader, program; + GLuint vert_shader, frag_shader, program = 0; vert_shader = qemu_gl_create_compile_shader(GL_VERTEX_SHADER, vert_src); frag_shader = qemu_gl_create_compile_shader(GL_FRAGMENT_SHADER, frag_src); if (!vert_shader || !frag_shader) { - return 0; + goto end; } program = qemu_gl_create_link_program(vert_shader, frag_shader); + +end: glDeleteShader(vert_shader); glDeleteShader(frag_shader); @@ -170,5 +172,8 @@ void qemu_gl_fini_shader(QemuGLShader *gls) if (!gls) { return; } + glDeleteProgram(gls->texture_blit_prog); + glDeleteProgram(gls->texture_blit_flip_prog); + glDeleteProgram(gls->texture_blit_vao); g_free(gls); } diff --git a/ui/spice-display.c b/ui/spice-display.c index a3078adf91..494168e7fe 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1125,8 +1125,15 @@ static const DisplayChangeListenerOps display_listener_gl_ops = { .dpy_gl_update = qemu_spice_gl_update, }; +static bool +qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &display_listener_gl_ops; +} + static const DisplayGLCtxOps gl_ctx_ops = { - .compatible_dcl = &display_listener_gl_ops, + .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl, .dpy_gl_ctx_create = qemu_spice_gl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, diff --git a/ui/vdagent.c b/ui/vdagent.c index 7ea4bc5d9a..aa6167f0b4 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "include/qemu-common.h" #include "chardev/char.h" #include "qemu/buffer.h" #include "qemu/option.h" @@ -664,7 +663,7 @@ static void vdagent_chr_open(Chardev *chr, VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); ChardevQemuVDAgent *cfg = backend->u.qemu_vdagent.data; -#if defined(HOST_WORDS_BIGENDIAN) +#if HOST_BIG_ENDIAN /* * TODO: vdagent protocol is defined to be LE, * so we have to byteswap everything on BE hosts. diff --git a/ui/vnc-clipboard.c b/ui/vnc-clipboard.c index d48f75eb1a..8aeadfaa21 100644 --- a/ui/vnc-clipboard.c +++ b/ui/vnc-clipboard.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "vnc.h" #include "vnc-jobs.h" diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index cebd35841a..09200d71b8 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -32,9 +32,9 @@ INT32 definitions between jmorecfg.h (included by jpeglib.h) and Win32 basetsd.h (included by windows.h). */ -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG /* The following define is needed by pngconf.h. Otherwise it won't compile, - because setjmp.h was already included by qemu-common.h. */ + because setjmp.h was already included by osdep.h. */ #define PNG_SKIP_SETJMP_CHECK #include #endif @@ -95,7 +95,7 @@ static const struct { }; #endif -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG static const struct { int png_zlib_level, png_filters; } tight_png_conf[] = { @@ -919,7 +919,7 @@ static int send_full_color_rect(VncState *vs, int x, int y, int w, int h) int stream = 0; ssize_t bytes; -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG if (tight_can_send_png_rect(vs, w, h)) { return send_png_rect(vs, x, y, w, h, NULL); } @@ -966,7 +966,7 @@ static int send_mono_rect(VncState *vs, int x, int y, int stream = 1; int level = tight_conf[vs->tight->compression].mono_zlib_level; -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG if (tight_can_send_png_rect(vs, w, h)) { int ret; int bpp = vs->client_pf.bytes_per_pixel * 8; @@ -1020,7 +1020,7 @@ static int send_mono_rect(VncState *vs, int x, int y, struct palette_cb_priv { VncState *vs; uint8_t *header; -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG png_colorp png_palette; #endif }; @@ -1082,7 +1082,7 @@ static int send_palette_rect(VncState *vs, int x, int y, int colors; ssize_t bytes; -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG if (tight_can_send_png_rect(vs, w, h)) { return send_png_rect(vs, x, y, w, h, palette); } @@ -1233,7 +1233,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) /* * PNG compression stuff. */ -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG static void write_png_palette(int idx, uint32_t pix, void *opaque) { struct palette_cb_priv *priv = opaque; @@ -1379,7 +1379,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, buffer_reset(&vs->tight->png); return 1; } -#endif /* CONFIG_VNC_PNG */ +#endif /* CONFIG_PNG */ static void vnc_tight_start(VncState *vs) { @@ -1477,7 +1477,7 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) #endif if (!color_count_palette) { - color_count_palette = g_malloc(sizeof(VncPalette)); + color_count_palette = g_new(VncPalette, 1); vnc_tight_cleanup_notifier.notify = vnc_tight_cleanup; qemu_thread_atexit_add(&vnc_tight_cleanup_notifier); } @@ -1706,7 +1706,7 @@ void vnc_tight_clear(VncState *vs) #ifdef CONFIG_VNC_JPEG buffer_free(&vs->tight->jpeg); #endif -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG buffer_free(&vs->tight->png); #endif } diff --git a/ui/vnc.c b/ui/vnc.c index 3ccd33dedc..6a05d06147 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -25,7 +25,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "vnc.h" #include "vnc-jobs.h" #include "trace.h" @@ -55,6 +54,7 @@ #include "qemu/cutils.h" #include "qemu/help_option.h" #include "io/dns-resolver.h" +#include "monitor/monitor.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -2165,7 +2165,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->features |= VNC_FEATURE_TIGHT_MASK; vs->vnc_encoding = enc; break; -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG case VNC_ENCODING_TIGHT_PNG: vs->features |= VNC_FEATURE_TIGHT_PNG_MASK; vs->vnc_encoding = enc; @@ -2340,7 +2340,7 @@ static void pixel_format_message (VncState *vs) { vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */ vnc_write_u8(vs, vs->client_pf.depth); /* depth */ -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN vnc_write_u8(vs, 1); /* big-endian-flag */ #else vnc_write_u8(vs, 0); /* big-endian-flag */ @@ -3098,6 +3098,9 @@ static int vnc_refresh_server_surface(VncDisplay *vd) VncState *vs; int has_dirty = 0; pixman_image_t *tmpbuf = NULL; + unsigned long offset; + int x; + uint8_t *guest_ptr, *server_ptr; struct timeval tv = { 0, 0 }; @@ -3106,6 +3109,13 @@ static int vnc_refresh_server_surface(VncDisplay *vd) has_dirty = vnc_update_stats(vd, &tv); } + offset = find_next_bit((unsigned long *) &vd->guest.dirty, + height * VNC_DIRTY_BPL(&vd->guest), 0); + if (offset == height * VNC_DIRTY_BPL(&vd->guest)) { + /* no dirty bits in guest surface */ + return has_dirty; + } + /* * Walk through the guest dirty map. * Check and copy modified bits from guest to server surface. @@ -3130,15 +3140,6 @@ static int vnc_refresh_server_surface(VncDisplay *vd) line_bytes = MIN(server_stride, guest_ll); for (;;) { - int x; - uint8_t *guest_ptr, *server_ptr; - unsigned long offset = find_next_bit((unsigned long *) &vd->guest.dirty, - height * VNC_DIRTY_BPL(&vd->guest), - y * VNC_DIRTY_BPL(&vd->guest)); - if (offset == height * VNC_DIRTY_BPL(&vd->guest)) { - /* no more dirty bits */ - break; - } y = offset / VNC_DIRTY_BPL(&vd->guest); x = offset % VNC_DIRTY_BPL(&vd->guest); @@ -3177,6 +3178,13 @@ static int vnc_refresh_server_surface(VncDisplay *vd) } y++; + offset = find_next_bit((unsigned long *) &vd->guest.dirty, + height * VNC_DIRTY_BPL(&vd->guest), + y * VNC_DIRTY_BPL(&vd->guest)); + if (offset == height * VNC_DIRTY_BPL(&vd->guest)) { + /* no more dirty bits */ + break; + } } qemu_pixman_image_unref(tmpbuf); return has_dirty; @@ -3248,7 +3256,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, #ifdef CONFIG_VNC_JPEG buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc); #endif -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc); #endif buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc); @@ -3812,30 +3820,19 @@ static int vnc_display_get_address(const char *addrstr, return ret; } -static void vnc_free_addresses(SocketAddress ***retsaddr, - size_t *retnsaddr) -{ - size_t i; - - for (i = 0; i < *retnsaddr; i++) { - qapi_free_SocketAddress((*retsaddr)[i]); - } - g_free(*retsaddr); - - *retsaddr = NULL; - *retnsaddr = 0; -} - static int vnc_display_get_addresses(QemuOpts *opts, bool reverse, - SocketAddress ***retsaddr, - size_t *retnsaddr, - SocketAddress ***retwsaddr, - size_t *retnwsaddr, + SocketAddressList **saddr_list_ret, + SocketAddressList **wsaddr_list_ret, Error **errp) { SocketAddress *saddr = NULL; SocketAddress *wsaddr = NULL; + g_autoptr(SocketAddressList) saddr_list = NULL; + SocketAddressList **saddr_tail = &saddr_list; + SocketAddress *single_saddr = NULL; + g_autoptr(SocketAddressList) wsaddr_list = NULL; + SocketAddressList **wsaddr_tail = &wsaddr_list; QemuOptsIter addriter; const char *addr; int to = qemu_opt_get_number(opts, "to", 0); @@ -3844,23 +3841,16 @@ static int vnc_display_get_addresses(QemuOpts *opts, bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false); bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false); int displaynum = -1; - int ret = -1; - - *retsaddr = NULL; - *retnsaddr = 0; - *retwsaddr = NULL; - *retnwsaddr = 0; addr = qemu_opt_get(opts, "vnc"); if (addr == NULL || g_str_equal(addr, "none")) { - ret = 0; - goto cleanup; + return 0; } if (qemu_opt_get(opts, "websocket") && !qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { error_setg(errp, "SHA1 hash support is required for websockets"); - goto cleanup; + return -1; } qemu_opt_iter_init(&addriter, opts, "vnc"); @@ -3871,7 +3861,7 @@ static int vnc_display_get_addresses(QemuOpts *opts, ipv4, ipv6, &saddr, errp); if (rv < 0) { - goto cleanup; + return -1; } /* Historical compat - first listen address can be used * to set the default websocket port @@ -3879,13 +3869,16 @@ static int vnc_display_get_addresses(QemuOpts *opts, if (displaynum == -1) { displaynum = rv; } - *retsaddr = g_renew(SocketAddress *, *retsaddr, *retnsaddr + 1); - (*retsaddr)[(*retnsaddr)++] = saddr; + QAPI_LIST_APPEND(saddr_tail, saddr); } - /* If we had multiple primary displays, we don't do defaults - * for websocket, and require explicit config instead. */ - if (*retnsaddr > 1) { + if (saddr_list && !saddr_list->next) { + single_saddr = saddr_list->value; + } else { + /* + * If we had multiple primary displays, we don't do defaults + * for websocket, and require explicit config instead. + */ displaynum = -1; } @@ -3895,57 +3888,50 @@ static int vnc_display_get_addresses(QemuOpts *opts, has_ipv4, has_ipv6, ipv4, ipv6, &wsaddr, errp) < 0) { - goto cleanup; + return -1; } /* Historical compat - if only a single listen address was * provided, then this is used to set the default listen * address for websocket too */ - if (*retnsaddr == 1 && - (*retsaddr)[0]->type == SOCKET_ADDRESS_TYPE_INET && + if (single_saddr && + single_saddr->type == SOCKET_ADDRESS_TYPE_INET && wsaddr->type == SOCKET_ADDRESS_TYPE_INET && g_str_equal(wsaddr->u.inet.host, "") && - !g_str_equal((*retsaddr)[0]->u.inet.host, "")) { + !g_str_equal(single_saddr->u.inet.host, "")) { g_free(wsaddr->u.inet.host); - wsaddr->u.inet.host = g_strdup((*retsaddr)[0]->u.inet.host); + wsaddr->u.inet.host = g_strdup(single_saddr->u.inet.host); } - *retwsaddr = g_renew(SocketAddress *, *retwsaddr, *retnwsaddr + 1); - (*retwsaddr)[(*retnwsaddr)++] = wsaddr; + QAPI_LIST_APPEND(wsaddr_tail, wsaddr); } - ret = 0; - cleanup: - if (ret < 0) { - vnc_free_addresses(retsaddr, retnsaddr); - vnc_free_addresses(retwsaddr, retnwsaddr); - } - return ret; + *saddr_list_ret = g_steal_pointer(&saddr_list); + *wsaddr_list_ret = g_steal_pointer(&wsaddr_list); + return 0; } static int vnc_display_connect(VncDisplay *vd, - SocketAddress **saddr, - size_t nsaddr, - SocketAddress **wsaddr, - size_t nwsaddr, + SocketAddressList *saddr_list, + SocketAddressList *wsaddr_list, Error **errp) { /* connect to viewer */ QIOChannelSocket *sioc = NULL; - if (nwsaddr != 0) { + if (wsaddr_list) { error_setg(errp, "Cannot use websockets in reverse mode"); return -1; } - if (nsaddr != 1) { + if (!saddr_list || saddr_list->next) { error_setg(errp, "Expected a single address in reverse mode"); return -1; } /* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */ - vd->is_unix = saddr[0]->type == SOCKET_ADDRESS_TYPE_UNIX; + vd->is_unix = saddr_list->value->type == SOCKET_ADDRESS_TYPE_UNIX; sioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse"); - if (qio_channel_socket_connect_sync(sioc, saddr[0], errp) < 0) { + if (qio_channel_socket_connect_sync(sioc, saddr_list->value, errp) < 0) { object_unref(OBJECT(sioc)); return -1; } @@ -3956,20 +3942,18 @@ static int vnc_display_connect(VncDisplay *vd, static int vnc_display_listen(VncDisplay *vd, - SocketAddress **saddr, - size_t nsaddr, - SocketAddress **wsaddr, - size_t nwsaddr, + SocketAddressList *saddr_list, + SocketAddressList *wsaddr_list, Error **errp) { - size_t i; + SocketAddressList *el; - if (nsaddr) { + if (saddr_list) { vd->listener = qio_net_listener_new(); qio_net_listener_set_name(vd->listener, "vnc-listen"); - for (i = 0; i < nsaddr; i++) { + for (el = saddr_list; el; el = el->next) { if (qio_net_listener_open_sync(vd->listener, - saddr[i], 1, + el->value, 1, errp) < 0) { return -1; } @@ -3979,12 +3963,12 @@ static int vnc_display_listen(VncDisplay *vd, vnc_listen_io, vd, NULL); } - if (nwsaddr) { + if (wsaddr_list) { vd->wslistener = qio_net_listener_new(); qio_net_listener_set_name(vd->wslistener, "vnc-ws-listen"); - for (i = 0; i < nwsaddr; i++) { + for (el = wsaddr_list; el; el = el->next) { if (qio_net_listener_open_sync(vd->wslistener, - wsaddr[i], 1, + el->value, 1, errp) < 0) { return -1; } @@ -3997,13 +3981,36 @@ static int vnc_display_listen(VncDisplay *vd, return 0; } +bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp) +{ + VncDisplay *vd = vnc_display_find(NULL); + + if (!vd) { + error_setg(errp, "Can not find vnc display"); + return false; + } + + if (arg->has_addresses) { + if (vd->listener) { + qio_net_listener_disconnect(vd->listener); + object_unref(OBJECT(vd->listener)); + vd->listener = NULL; + } + + if (vnc_display_listen(vd, arg->addresses, NULL, errp) < 0) { + return false; + } + } + + return true; +} void vnc_display_open(const char *id, Error **errp) { VncDisplay *vd = vnc_display_find(id); QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id); - SocketAddress **saddr = NULL, **wsaddr = NULL; - size_t nsaddr, nwsaddr; + g_autoptr(SocketAddressList) saddr_list = NULL; + g_autoptr(SocketAddressList) wsaddr_list = NULL; const char *share, *device_id; QemuConsole *con; bool password = false; @@ -4028,8 +4035,8 @@ void vnc_display_open(const char *id, Error **errp) } reverse = qemu_opt_get_bool(opts, "reverse", false); - if (vnc_display_get_addresses(opts, reverse, &saddr, &nsaddr, - &wsaddr, &nwsaddr, errp) < 0) { + if (vnc_display_get_addresses(opts, reverse, &saddr_list, &wsaddr_list, + errp) < 0) { goto fail; } @@ -4051,13 +4058,6 @@ void vnc_display_open(const char *id, Error **errp) password = qemu_opt_get_bool(opts, "password", false); } if (password) { - if (fips_get_state()) { - error_setg(errp, - "VNC password auth disabled due to FIPS mode, " - "consider using the VeNCrypt or SASL authentication " - "methods as an alternative"); - goto fail; - } if (!qcrypto_cipher_supports( QCRYPTO_CIPHER_ALG_DES, QCRYPTO_CIPHER_MODE_ECB)) { error_setg(errp, @@ -4211,16 +4211,16 @@ void vnc_display_open(const char *id, Error **errp) } qkbd_state_set_delay(vd->kbd, key_delay_ms); - if (saddr == NULL) { - goto cleanup; + if (saddr_list == NULL) { + return; } if (reverse) { - if (vnc_display_connect(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) { + if (vnc_display_connect(vd, saddr_list, wsaddr_list, errp) < 0) { goto fail; } } else { - if (vnc_display_listen(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) { + if (vnc_display_listen(vd, saddr_list, wsaddr_list, errp) < 0) { goto fail; } } @@ -4229,14 +4229,11 @@ void vnc_display_open(const char *id, Error **errp) vnc_display_print_local_addr(vd); } - cleanup: - vnc_free_addresses(&saddr, &nsaddr); - vnc_free_addresses(&wsaddr, &nwsaddr); + /* Success */ return; fail: vnc_display_close(vd); - goto cleanup; } void vnc_display_add_client(const char *id, int csock, bool skipauth) diff --git a/ui/vnc.h b/ui/vnc.h index a7149831f9..a60fb13115 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -201,7 +201,7 @@ typedef struct VncTight { #ifdef CONFIG_VNC_JPEG Buffer jpeg; #endif -#ifdef CONFIG_VNC_PNG +#ifdef CONFIG_PNG Buffer png; #endif int levels[4]; diff --git a/util/aio-posix.c b/util/aio-posix.c index 7b9f629218..be0182a3c6 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -23,15 +23,6 @@ #include "trace.h" #include "aio-posix.h" -/* - * G_IO_IN and G_IO_OUT are not appropriate revents values for polling, since - * the handler may not need to access the file descriptor. For example, the - * handler doesn't need to read from an EventNotifier if it polled a memory - * location and a read syscall would be slow. Define our own unique revents - * value to indicate that polling determined this AioHandler is ready. - */ -#define REVENTS_POLL_READY 0 - /* Stop userspace polling on a handler if it isn't active for some time */ #define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND) @@ -49,6 +40,14 @@ void aio_add_ready_handler(AioHandlerList *ready_list, QLIST_INSERT_HEAD(ready_list, node, node_ready); } +static void aio_add_poll_ready_handler(AioHandlerList *ready_list, + AioHandler *node) +{ + QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ + node->poll_ready = true; + QLIST_INSERT_HEAD(ready_list, node, node_ready); +} + static AioHandler *find_aio_handler(AioContext *ctx, int fd) { AioHandler *node; @@ -76,6 +75,7 @@ static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node) } node->pfd.revents = 0; + node->poll_ready = false; /* If the fd monitor has already marked it deleted, leave it alone */ if (QLIST_IS_INSERTED(node, node_deleted)) { @@ -247,7 +247,7 @@ static bool poll_set_started(AioContext *ctx, AioHandlerList *ready_list, /* Poll one last time in case ->io_poll_end() raced with the event */ if (!started && node->io_poll(node->opaque)) { - aio_add_ready_handler(ready_list, node, REVENTS_POLL_READY); + aio_add_poll_ready_handler(ready_list, node); progress = true; } } @@ -282,6 +282,7 @@ bool aio_pending(AioContext *ctx) QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { int revents; + /* TODO should this check poll ready? */ revents = node->pfd.revents & node->pfd.events; if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read && aio_node_check(ctx, node->is_external)) { @@ -323,11 +324,15 @@ static void aio_free_deleted_handlers(AioContext *ctx) static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) { bool progress = false; + bool poll_ready; int revents; revents = node->pfd.revents & node->pfd.events; node->pfd.revents = 0; + poll_ready = node->poll_ready; + node->poll_ready = false; + /* * Start polling AioHandlers when they become ready because activity is * likely to continue. Note that starvation is theoretically possible when @@ -344,7 +349,7 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); } if (!QLIST_IS_INSERTED(node, node_deleted) && - revents == 0 && + poll_ready && revents == 0 && aio_node_check(ctx, node->is_external) && node->io_poll_ready) { node->io_poll_ready(node->opaque); @@ -432,7 +437,7 @@ static bool run_poll_handlers_once(AioContext *ctx, QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { if (aio_node_check(ctx, node->is_external) && node->io_poll(node->opaque)) { - aio_add_ready_handler(ready_list, node, REVENTS_POLL_READY); + aio_add_poll_ready_handler(ready_list, node); node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; @@ -491,8 +496,7 @@ static bool remove_idle_poll_handlers(AioContext *ctx, * this causes progress. */ if (node->io_poll(node->opaque)) { - aio_add_ready_handler(ready_list, node, - REVENTS_POLL_READY); + aio_add_poll_ready_handler(ready_list, node); progress = true; } } diff --git a/util/aio-posix.h b/util/aio-posix.h index 7f2c37a684..80b927c7f4 100644 --- a/util/aio-posix.h +++ b/util/aio-posix.h @@ -37,6 +37,7 @@ struct AioHandler { unsigned flags; /* see fdmon-io_uring.c */ #endif int64_t poll_idle_timeout; /* when to stop userspace polling */ + bool poll_ready; /* has polling detected an event? */ bool is_external; }; diff --git a/util/aio-win32.c b/util/aio-win32.c index 7aac89df3a..44003d645e 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -16,7 +16,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "block/block.h" #include "qemu/main-loop.h" #include "qemu/queue.h" diff --git a/util/async.c b/util/async.c index 08d25feef5..2ea1172f3e 100644 --- a/util/async.c +++ b/util/async.c @@ -32,6 +32,7 @@ #include "qemu/rcu_queue.h" #include "block/raw-aio.h" #include "qemu/coroutine_int.h" +#include "qemu/coroutine-tls.h" #include "trace.h" /***********************************************************/ @@ -675,12 +676,13 @@ void aio_context_release(AioContext *ctx) qemu_rec_mutex_unlock(&ctx->lock); } -static __thread AioContext *my_aiocontext; +QEMU_DEFINE_STATIC_CO_TLS(AioContext *, my_aiocontext) AioContext *qemu_get_current_aio_context(void) { - if (my_aiocontext) { - return my_aiocontext; + AioContext *ctx = get_my_aiocontext(); + if (ctx) { + return ctx; } if (qemu_mutex_iothread_locked()) { /* Possibly in a vCPU thread. */ @@ -691,6 +693,6 @@ AioContext *qemu_get_current_aio_context(void) void qemu_set_current_aio_context(AioContext *ctx) { - assert(!my_aiocontext); - my_aiocontext = ctx; + assert(!get_my_aiocontext()); + set_my_aiocontext(ctx); } diff --git a/util/atomic64.c b/util/atomic64.c index 22983a970f..c20d071d8e 100644 --- a/util/atomic64.c +++ b/util/atomic64.c @@ -8,6 +8,7 @@ #include "qemu/atomic.h" #include "qemu/thread.h" #include "qemu/cacheinfo.h" +#include "qemu/memalign.h" #ifdef CONFIG_ATOMIC64 #error This file must only be compiled if !CONFIG_ATOMIC64 diff --git a/util/bitmap.c b/util/bitmap.c index 1f201393ae..f81d8057a7 100644 --- a/util/bitmap.c +++ b/util/bitmap.c @@ -376,7 +376,7 @@ static void bitmap_to_from_le(unsigned long *dst, { long len = BITS_TO_LONGS(nbits); -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN long index; for (index = 0; index < len; index++) { diff --git a/util/compatfd.c b/util/compatfd.c index ab810c42a9..147e39e2c6 100644 --- a/util/compatfd.c +++ b/util/compatfd.c @@ -42,25 +42,11 @@ static void *sigwait_compat(void *opaque) } } else { struct qemu_signalfd_siginfo buffer; - size_t offset = 0; - memset(&buffer, 0, sizeof(buffer)); buffer.ssi_signo = sig; - while (offset < sizeof(buffer)) { - ssize_t len; - - len = write(info->fd, (char *)&buffer + offset, - sizeof(buffer) - offset); - if (len == -1 && errno == EINTR) { - continue; - } - - if (len <= 0) { - return NULL; - } - - offset += len; + if (qemu_write_full(info->fd, &buffer, sizeof(buffer)) != sizeof(buffer)) { + return NULL; } } } @@ -74,14 +60,11 @@ static int qemu_signalfd_compat(const sigset_t *mask) info = g_malloc(sizeof(*info)); - if (pipe(fds) == -1) { + if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { g_free(info); return -1; } - qemu_set_cloexec(fds[0]); - qemu_set_cloexec(fds[1]); - memcpy(&info->mask, mask, sizeof(*mask)); info->fd = fds[1]; diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c index e99b8a4f9c..e2690c5f41 100644 --- a/util/coroutine-sigaltstack.c +++ b/util/coroutine-sigaltstack.c @@ -27,7 +27,6 @@ #endif #include "qemu/osdep.h" #include -#include "qemu-common.h" #include "qemu/coroutine_int.h" #ifdef CONFIG_SAFESTACK diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c index 904b375192..ed368e1a3e 100644 --- a/util/coroutine-ucontext.c +++ b/util/coroutine-ucontext.c @@ -30,7 +30,7 @@ #include #endif -#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) +#ifdef QEMU_SANITIZE_ADDRESS #ifdef CONFIG_ASAN_IFACE_FIBER #define CONFIG_ASAN 1 #include diff --git a/util/coroutine-win32.c b/util/coroutine-win32.c index de6bd4fd3e..c196f956d2 100644 --- a/util/coroutine-win32.c +++ b/util/coroutine-win32.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/coroutine_int.h" typedef struct diff --git a/util/cutils.c b/util/cutils.c index c9b91e7535..b2777210e7 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -26,10 +26,6 @@ #include "qemu/host-utils.h" #include -#include "qemu-common.h" -#include "qemu/sockets.h" -#include "qemu/iov.h" -#include "net/net.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -148,77 +144,6 @@ time_t mktimegm(struct tm *tm) return t; } -/* - * Make sure data goes on disk, but if possible do not bother to - * write out the inode just for timestamp updates. - * - * Unfortunately even in 2009 many operating systems do not support - * fdatasync and have to fall back to fsync. - */ -int qemu_fdatasync(int fd) -{ -#ifdef CONFIG_FDATASYNC - return fdatasync(fd); -#else - return fsync(fd); -#endif -} - -/** - * Sync changes made to the memory mapped file back to the backing - * storage. For POSIX compliant systems this will fallback - * to regular msync call. Otherwise it will trigger whole file sync - * (including the metadata case there is no support to skip that otherwise) - * - * @addr - start of the memory area to be synced - * @length - length of the are to be synced - * @fd - file descriptor for the file to be synced - * (mandatory only for POSIX non-compliant systems) - */ -int qemu_msync(void *addr, size_t length, int fd) -{ -#ifdef CONFIG_POSIX - size_t align_mask = ~(qemu_real_host_page_size - 1); - - /** - * There are no strict reqs as per the length of mapping - * to be synced. Still the length needs to follow the address - * alignment changes. Additionally - round the size to the multiple - * of PAGE_SIZE - */ - length += ((uintptr_t)addr & (qemu_real_host_page_size - 1)); - length = (length + ~align_mask) & align_mask; - - addr = (void *)((uintptr_t)addr & align_mask); - - return msync(addr, length, MS_SYNC); -#else /* CONFIG_POSIX */ - /** - * Perform the sync based on the file descriptor - * The sync range will most probably be wider than the one - * requested - but it will still get the job done - */ - return qemu_fdatasync(fd); -#endif /* CONFIG_POSIX */ -} - -#ifndef _WIN32 -/* Sets a specific flag */ -int fcntl_setfl(int fd, int flag) -{ - int flags; - - flags = fcntl(fd, F_GETFL); - if (flags == -1) - return -errno; - - if (fcntl(fd, F_SETFL, flags | flag) == -1) - return -errno; - - return 0; -} -#endif - static int64_t suffix_mul(char suffix, int64_t unit) { switch (qemu_toupper(suffix)) { @@ -938,19 +863,6 @@ int parse_debug_env(const char *name, int max, int initial) return debug; } -/* - * Helper to print ethernet mac address - */ -const char *qemu_ether_ntoa(const MACAddr *mac) -{ - static char ret[18]; - - snprintf(ret, sizeof(ret), "%02x:%02x:%02x:%02x:%02x:%02x", - mac->a[0], mac->a[1], mac->a[2], mac->a[3], mac->a[4], mac->a[5]); - - return ret; -} - /* * Return human readable string for size @val. * @val can be anything that uint64_t allows (no more than "16 EiB"). diff --git a/util/envlist.c b/util/envlist.c index 2bcc13f094..ab5553498a 100644 --- a/util/envlist.c +++ b/util/envlist.c @@ -217,7 +217,7 @@ envlist_to_environ(const envlist_t *envlist, size_t *count) struct envlist_entry *entry; char **env, **penv; - penv = env = g_malloc((envlist->el_count + 1) * sizeof(char *)); + penv = env = g_new(char *, envlist->el_count + 1); for (entry = envlist->el_entries.lh_first; entry != NULL; entry = entry->ev_link.le_next) { diff --git a/util/qemu-error.c b/util/error-report.c similarity index 93% rename from util/qemu-error.c rename to util/error-report.c index 52a9e013c4..5edb2e6040 100644 --- a/util/qemu-error.c +++ b/util/error-report.c @@ -40,17 +40,6 @@ int error_printf(const char *fmt, ...) return ret; } -int error_printf_unless_qmp(const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = error_vprintf_unless_qmp(fmt, ap); - va_end(ap); - return ret; -} - static Location std_loc = { .kind = LOC_NONE }; @@ -146,22 +135,6 @@ void loc_set_file(const char *fname, int lno) } } -static const char *progname; - -/* - * Set the program name for error_print_loc(). - */ -static void error_set_progname(const char *argv0) -{ - const char *p = strrchr(argv0, '/'); - progname = p ? p + 1 : argv0; -} - -const char *error_get_progname(void) -{ - return progname; -} - /* * Print current location to current monitor if we have one, else to stderr. */ @@ -171,8 +144,8 @@ static void print_loc(void) int i; const char *const *argp; - if (!monitor_cur() && progname) { - fprintf(stderr, "%s:", progname); + if (!monitor_cur() && g_get_prgname()) { + error_printf("%s:", g_get_prgname()); sep = " "; } switch (cur_loc->kind) { @@ -196,6 +169,23 @@ static void print_loc(void) } } +static char * +real_time_iso8601(void) +{ +#if GLIB_CHECK_VERSION(2,62,0) + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); + /* ignore deprecation warning, since GLIB_VERSION_MAX_ALLOWED is 2.56 */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + return g_date_time_format_iso8601(dt); +#pragma GCC diagnostic pop +#else + GTimeVal tv; + g_get_current_time(&tv); + return g_time_val_to_iso8601(&tv); +#endif +} + /* * Print a message to current monitor if we have one, else to stderr. * @report_type is the type of message: error, warning or informational. @@ -205,12 +195,10 @@ static void print_loc(void) */ static void vreport(report_type type, const char *fmt, va_list ap) { - GTimeVal tv; gchar *timestr; if (message_with_timestamp && !monitor_cur()) { - g_get_current_time(&tv); - timestr = g_time_val_to_iso8601(&tv); + timestr = real_time_iso8601(); error_printf("%s ", timestr); g_free(timestr); } @@ -400,8 +388,10 @@ static void qemu_log_func(const gchar *log_domain, void error_init(const char *argv0) { + const char *p = strrchr(argv0, '/'); + /* Set the program name for error_print_loc(). */ - error_set_progname(argv0); + g_set_prgname(p ? p + 1 : argv0); /* * This sets up glib logging so libraries using it also print their logs diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c index 8307013c5d..76420c5b56 100644 --- a/util/event_notifier-posix.c +++ b/util/event_notifier-posix.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" @@ -50,16 +49,14 @@ int event_notifier_init(EventNotifier *e, int active) if (errno != ENOSYS) { return -errno; } - if (qemu_pipe(fds) < 0) { + if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { return -errno; } - ret = fcntl_setfl(fds[0], O_NONBLOCK); - if (ret < 0) { + if (!g_unix_set_fd_nonblocking(fds[0], true, NULL)) { ret = -errno; goto fail; } - ret = fcntl_setfl(fds[1], O_NONBLOCK); - if (ret < 0) { + if (!g_unix_set_fd_nonblocking(fds[1], true, NULL)) { ret = -errno; goto fail; } @@ -99,6 +96,11 @@ int event_notifier_get_fd(const EventNotifier *e) return e->rfd; } +int event_notifier_get_wfd(const EventNotifier *e) +{ + return e->wfd; +} + int event_notifier_set(EventNotifier *e) { static const uint64_t value = 1; diff --git a/util/event_notifier-win32.c b/util/event_notifier-win32.c index 62c53b0a99..9352da4699 100644 --- a/util/event_notifier-win32.c +++ b/util/event_notifier-win32.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" diff --git a/util/fdmon-io_uring.c b/util/fdmon-io_uring.c index 1461dfa407..ab43052dd7 100644 --- a/util/fdmon-io_uring.c +++ b/util/fdmon-io_uring.c @@ -179,7 +179,11 @@ static void add_poll_remove_sqe(AioContext *ctx, AioHandler *node) { struct io_uring_sqe *sqe = get_sqe(ctx); +#ifdef LIBURING_HAVE_DATA64 + io_uring_prep_poll_remove(sqe, (__u64)(uintptr_t)node); +#else io_uring_prep_poll_remove(sqe, node); +#endif } /* Add a timeout that self-cancels when another cqe becomes ready */ diff --git a/util/hbitmap.c b/util/hbitmap.c index 305b894a63..ea989e1f0e 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -301,6 +301,39 @@ bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, return true; } +bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count, + int64_t *pnum) +{ + int64_t next_dirty, next_zero; + + assert(start >= 0); + assert(count > 0); + assert(start + count <= hb->orig_size); + + next_dirty = hbitmap_next_dirty(hb, start, count); + if (next_dirty == -1) { + *pnum = count; + return false; + } + + if (next_dirty > start) { + *pnum = next_dirty - start; + return false; + } + + assert(next_dirty == start); + + next_zero = hbitmap_next_zero(hb, start, count); + if (next_zero == -1) { + *pnum = count; + return true; + } + + assert(next_zero > start); + *pnum = next_zero - start; + return false; +} + bool hbitmap_empty(const HBitmap *hb) { return hb->count == 0; @@ -829,7 +862,7 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) } old = hb->sizes[i]; hb->sizes[i] = size; - hb->levels[i] = g_realloc(hb->levels[i], size * sizeof(unsigned long)); + hb->levels[i] = g_renew(unsigned long, hb->levels[i], size); if (!shrink) { memset(&hb->levels[i][old], 0x00, (size - old) * sizeof(*hb->levels[i])); diff --git a/util/hexdump.c b/util/hexdump.c index 2c105a8846..9921114b3c 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -14,7 +14,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/cutils.h" void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, unsigned int len, bool ascii) diff --git a/util/host-utils.c b/util/host-utils.c index bcc772b8ec..96d5dc0bed 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -34,7 +34,7 @@ static inline void mul64(uint64_t *plow, uint64_t *phigh, typedef union { uint64_t ll; struct { -#ifdef HOST_WORDS_BIGENDIAN +#if HOST_BIG_ENDIAN uint32_t high, low; #else uint32_t low, high; diff --git a/util/iov.c b/util/iov.c index 58c7b3eeee..22d6996cce 100644 --- a/util/iov.c +++ b/util/iov.c @@ -17,7 +17,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/cutils.h" diff --git a/util/iova-tree.c b/util/iova-tree.c index 23ea35b7a4..6dff29c1f6 100644 --- a/util/iova-tree.c +++ b/util/iova-tree.c @@ -16,6 +16,45 @@ struct IOVATree { GTree *tree; }; +/* Args to pass to iova_tree_alloc foreach function. */ +struct IOVATreeAllocArgs { + /* Size of the desired allocation */ + size_t new_size; + + /* The minimum address allowed in the allocation */ + hwaddr iova_begin; + + /* Map at the left of the hole, can be NULL if "this" is first one */ + const DMAMap *prev; + + /* Map at the right of the hole, can be NULL if "prev" is the last one */ + const DMAMap *this; + + /* If found, we fill in the IOVA here */ + hwaddr iova_result; + + /* Whether have we found a valid IOVA */ + bool iova_found; +}; + +typedef struct IOVATreeFindIOVAArgs { + const DMAMap *needle; + const DMAMap *result; +} IOVATreeFindIOVAArgs; + +/** + * Iterate args to the next hole + * + * @args: The alloc arguments + * @next: The next mapping in the tree. Can be NULL to signal the last one + */ +static void iova_tree_alloc_args_iterate(struct IOVATreeAllocArgs *args, + const DMAMap *next) +{ + args->prev = args->this; + args->this = next; +} + static int iova_tree_compare(gconstpointer a, gconstpointer b, gpointer data) { const DMAMap *m1 = a, *m2 = b; @@ -47,6 +86,35 @@ const DMAMap *iova_tree_find(const IOVATree *tree, const DMAMap *map) return g_tree_lookup(tree->tree, map); } +static gboolean iova_tree_find_address_iterator(gpointer key, gpointer value, + gpointer data) +{ + const DMAMap *map = key; + IOVATreeFindIOVAArgs *args = data; + const DMAMap *needle; + + g_assert(key == value); + + needle = args->needle; + if (map->translated_addr + map->size < needle->translated_addr || + needle->translated_addr + needle->size < map->translated_addr) { + return false; + } + + args->result = map; + return true; +} + +const DMAMap *iova_tree_find_iova(const IOVATree *tree, const DMAMap *map) +{ + IOVATreeFindIOVAArgs args = { + .needle = map, + }; + + g_tree_foreach(tree->tree, iova_tree_find_address_iterator, &args); + return args.result; +} + const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova) { const DMAMap map = { .iova = iova, .size = 0 }; @@ -107,6 +175,108 @@ int iova_tree_remove(IOVATree *tree, const DMAMap *map) return IOVA_OK; } +/** + * Try to find an unallocated IOVA range between prev and this elements. + * + * @args: Arguments to allocation + * + * Cases: + * + * (1) !prev, !this: No entries allocated, always succeed + * + * (2) !prev, this: We're iterating at the 1st element. + * + * (3) prev, !this: We're iterating at the last element. + * + * (4) prev, this: this is the most common case, we'll try to find a hole + * between "prev" and "this" mapping. + * + * Note that this function assumes the last valid iova is HWADDR_MAX, but it + * searches linearly so it's easy to discard the result if it's not the case. + */ +static void iova_tree_alloc_map_in_hole(struct IOVATreeAllocArgs *args) +{ + const DMAMap *prev = args->prev, *this = args->this; + uint64_t hole_start, hole_last; + + if (this && this->iova + this->size < args->iova_begin) { + return; + } + + hole_start = MAX(prev ? prev->iova + prev->size + 1 : 0, args->iova_begin); + hole_last = this ? this->iova : HWADDR_MAX; + + if (hole_last - hole_start > args->new_size) { + args->iova_result = hole_start; + args->iova_found = true; + } +} + +/** + * Foreach dma node in the tree, compare if there is a hole with its previous + * node (or minimum iova address allowed) and the node. + * + * @key: Node iterating + * @value: Node iterating + * @pargs: Struct to communicate with the outside world + * + * Return: false to keep iterating, true if needs break. + */ +static gboolean iova_tree_alloc_traverse(gpointer key, gpointer value, + gpointer pargs) +{ + struct IOVATreeAllocArgs *args = pargs; + DMAMap *node = value; + + assert(key == value); + + iova_tree_alloc_args_iterate(args, node); + iova_tree_alloc_map_in_hole(args); + return args->iova_found; +} + +int iova_tree_alloc_map(IOVATree *tree, DMAMap *map, hwaddr iova_begin, + hwaddr iova_last) +{ + struct IOVATreeAllocArgs args = { + .new_size = map->size, + .iova_begin = iova_begin, + }; + + if (unlikely(iova_last < iova_begin)) { + return IOVA_ERR_INVALID; + } + + /* + * Find a valid hole for the mapping + * + * Assuming low iova_begin, so no need to do a binary search to + * locate the first node. + * + * TODO: Replace all this with g_tree_node_first/next/last when available + * (from glib since 2.68). To do it with g_tree_foreach complicates the + * code a lot. + * + */ + g_tree_foreach(tree->tree, iova_tree_alloc_traverse, &args); + if (!args.iova_found) { + /* + * Either tree is empty or the last hole is still not checked. + * g_tree_foreach does not compare (last, iova_last] range, so we check + * it here. + */ + iova_tree_alloc_args_iterate(&args, NULL); + iova_tree_alloc_map_in_hole(&args); + } + + if (!args.iova_found || args.iova_result + map->size > iova_last) { + return IOVA_ERR_NOMEM; + } + + map->iova = args.iova_result; + return iova_tree_insert(tree, map); +} + void iova_tree_destroy(IOVATree *tree) { g_tree_destroy(tree->tree); diff --git a/util/keyval.c b/util/keyval.c index 904337c8a1..66a5b4740f 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -16,7 +16,9 @@ * key-vals = [ key-val { ',' key-val } [ ',' ] ] * key-val = key '=' val | help * key = key-fragment { '.' key-fragment } - * key-fragment = / [^=,.]+ / + * key-fragment = qapi-name | index + * qapi-name = '__' / [a-z0-9.-]+ / '_' / [A-Za-z][A-Za-z0-9_-]* / + * index = / [0-9]+ / * val = { / [^,]+ / | ',,' } * help = 'help' | '?' * @@ -93,8 +95,8 @@ #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" #include "qemu/cutils.h" +#include "qemu/keyval.h" #include "qemu/help_option.h" -#include "qemu/option.h" /* * Convert @key to a list index. diff --git a/util/log.c b/util/log.c index 2ee1500bee..d6eb0378c3 100644 --- a/util/log.c +++ b/util/log.c @@ -26,153 +26,290 @@ #include "trace/control.h" #include "qemu/thread.h" #include "qemu/lockable.h" +#include "qemu/rcu.h" +#ifdef CONFIG_LINUX +#include +#endif + + +typedef struct RCUCloseFILE { + struct rcu_head rcu; + FILE *fd; +} RCUCloseFILE; + +/* Mutex covering the other global_* variables. */ +static QemuMutex global_mutex; +static char *global_filename; +static FILE *global_file; +static __thread FILE *thread_file; -static char *logfilename; -static QemuMutex qemu_logfile_mutex; -QemuLogFile *qemu_logfile; int qemu_loglevel; -static int log_append = 0; +static bool log_append; +static bool log_per_thread; static GArray *debug_regions; -/* Return the number of characters emitted. */ -int qemu_log(const char *fmt, ...) +/* Returns true if qemu_log() will really write somewhere. */ +bool qemu_log_enabled(void) { - int ret = 0; - QemuLogFile *logfile; + return log_per_thread || qatomic_read(&global_file) != NULL; +} - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - va_list ap; - va_start(ap, fmt); - ret = vfprintf(logfile->fd, fmt, ap); - va_end(ap); +/* Returns true if qemu_log() will write somewhere other than stderr. */ +bool qemu_log_separate(void) +{ + if (log_per_thread) { + return true; + } else { + FILE *logfile = qatomic_read(&global_file); + return logfile && logfile != stderr; + } +} - /* Don't pass back error results. */ - if (ret < 0) { - ret = 0; +static int log_thread_id(void) +{ +#ifdef CONFIG_GETTID + return gettid(); +#elif defined(SYS_gettid) + return syscall(SYS_gettid); +#else + static int counter; + return qatomic_fetch_inc(&counter); +#endif +} + +/* Lock/unlock output. */ + +FILE *qemu_log_trylock(void) +{ + FILE *logfile; + + logfile = thread_file; + if (!logfile) { + if (log_per_thread) { + g_autofree char *filename + = g_strdup_printf(global_filename, log_thread_id()); + logfile = fopen(filename, "w"); + if (!logfile) { + return NULL; + } + thread_file = logfile; + } else { + rcu_read_lock(); + /* + * FIXME: typeof_strip_qual, as used by qatomic_rcu_read, + * does not work with pointers to undefined structures, + * such as we have with struct _IO_FILE and musl libc. + * Since all we want is a read of a pointer, cast to void**, + * which does work with typeof_strip_qual. + */ + logfile = qatomic_rcu_read((void **)&global_file); + if (!logfile) { + rcu_read_unlock(); + return NULL; + } } } - rcu_read_unlock(); - return ret; + + qemu_flockfile(logfile); + return logfile; } -static void __attribute__((__constructor__)) qemu_logfile_init(void) +void qemu_log_unlock(FILE *logfile) { - qemu_mutex_init(&qemu_logfile_mutex); -} - -static void qemu_logfile_free(QemuLogFile *logfile) -{ - g_assert(logfile); - - if (logfile->fd != stderr) { - fclose(logfile->fd); + if (logfile) { + fflush(logfile); + qemu_funlockfile(logfile); + if (!log_per_thread) { + rcu_read_unlock(); + } } - g_free(logfile); } -static bool log_uses_own_buffers; +void qemu_log(const char *fmt, ...) +{ + FILE *f = qemu_log_trylock(); + if (f) { + va_list ap; + + va_start(ap, fmt); + vfprintf(f, fmt, ap); + va_end(ap); + qemu_log_unlock(f); + } +} + +static void __attribute__((__constructor__)) startup(void) +{ + qemu_mutex_init(&global_mutex); +} + +static void rcu_close_file(RCUCloseFILE *r) +{ + fclose(r->fd); + g_free(r); +} + +/** + * valid_filename_template: + * + * Validate the filename template. Require %d if per_thread, allow it + * otherwise; require no other % within the template. + */ + +typedef enum { + vft_error, + vft_stderr, + vft_strdup, + vft_pid_printf, +} ValidFilenameTemplateResult; + +static ValidFilenameTemplateResult +valid_filename_template(const char *filename, bool per_thread, Error **errp) +{ + if (filename) { + char *pidstr = strstr(filename, "%"); + + if (pidstr) { + /* We only accept one %d, no other format strings */ + if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) { + error_setg(errp, "Bad logfile template: %s", filename); + return 0; + } + return per_thread ? vft_strdup : vft_pid_printf; + } + } + if (per_thread) { + error_setg(errp, "Filename template with '%%d' required for 'tid'"); + return vft_error; + } + return filename ? vft_strdup : vft_stderr; +} /* enable or disable low levels log */ -void qemu_set_log(int log_flags) +static bool qemu_set_log_internal(const char *filename, bool changed_name, + int log_flags, Error **errp) { - bool need_to_open_file = false; - QemuLogFile *logfile; + bool need_to_open_file; + bool daemonized; + bool per_thread; + FILE *logfile; - qemu_loglevel = log_flags; + QEMU_LOCK_GUARD(&global_mutex); + logfile = global_file; + + per_thread = log_flags & LOG_PER_THREAD; + + if (changed_name) { + char *newname = NULL; + + /* + * Once threads start opening their own log files, we have no + * easy mechanism to tell them all to close and re-open. + * There seems little cause to do so either -- this option + * will most often be used at user-only startup. + */ + if (log_per_thread) { + error_setg(errp, "Cannot change log filename after setting 'tid'"); + return false; + } + + switch (valid_filename_template(filename, per_thread, errp)) { + case vft_error: + return false; + case vft_stderr: + break; + case vft_strdup: + newname = g_strdup(filename); + break; + case vft_pid_printf: + newname = g_strdup_printf(filename, getpid()); + break; + } + + g_free(global_filename); + global_filename = newname; + filename = newname; + } else { + filename = global_filename; + if (per_thread && + valid_filename_template(filename, true, errp) == vft_error) { + return false; + } + } + + /* Once the per-thread flag is set, it cannot be unset. */ + if (per_thread) { + log_per_thread = true; + } + /* The flag itself is not relevant for need_to_open_file. */ + log_flags &= ~LOG_PER_THREAD; #ifdef CONFIG_TRACE_LOG - qemu_loglevel |= LOG_TRACE; + log_flags |= LOG_TRACE; #endif + qemu_loglevel = log_flags; + /* * In all cases we only log if qemu_loglevel is set. * Also: + * If per-thread, open the file for each thread in qemu_log_lock. * If not daemonized we will always log either to stderr - * or to a file (if there is a logfilename). - * If we are daemonized, - * we will only log if there is a logfilename. + * or to a file (if there is a filename). + * If we are daemonized, we will only log if there is a filename. */ - if (qemu_loglevel && (!is_daemonized() || logfilename)) { - need_to_open_file = true; + daemonized = is_daemonized(); + need_to_open_file = log_flags && !per_thread && (!daemonized || filename); + + if (logfile && (!need_to_open_file || changed_name)) { + qatomic_rcu_set(&global_file, NULL); + if (logfile != stderr) { + RCUCloseFILE *r = g_new0(RCUCloseFILE, 1); + r->fd = logfile; + call_rcu(r, rcu_close_file, rcu); + } + logfile = NULL; } - QEMU_LOCK_GUARD(&qemu_logfile_mutex); - if (qemu_logfile && !need_to_open_file) { - logfile = qemu_logfile; - qatomic_rcu_set(&qemu_logfile, NULL); - call_rcu(logfile, qemu_logfile_free, rcu); - } else if (!qemu_logfile && need_to_open_file) { - logfile = g_new0(QemuLogFile, 1); - if (logfilename) { - logfile->fd = fopen(logfilename, log_append ? "a" : "w"); - if (!logfile->fd) { - g_free(logfile); - perror(logfilename); - _exit(1); + + if (!logfile && need_to_open_file) { + if (filename) { + logfile = fopen(filename, log_append ? "a" : "w"); + if (!logfile) { + error_setg_errno(errp, errno, "Error opening logfile %s", + filename); + return false; } /* In case we are a daemon redirect stderr to logfile */ - if (is_daemonized()) { - dup2(fileno(logfile->fd), STDERR_FILENO); - fclose(logfile->fd); - /* This will skip closing logfile in qemu_log_close() */ - logfile->fd = stderr; + if (daemonized) { + dup2(fileno(logfile), STDERR_FILENO); + fclose(logfile); + /* This will skip closing logfile in rcu_close_file. */ + logfile = stderr; } } else { /* Default to stderr if no log file specified */ - assert(!is_daemonized()); - logfile->fd = stderr; + assert(!daemonized); + logfile = stderr; } - /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ - if (log_uses_own_buffers) { - static char logfile_buf[4096]; - setvbuf(logfile->fd, logfile_buf, _IOLBF, sizeof(logfile_buf)); - } else { -#if defined(_WIN32) - /* Win32 doesn't support line-buffering, so use unbuffered output. */ - setvbuf(logfile->fd, NULL, _IONBF, 0); -#else - setvbuf(logfile->fd, NULL, _IOLBF, 0); -#endif - log_append = 1; - } - qatomic_rcu_set(&qemu_logfile, logfile); + log_append = 1; + + qatomic_rcu_set(&global_file, logfile); } + return true; } -void qemu_log_needs_buffers(void) +bool qemu_set_log(int log_flags, Error **errp) { - log_uses_own_buffers = true; + return qemu_set_log_internal(NULL, false, log_flags, errp); } -/* - * Allow the user to include %d in their logfile which will be - * substituted with the current PID. This is useful for debugging many - * nested linux-user tasks but will result in lots of logs. - * - * filename may be NULL. In that case, log output is sent to stderr - */ -void qemu_set_log_filename(const char *filename, Error **errp) +bool qemu_set_log_filename(const char *filename, Error **errp) { - g_free(logfilename); - logfilename = NULL; + return qemu_set_log_internal(filename, true, qemu_loglevel, errp); +} - if (filename) { - char *pidstr = strstr(filename, "%"); - if (pidstr) { - /* We only accept one %d, no other format strings */ - if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) { - error_setg(errp, "Bad logfile format: %s", filename); - return; - } else { - logfilename = g_strdup_printf(filename, getpid()); - } - } else { - logfilename = g_strdup(filename); - } - } - - qemu_log_close(); - qemu_set_log(qemu_loglevel); +bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp) +{ + return qemu_set_log_internal(name, true, flags, errp); } /* Returns true if addr is in our debug filter or no filter defined @@ -266,34 +403,6 @@ out: g_strfreev(ranges); } -/* fflush() the log file */ -void qemu_log_flush(void) -{ - QemuLogFile *logfile; - - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - if (logfile) { - fflush(logfile->fd); - } - rcu_read_unlock(); -} - -/* Close the log file */ -void qemu_log_close(void) -{ - QemuLogFile *logfile; - - qemu_mutex_lock(&qemu_logfile_mutex); - logfile = qemu_logfile; - - if (logfile) { - qatomic_rcu_set(&qemu_logfile, NULL); - call_rcu(logfile, qemu_logfile_free, rcu); - } - qemu_mutex_unlock(&qemu_logfile_mutex); -} - const QEMULogItem qemu_log_items[] = { { CPU_LOG_TB_OUT_ASM, "out_asm", "show generated host assembly code for each compiled TB" }, @@ -334,6 +443,8 @@ const QEMULogItem qemu_log_items[] = { #endif { LOG_STRACE, "strace", "log every user-mode syscall, its input, and its result" }, + { LOG_PER_THREAD, "tid", + "open a separate log file per thread; filename must contain '%d'" }, { 0, NULL, NULL }, }; diff --git a/util/main-loop.c b/util/main-loop.c index 4d5a5b9943..9afac10dff 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -114,7 +114,7 @@ static int qemu_signal_init(Error **errp) return -errno; } - fcntl_setfl(sigfd, O_NONBLOCK); + g_unix_set_fd_nonblocking(sigfd, true, NULL); qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); @@ -273,7 +273,7 @@ static PollingEntry *first_polling_entry; int qemu_add_polling_cb(PollingFunc *func, void *opaque) { PollingEntry **ppe, *pe; - pe = g_malloc0(sizeof(PollingEntry)); + pe = g_new0(PollingEntry, 1); pe->func = func; pe->opaque = opaque; for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); diff --git a/util/memalign.c b/util/memalign.c new file mode 100644 index 0000000000..c199ae7073 --- /dev/null +++ b/util/memalign.c @@ -0,0 +1,92 @@ +/* + * memalign.c: Allocate an aligned memory region + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010-2016 Red Hat, Inc. + * Copyright (c) 2022 Linaro Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "qemu/memalign.h" +#include "trace.h" + +void *qemu_try_memalign(size_t alignment, size_t size) +{ + void *ptr; + + if (alignment < sizeof(void*)) { + alignment = sizeof(void*); + } else { + g_assert(is_power_of_2(alignment)); + } + + /* + * Handling of 0 allocations varies among the different + * platform APIs (for instance _aligned_malloc() will + * fail) -- ensure that we always return a valid non-NULL + * pointer that can be freed by qemu_vfree(). + */ + if (size == 0) { + size++; + } +#if defined(CONFIG_POSIX_MEMALIGN) + int ret; + ret = posix_memalign(&ptr, alignment, size); + if (ret != 0) { + errno = ret; + ptr = NULL; + } +#elif defined(CONFIG_ALIGNED_MALLOC) + ptr = _aligned_malloc(size, alignment); +#elif defined(CONFIG_VALLOC) + ptr = valloc(size); +#elif defined(CONFIG_MEMALIGN) + ptr = memalign(alignment, size); +#else + #error No function to allocate aligned memory available +#endif + trace_qemu_memalign(alignment, size, ptr); + return ptr; +} + +void *qemu_memalign(size_t alignment, size_t size) +{ + void *p = qemu_try_memalign(alignment, size); + if (p) { + return p; + } + fprintf(stderr, + "qemu_memalign: failed to allocate %zu bytes at alignment %zu: %s\n", + size, alignment, strerror(errno)); + abort(); +} + +void qemu_vfree(void *ptr) +{ + trace_qemu_vfree(ptr); +#if !defined(CONFIG_POSIX_MEMALIGN) && defined(CONFIG_ALIGNED_MALLOC) + /* Only Windows _aligned_malloc needs a special free function */ + _aligned_free(ptr); +#else + free(ptr); +#endif +} diff --git a/util/meson.build b/util/meson.build index 3736988b9f..8f16018cd4 100644 --- a/util/meson.build +++ b/util/meson.build @@ -11,8 +11,11 @@ util_ss.add(when: linux_io_uring, if_true: files('fdmon-io_uring.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('compatfd.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('event_notifier-posix.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('mmap-alloc.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('oslib-posix.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: [files('qemu-openpty.c'), util]) +freebsd_dep = [] +if targetos == 'freebsd' + freebsd_dep = util +endif +util_ss.add(when: 'CONFIG_POSIX', if_true: [files('oslib-posix.c'), freebsd_dep]) util_ss.add(when: 'CONFIG_POSIX', if_true: files('qemu-thread-posix.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('memfd.c')) util_ss.add(when: 'CONFIG_WIN32', if_true: files('aio-win32.c')) @@ -25,7 +28,7 @@ util_ss.add(files('host-utils.c')) util_ss.add(files('bitmap.c', 'bitops.c')) util_ss.add(files('fifo8.c')) util_ss.add(files('cacheinfo.c', 'cacheflush.c')) -util_ss.add(files('error.c', 'qemu-error.c')) +util_ss.add(files('error.c', 'error-report.c')) util_ss.add(files('qemu-print.c')) util_ss.add(files('id.c')) util_ss.add(files('qemu-config.c', 'notify.c')) @@ -39,7 +42,6 @@ if have_membarrier util_ss.add(files('sys_membarrier.c')) endif util_ss.add(files('log.c')) -util_ss.add(files('pagesize.c')) util_ss.add(files('qdist.c')) util_ss.add(files('qht.c')) util_ss.add(files('qsp.c')) @@ -51,6 +53,7 @@ util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c')) util_ss.add(files('guest-random.c')) util_ss.add(files('yank.c')) util_ss.add(files('int128.c')) +util_ss.add(files('memalign.c')) if have_user util_ss.add(files('selfmap.c')) @@ -58,7 +61,7 @@ endif if have_system util_ss.add(files('crc-ccitt.c')) - util_ss.add(when: 'CONFIG_GIO', if_true: [files('dbus.c'), gio]) + util_ss.add(when: gio, if_true: files('dbus.c')) util_ss.add(when: 'CONFIG_LINUX', if_true: files('userfaultfd.c')) endif diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 893d864354..5b90cb68ea 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -50,7 +50,7 @@ size_t qemu_fd_getpagesize(int fd) #endif #endif - return qemu_real_host_page_size; + return qemu_real_host_page_size(); } size_t qemu_mempath_getpagesize(const char *mem_path) @@ -81,7 +81,7 @@ size_t qemu_mempath_getpagesize(const char *mem_path) #endif #endif - return qemu_real_host_page_size; + return qemu_real_host_page_size(); } #define OVERCOMMIT_MEMORY_PATH "/proc/sys/vm/overcommit_memory" @@ -101,7 +101,7 @@ static bool map_noreserve_effective(int fd, uint32_t qemu_map_flags) * MAP_NORESERVE. * b) MAP_NORESERVE is not affected by /proc/sys/vm/overcommit_memory. */ - if (qemu_fd_getpagesize(fd) != qemu_real_host_page_size) { + if (qemu_fd_getpagesize(fd) != qemu_real_host_page_size()) { return true; } @@ -166,7 +166,7 @@ static void *mmap_reserve(size_t size, int fd) * We do this unless we are using the system page size, in which case * anonymous memory is OK. */ - if (fd == -1 || qemu_fd_getpagesize(fd) == qemu_real_host_page_size) { + if (fd == -1 || qemu_fd_getpagesize(fd) == qemu_real_host_page_size()) { fd = -1; flags |= MAP_ANONYMOUS; } else { @@ -243,7 +243,7 @@ static inline size_t mmap_guard_pagesize(int fd) /* Mappings in the same segment must share the same page size */ return qemu_fd_getpagesize(fd); #else - return qemu_real_host_page_size; + return qemu_real_host_page_size(); #endif } diff --git a/util/osdep.c b/util/osdep.c index 723cdcb004..60fcbbaebe 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -23,18 +23,6 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" - -/* Needed early for CONFIG_BSD etc. */ - -#ifdef CONFIG_SOLARIS -#include -/* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for - discussion about Solaris header problems */ -extern int madvise(char *, size_t, int); -#endif - -#include -#include "qemu-common.h" #include "qemu/cutils.h" #include "qemu/sockets.h" #include "qemu/error-report.h" @@ -43,14 +31,12 @@ extern int madvise(char *, size_t, int); #include "qemu/hw-version.h" #include "monitor/monitor.h" -static bool fips_enabled = false; - static const char *hw_version = QEMU_HW_VERSION; int socket_set_cork(int fd, int v) { #if defined(SOL_TCP) && defined(TCP_CORK) - return qemu_setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v)); + return setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v)); #else return 0; #endif @@ -59,7 +45,7 @@ int socket_set_cork(int fd, int v) int socket_set_nodelay(int fd) { int v = 1; - return qemu_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); } int qemu_madvise(void *addr, size_t len, int advice) @@ -80,8 +66,8 @@ int qemu_madvise(void *addr, size_t len, int advice) static int qemu_mprotect__osdep(void *addr, size_t size, int prot) { - g_assert(!((uintptr_t)addr & ~qemu_real_host_page_mask)); - g_assert(!(size & ~qemu_real_host_page_mask)); + g_assert(!((uintptr_t)addr & ~qemu_real_host_page_mask())); + g_assert(!(size & ~qemu_real_host_page_mask())); #ifdef _WIN32 DWORD old_protect; @@ -526,32 +512,6 @@ const char *qemu_hw_version(void) return hw_version; } -void fips_set_state(bool requested) -{ -#ifdef __linux__ - if (requested) { - FILE *fds = fopen("/proc/sys/crypto/fips_enabled", "r"); - if (fds != NULL) { - fips_enabled = (fgetc(fds) == '1'); - fclose(fds); - } - } -#else - fips_enabled = false; -#endif /* __linux__ */ - -#ifdef _FIPS_DEBUG - fprintf(stderr, "FIPS mode %s (requested %s)\n", - (fips_enabled ? "enabled" : "disabled"), - (requested ? "enabled" : "disabled")); -#endif -} - -bool fips_get_state(void) -{ - return fips_enabled; -} - #ifdef _WIN32 static void socket_cleanup(void) { @@ -620,22 +580,18 @@ writev(int fd, const struct iovec *iov, int iov_cnt) } #endif -struct dirent * -qemu_dirent_dup(struct dirent *dent) +/* + * Make sure data goes on disk, but if possible do not bother to + * write out the inode just for timestamp updates. + * + * Unfortunately even in 2009 many operating systems do not support + * fdatasync and have to fall back to fsync. + */ +int qemu_fdatasync(int fd) { - size_t sz = 0; -#if defined _DIRENT_HAVE_D_RECLEN - /* Avoid use of strlen() if platform supports d_reclen. */ - sz = dent->d_reclen; +#ifdef CONFIG_FDATASYNC + return fdatasync(fd); +#else + return fsync(fd); #endif - /* - * Test sz for zero even if d_reclen is available - * because some drivers may set d_reclen to zero. - */ - if (sz == 0) { - /* Fallback to the most portable way. */ - sz = offsetof(struct dirent, d_name) + - strlen(dent->d_name) + 1; - } - return g_memdup(dent, sz); } diff --git a/util/oslib-posix.c b/util/oslib-posix.c index f2be7321c5..477990f39b 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -31,7 +31,6 @@ #include -#include "qemu-common.h" #include "sysemu/sysemu.h" #include "trace.h" #include "qapi/error.h" @@ -139,9 +138,8 @@ bool qemu_write_pidfile(const char *path, Error **errp) .l_len = 0, }; - fd = qemu_open_old(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + fd = qemu_create(path, O_WRONLY, S_IRUSR | S_IWUSR, errp); if (fd == -1) { - error_setg_errno(errp, errno, "Cannot open pid file"); return false; } @@ -185,7 +183,7 @@ bool qemu_write_pidfile(const char *path, Error **errp) } snprintf(pidstr, sizeof(pidstr), FMT_pid "\n", getpid()); - if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) { + if (qemu_write_full(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) { error_setg(errp, "Failed to write pid file"); goto fail_unlink; } @@ -199,46 +197,6 @@ fail_close: return false; } -void *qemu_oom_check(void *ptr) -{ - if (ptr == NULL) { - fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno)); - abort(); - } - return ptr; -} - -void *qemu_try_memalign(size_t alignment, size_t size) -{ - void *ptr; - - if (alignment < sizeof(void*)) { - alignment = sizeof(void*); - } else { - g_assert(is_power_of_2(alignment)); - } - -#if defined(CONFIG_POSIX_MEMALIGN) - int ret; - ret = posix_memalign(&ptr, alignment, size); - if (ret != 0) { - errno = ret; - ptr = NULL; - } -#elif defined(CONFIG_BSD) - ptr = valloc(size); -#else - ptr = memalign(alignment, size); -#endif - trace_qemu_memalign(alignment, size, ptr); - return ptr; -} - -void *qemu_memalign(size_t alignment, size_t size) -{ - return qemu_oom_check(qemu_try_memalign(alignment, size)); -} - /* alloc shared memory pages */ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, bool noreserve) @@ -260,44 +218,26 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, return ptr; } -void qemu_vfree(void *ptr) -{ - trace_qemu_vfree(ptr); - free(ptr); -} - void qemu_anon_ram_free(void *ptr, size_t size) { trace_qemu_anon_ram_free(ptr, size); qemu_ram_munmap(-1, ptr, size); } -void qemu_set_block(int fd) +void qemu_socket_set_block(int fd) { - int f; - f = fcntl(fd, F_GETFL); - assert(f != -1); - f = fcntl(fd, F_SETFL, f & ~O_NONBLOCK); - assert(f != -1); + g_unix_set_fd_nonblocking(fd, false, NULL); } -int qemu_try_set_nonblock(int fd) +int qemu_socket_try_set_nonblock(int fd) { - int f; - f = fcntl(fd, F_GETFL); - if (f == -1) { - return -errno; - } - if (fcntl(fd, F_SETFL, f | O_NONBLOCK) == -1) { - return -errno; - } - return 0; + return g_unix_set_fd_nonblocking(fd, true, NULL) ? 0 : -errno; } -void qemu_set_nonblock(int fd) +void qemu_socket_set_nonblock(int fd) { int f; - f = qemu_try_set_nonblock(fd); + f = qemu_socket_try_set_nonblock(fd); assert(f == 0); } @@ -322,35 +262,10 @@ void qemu_set_cloexec(int fd) assert(f != -1); } -/* - * Creates a pipe with FD_CLOEXEC set on both file descriptors - */ -int qemu_pipe(int pipefd[2]) -{ - int ret; - -#ifdef CONFIG_PIPE2 - ret = pipe2(pipefd, O_CLOEXEC); - if (ret != -1 || errno != ENOSYS) { - return ret; - } -#endif - ret = pipe(pipefd); - if (ret == 0) { - qemu_set_cloexec(pipefd[0]); - qemu_set_cloexec(pipefd[1]); - } - - return ret; -} - char * -qemu_get_local_state_pathname(const char *relative_pathname) +qemu_get_local_state_dir(void) { - g_autofree char *dir = g_strdup_printf("%s/%s", - CONFIG_QEMU_LOCALSTATEDIR, - relative_pathname); - return get_relocated_path(dir); + return get_relocated_path(CONFIG_QEMU_LOCALSTATEDIR); } void qemu_set_tty_echo(int fd, bool echo) @@ -813,7 +728,7 @@ void *qemu_alloc_stack(size_t *sz) #ifdef CONFIG_DEBUG_STACK_USAGE void *ptr2; #endif - size_t pagesz = qemu_real_host_page_size; + size_t pagesz = qemu_real_host_page_size(); #ifdef _SC_THREAD_STACK_MIN /* avoid stacks smaller than _SC_THREAD_STACK_MIN */ long min_stack_sz = sysconf(_SC_THREAD_STACK_MIN); @@ -875,7 +790,7 @@ void qemu_free_stack(void *stack, size_t sz) unsigned int usage; void *ptr; - for (ptr = stack + qemu_real_host_page_size; ptr < stack + sz; + for (ptr = stack + qemu_real_host_page_size(); ptr < stack + sz; ptr += sizeof(uint32_t)) { if (*(uint32_t *)ptr != 0xdeadbeaf) { break; @@ -933,52 +848,35 @@ void sigaction_invoke(struct sigaction *action, action->sa_sigaction(info->ssi_signo, &si, NULL); } -#ifndef HOST_NAME_MAX -# ifdef _POSIX_HOST_NAME_MAX -# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX -# else -# define HOST_NAME_MAX 255 -# endif -#endif - -char *qemu_get_host_name(Error **errp) -{ - long len = -1; - g_autofree char *hostname = NULL; - -#ifdef _SC_HOST_NAME_MAX - len = sysconf(_SC_HOST_NAME_MAX); -#endif /* _SC_HOST_NAME_MAX */ - - if (len < 0) { - len = HOST_NAME_MAX; - } - - /* Unfortunately, gethostname() below does not guarantee a - * NULL terminated string. Therefore, allocate one byte more - * to be sure. */ - hostname = g_new0(char, len + 1); - - if (gethostname(hostname, len) < 0) { - error_setg_errno(errp, errno, - "cannot get hostname"); - return NULL; - } - - return g_steal_pointer(&hostname); -} - size_t qemu_get_host_physmem(void) { #ifdef _SC_PHYS_PAGES long pages = sysconf(_SC_PHYS_PAGES); if (pages > 0) { - if (pages > SIZE_MAX / qemu_real_host_page_size) { + if (pages > SIZE_MAX / qemu_real_host_page_size()) { return SIZE_MAX; } else { - return pages * qemu_real_host_page_size; + return pages * qemu_real_host_page_size(); } } #endif return 0; } + +int qemu_msync(void *addr, size_t length, int fd) +{ + size_t align_mask = ~(qemu_real_host_page_size() - 1); + + /** + * There are no strict reqs as per the length of mapping + * to be synced. Still the length needs to follow the address + * alignment changes. Additionally - round the size to the multiple + * of PAGE_SIZE + */ + length += ((uintptr_t)addr & (qemu_real_host_page_size() - 1)); + length = (length + ~align_mask) & align_mask; + + addr = (void *)((uintptr_t)addr & align_mask); + + return msync(addr, length, MS_SYNC); +} diff --git a/util/oslib-win32.c b/util/oslib-win32.c index af559ef339..dafef4f157 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -32,7 +32,6 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" #include "qapi/error.h" #include "qemu/main-loop.h" #include "trace.h" @@ -44,35 +43,6 @@ /* this must come after including "trace.h" */ #include -void *qemu_oom_check(void *ptr) -{ - if (ptr == NULL) { - fprintf(stderr, "Failed to allocate memory: %lu\n", GetLastError()); - abort(); - } - return ptr; -} - -void *qemu_try_memalign(size_t alignment, size_t size) -{ - void *ptr; - - g_assert(size != 0); - if (alignment < sizeof(void *)) { - alignment = sizeof(void *); - } else { - g_assert(is_power_of_2(alignment)); - } - ptr = _aligned_malloc(size, alignment); - trace_qemu_memalign(alignment, size, ptr); - return ptr; -} - -void *qemu_memalign(size_t alignment, size_t size) -{ - return qemu_oom_check(qemu_try_memalign(alignment, size)); -} - static int get_allocation_granularity(void) { SYSTEM_INFO system_info; @@ -104,12 +74,6 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, return ptr; } -void qemu_vfree(void *ptr) -{ - trace_qemu_vfree(ptr); - _aligned_free(ptr); -} - void qemu_anon_ram_free(void *ptr, size_t size) { trace_qemu_anon_ram_free(ptr, size); @@ -220,14 +184,14 @@ static int socket_error(void) } } -void qemu_set_block(int fd) +void qemu_socket_set_block(int fd) { unsigned long opt = 0; WSAEventSelect(fd, NULL, 0); ioctlsocket(fd, FIONBIO, &opt); } -int qemu_try_set_nonblock(int fd) +int qemu_socket_try_set_nonblock(int fd) { unsigned long opt = 1; if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) { @@ -236,9 +200,9 @@ int qemu_try_set_nonblock(int fd) return 0; } -void qemu_set_nonblock(int fd) +void qemu_socket_set_nonblock(int fd) { - (void)qemu_try_set_nonblock(fd); + (void)qemu_socket_try_set_nonblock(fd); } int socket_set_fast_reuse(int fd) @@ -265,33 +229,13 @@ void qemu_set_cloexec(int fd) { } -/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */ -#define _W32_FT_OFFSET (116444736000000000ULL) - -int qemu_gettimeofday(qemu_timeval *tp) -{ - union { - unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } _now; - - if(tp) { - GetSystemTimeAsFileTime (&_now.ft); - tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL ); - tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL); - } - /* Always return 0 as per Open Group Base Specifications Issue 6. - Do not set errno on error. */ - return 0; -} - int qemu_get_thread_id(void) { return GetCurrentThreadId(); } char * -qemu_get_local_state_pathname(const char *relative_pathname) +qemu_get_local_state_dir(void) { HRESULT result; char base_path[MAX_PATH+1] = ""; @@ -303,8 +247,7 @@ qemu_get_local_state_pathname(const char *relative_pathname) g_critical("CSIDL_COMMON_APPDATA unavailable: %ld", (long)result); abort(); } - return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", base_path, - relative_pathname); + return g_strdup(base_path); } void qemu_set_tty_echo(int fd, bool echo) @@ -374,7 +317,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, Error **errp) { int i; - size_t pagesize = qemu_real_host_page_size; + size_t pagesize = qemu_real_host_page_size(); memory = (memory + pagesize - 1) & -pagesize; for (i = 0; i < memory / pagesize; i++) { @@ -629,19 +572,6 @@ bool qemu_write_pidfile(const char *filename, Error **errp) return true; } -char *qemu_get_host_name(Error **errp) -{ - wchar_t tmp[MAX_COMPUTERNAME_LENGTH + 1]; - DWORD size = G_N_ELEMENTS(tmp); - - if (GetComputerNameW(tmp, &size) == 0) { - error_setg_win32(errp, GetLastError(), "failed close handle"); - return NULL; - } - - return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL); -} - size_t qemu_get_host_physmem(void) { MEMORYSTATUSEX statex; @@ -652,3 +582,13 @@ size_t qemu_get_host_physmem(void) } return 0; } + +int qemu_msync(void *addr, size_t length, int fd) +{ + /** + * Perform the sync based on the file descriptor + * The sync range will most probably be wider than the one + * requested - but it will still get the job done + */ + return qemu_fdatasync(fd); +} diff --git a/util/pagesize.c b/util/pagesize.c deleted file mode 100644 index 998632cf6e..0000000000 --- a/util/pagesize.c +++ /dev/null @@ -1,18 +0,0 @@ -/* - * pagesize.c - query the host about its page size - * - * Copyright (C) 2017, Emilio G. Cota - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -uintptr_t qemu_real_host_page_size; -intptr_t qemu_real_host_page_mask; - -static void __attribute__((constructor)) init_real_host_page_size(void) -{ - qemu_real_host_page_size = getpagesize(); - qemu_real_host_page_mask = -(intptr_t)qemu_real_host_page_size; -} diff --git a/util/qemu-config.c b/util/qemu-config.c index 436ab63b16..433488aa56 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -314,48 +314,6 @@ void qemu_add_opts(QemuOptsList *list) abort(); } -struct ConfigWriteData { - QemuOptsList *list; - FILE *fp; -}; - -static int config_write_opt(void *opaque, const char *name, const char *value, - Error **errp) -{ - struct ConfigWriteData *data = opaque; - - fprintf(data->fp, " %s = \"%s\"\n", name, value); - return 0; -} - -static int config_write_opts(void *opaque, QemuOpts *opts, Error **errp) -{ - struct ConfigWriteData *data = opaque; - const char *id = qemu_opts_id(opts); - - if (id) { - fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id); - } else { - fprintf(data->fp, "[%s]\n", data->list->name); - } - qemu_opt_foreach(opts, config_write_opt, data, NULL); - fprintf(data->fp, "\n"); - return 0; -} - -void qemu_config_write(FILE *fp) -{ - struct ConfigWriteData data = { .fp = fp }; - QemuOptsList **lists = vm_config_groups; - int i; - - fprintf(fp, "# qemu config file\n\n"); - for (i = 0; lists[i] != NULL; i++) { - data.list = lists[i]; - qemu_opts_foreach(data.list, config_write_opts, &data, NULL); - } -} - /* Returns number of config groups on success, -errno on error */ static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque, const char *fname, Error **errp) diff --git a/util/qemu-coroutine-io.c b/util/qemu-coroutine-io.c index 7f5839cb76..d791932d63 100644 --- a/util/qemu-coroutine-io.c +++ b/util/qemu-coroutine-io.c @@ -23,7 +23,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/sockets.h" #include "qemu/coroutine.h" #include "qemu/iov.h" diff --git a/util/qemu-openpty.c b/util/qemu-openpty.c deleted file mode 100644 index 427f43a769..0000000000 --- a/util/qemu-openpty.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * qemu-openpty.c - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2010 Red Hat, Inc. - * - * Wrapper function qemu_openpty() implementation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * This is not part of oslib-posix.c because this function - * uses openpty() which often in -lutil, and if we add this - * dependency to oslib-posix.o, every app will have to be - * linked with -lutil. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" - -#if defined HAVE_PTY_H -# include -#elif defined CONFIG_BSD -# include -# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) -# include -# else -# include -# endif -#elif defined CONFIG_SOLARIS -# include -# include -#else -# include -#endif - -#ifdef __sun__ - -#if !defined(HAVE_OPENPTY) -/* Once illumos has openpty(), this is going to be removed. */ -static int openpty(int *amaster, int *aslave, char *name, - struct termios *termp, struct winsize *winp) -{ - const char *slave; - int mfd = -1, sfd = -1; - - *amaster = *aslave = -1; - - mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); - if (mfd < 0) - goto err; - - if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) - goto err; - - if ((slave = ptsname(mfd)) == NULL) - goto err; - - if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) - goto err; - - if (ioctl(sfd, I_PUSH, "ptem") == -1 || - (termp != NULL && tcgetattr(sfd, termp) < 0)) - goto err; - - *amaster = mfd; - *aslave = sfd; - - if (winp) - ioctl(sfd, TIOCSWINSZ, winp); - - return 0; - -err: - if (sfd != -1) - close(sfd); - close(mfd); - return -1; -} -#endif - -static void cfmakeraw (struct termios *termios_p) -{ - termios_p->c_iflag &= - ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); - termios_p->c_oflag &= ~OPOST; - termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - termios_p->c_cflag &= ~(CSIZE|PARENB); - termios_p->c_cflag |= CS8; - - termios_p->c_cc[VMIN] = 0; - termios_p->c_cc[VTIME] = 0; -} -#endif - -int qemu_openpty_raw(int *aslave, char *pty_name) -{ - int amaster; - struct termios tty; -#if defined(__OpenBSD__) || defined(__DragonFly__) - char pty_buf[PATH_MAX]; -#define q_ptsname(x) pty_buf -#else - char *pty_buf = NULL; -#define q_ptsname(x) ptsname(x) -#endif - - if (openpty(&amaster, aslave, pty_buf, NULL, NULL) < 0) { - return -1; - } - - /* Set raw attributes on the pty. */ - tcgetattr(*aslave, &tty); - cfmakeraw(&tty); - tcsetattr(*aslave, TCSAFLUSH, &tty); - - if (pty_name) { - strcpy(pty_name, q_ptsname(amaster)); - } - - return amaster; -} diff --git a/util/qemu-progress.c b/util/qemu-progress.c index 20d51f8c12..aa994668f1 100644 --- a/util/qemu-progress.c +++ b/util/qemu-progress.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" +#include "qemu/qemu-progress.h" struct progress_state { float current; diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 0585e7a629..13b5b197f9 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -21,7 +21,6 @@ #include #endif /* CONFIG_AF_VSOCK */ -#include "qemu-common.h" #include "monitor/monitor.h" #include "qapi/clone-visitor.h" #include "qapi/error.h" @@ -97,7 +96,7 @@ bool fd_is_socket(int fd) { int optval; socklen_t optlen = sizeof(optval); - return !qemu_getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); + return !getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); } @@ -185,8 +184,8 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) rebind: if (e->ai_family == PF_INET6) { - qemu_setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, - sizeof(v6only)); + setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, + sizeof(v6only)); } stat = bind(socket, e->ai_addr, e->ai_addrlen); @@ -483,8 +482,8 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) if (saddr->keep_alive) { int val = 1; - int ret = qemu_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, - &val, sizeof(val)); + int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, + &val, sizeof(val)); if (ret < 0) { error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index e1225b63bd..ac1d56e673 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -38,12 +38,20 @@ static void error_exit(int err, const char *msg) abort(); } +static inline clockid_t qemu_timedwait_clockid(void) +{ +#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK + return CLOCK_MONOTONIC; +#else + return CLOCK_REALTIME; +#endif +} + static void compute_abs_deadline(struct timespec *ts, int ms) { - struct timeval tv; - gettimeofday(&tv, NULL); - ts->tv_nsec = tv.tv_usec * 1000 + (ms % 1000) * 1000000; - ts->tv_sec = tv.tv_sec + ms / 1000; + clock_gettime(qemu_timedwait_clockid(), ts); + ts->tv_nsec += (ms % 1000) * 1000000; + ts->tv_sec += ms / 1000; if (ts->tv_nsec >= 1000000000) { ts->tv_sec++; ts->tv_nsec -= 1000000000; @@ -147,11 +155,27 @@ void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line) void qemu_cond_init(QemuCond *cond) { + pthread_condattr_t attr; int err; - err = pthread_cond_init(&cond->cond, NULL); - if (err) + err = pthread_condattr_init(&attr); + if (err) { error_exit(err, __func__); + } +#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK + err = pthread_condattr_setclock(&attr, qemu_timedwait_clockid()); + if (err) { + error_exit(err, __func__); + } +#endif + err = pthread_cond_init(&cond->cond, &attr); + if (err) { + error_exit(err, __func__); + } + err = pthread_condattr_destroy(&attr); + if (err) { + error_exit(err, __func__); + } cond->initialized = true; } @@ -198,16 +222,15 @@ void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, con error_exit(err, __func__); } -bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, - const char *file, const int line) +static bool +qemu_cond_timedwait_ts(QemuCond *cond, QemuMutex *mutex, struct timespec *ts, + const char *file, const int line) { int err; - struct timespec ts; assert(cond->initialized); trace_qemu_mutex_unlock(mutex, file, line); - compute_abs_deadline(&ts, ms); - err = pthread_cond_timedwait(&cond->cond, &mutex->lock, &ts); + err = pthread_cond_timedwait(&cond->cond, &mutex->lock, ts); trace_qemu_mutex_locked(mutex, file, line); if (err && err != ETIMEDOUT) { error_exit(err, __func__); @@ -215,152 +238,77 @@ bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, return err != ETIMEDOUT; } +bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, + const char *file, const int line) +{ + struct timespec ts; + + compute_abs_deadline(&ts, ms); + return qemu_cond_timedwait_ts(cond, mutex, &ts, file, line); +} + void qemu_sem_init(QemuSemaphore *sem, int init) { - int rc; + qemu_mutex_init(&sem->mutex); + qemu_cond_init(&sem->cond); -#ifndef CONFIG_SEM_TIMEDWAIT - rc = pthread_mutex_init(&sem->lock, NULL); - if (rc != 0) { - error_exit(rc, __func__); - } - rc = pthread_cond_init(&sem->cond, NULL); - if (rc != 0) { - error_exit(rc, __func__); - } if (init < 0) { error_exit(EINVAL, __func__); } sem->count = init; -#else - rc = sem_init(&sem->sem, 0, init); - if (rc < 0) { - error_exit(errno, __func__); - } -#endif - sem->initialized = true; } void qemu_sem_destroy(QemuSemaphore *sem) { - int rc; - - assert(sem->initialized); - sem->initialized = false; -#ifndef CONFIG_SEM_TIMEDWAIT - rc = pthread_cond_destroy(&sem->cond); - if (rc < 0) { - error_exit(rc, __func__); - } - rc = pthread_mutex_destroy(&sem->lock); - if (rc < 0) { - error_exit(rc, __func__); - } -#else - rc = sem_destroy(&sem->sem); - if (rc < 0) { - error_exit(errno, __func__); - } -#endif + qemu_cond_destroy(&sem->cond); + qemu_mutex_destroy(&sem->mutex); } void qemu_sem_post(QemuSemaphore *sem) { - int rc; - - assert(sem->initialized); -#ifndef CONFIG_SEM_TIMEDWAIT - pthread_mutex_lock(&sem->lock); + qemu_mutex_lock(&sem->mutex); if (sem->count == UINT_MAX) { - rc = EINVAL; + error_exit(EINVAL, __func__); } else { sem->count++; - rc = pthread_cond_signal(&sem->cond); + qemu_cond_signal(&sem->cond); } - pthread_mutex_unlock(&sem->lock); - if (rc != 0) { - error_exit(rc, __func__); - } -#else - rc = sem_post(&sem->sem); - if (rc < 0) { - error_exit(errno, __func__); - } -#endif + qemu_mutex_unlock(&sem->mutex); } int qemu_sem_timedwait(QemuSemaphore *sem, int ms) { - int rc; + bool rc = true; struct timespec ts; - assert(sem->initialized); -#ifndef CONFIG_SEM_TIMEDWAIT - rc = 0; compute_abs_deadline(&ts, ms); - pthread_mutex_lock(&sem->lock); + qemu_mutex_lock(&sem->mutex); while (sem->count == 0) { - rc = pthread_cond_timedwait(&sem->cond, &sem->lock, &ts); - if (rc == ETIMEDOUT) { + if (ms == 0) { + rc = false; + } else { + rc = qemu_cond_timedwait_ts(&sem->cond, &sem->mutex, &ts, + __FILE__, __LINE__); + } + if (!rc) { /* timeout */ break; } - if (rc != 0) { - error_exit(rc, __func__); - } } - if (rc != ETIMEDOUT) { + if (rc) { --sem->count; } - pthread_mutex_unlock(&sem->lock); - return (rc == ETIMEDOUT ? -1 : 0); -#else - if (ms <= 0) { - /* This is cheaper than sem_timedwait. */ - do { - rc = sem_trywait(&sem->sem); - } while (rc == -1 && errno == EINTR); - if (rc == -1 && errno == EAGAIN) { - return -1; - } - } else { - compute_abs_deadline(&ts, ms); - do { - rc = sem_timedwait(&sem->sem, &ts); - } while (rc == -1 && errno == EINTR); - if (rc == -1 && errno == ETIMEDOUT) { - return -1; - } - } - if (rc < 0) { - error_exit(errno, __func__); - } - return 0; -#endif + qemu_mutex_unlock(&sem->mutex); + return (rc ? 0 : -1); } void qemu_sem_wait(QemuSemaphore *sem) { - int rc; - - assert(sem->initialized); -#ifndef CONFIG_SEM_TIMEDWAIT - pthread_mutex_lock(&sem->lock); + qemu_mutex_lock(&sem->mutex); while (sem->count == 0) { - rc = pthread_cond_wait(&sem->cond, &sem->lock); - if (rc != 0) { - error_exit(rc, __func__); - } + qemu_cond_wait(&sem->cond, &sem->mutex); } --sem->count; - pthread_mutex_unlock(&sem->lock); -#else - do { - rc = sem_wait(&sem->sem); - } while (rc == -1 && errno == EINTR); - if (rc < 0) { - error_exit(errno, __func__); - } -#endif + qemu_mutex_unlock(&sem->mutex); } #ifdef __linux__ diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index 52eb19f351..a2d5a6e825 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/thread.h" #include "qemu/notify.h" #include "qemu-thread-common.h" diff --git a/util/qemu-timer.c b/util/qemu-timer.c index f36c75e594..a670a57881 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -100,7 +100,7 @@ QEMUTimerList *timerlist_new(QEMUClockType type, QEMUTimerList *timer_list; QEMUClock *clock = qemu_clock_ptr(type); - timer_list = g_malloc0(sizeof(QEMUTimerList)); + timer_list = g_new0(QEMUTimerList, 1); qemu_event_init(&timer_list->timers_done_ev, true); timer_list->clock = clock; timer_list->notify_cb = cb; diff --git a/util/qht.c b/util/qht.c index 079605121b..065fc501f4 100644 --- a/util/qht.c +++ b/util/qht.c @@ -69,6 +69,7 @@ #include "qemu/qht.h" #include "qemu/atomic.h" #include "qemu/rcu.h" +#include "qemu/memalign.h" //#define QHT_DEBUG diff --git a/util/rcu.c b/util/rcu.c index c91da9f137..b6d6c71cff 100644 --- a/util/rcu.c +++ b/util/rcu.c @@ -65,7 +65,7 @@ static inline int rcu_gp_ongoing(unsigned long *ctr) /* Written to only by each individual reader. Read by both the reader and the * writers. */ -__thread struct rcu_reader_data rcu_reader; +QEMU_DEFINE_CO_TLS(struct rcu_reader_data, rcu_reader) /* Protected by rcu_registry_lock. */ typedef QLIST_HEAD(, rcu_reader_data) ThreadList; @@ -355,23 +355,23 @@ void drain_call_rcu(void) void rcu_register_thread(void) { - assert(rcu_reader.ctr == 0); + assert(get_ptr_rcu_reader()->ctr == 0); qemu_mutex_lock(&rcu_registry_lock); - QLIST_INSERT_HEAD(®istry, &rcu_reader, node); + QLIST_INSERT_HEAD(®istry, get_ptr_rcu_reader(), node); qemu_mutex_unlock(&rcu_registry_lock); } void rcu_unregister_thread(void) { qemu_mutex_lock(&rcu_registry_lock); - QLIST_REMOVE(&rcu_reader, node); + QLIST_REMOVE(get_ptr_rcu_reader(), node); qemu_mutex_unlock(&rcu_registry_lock); } void rcu_add_force_rcu_notifier(Notifier *n) { qemu_mutex_lock(&rcu_registry_lock); - notifier_list_add(&rcu_reader.force_rcu, n); + notifier_list_add(&get_ptr_rcu_reader()->force_rcu, n); qemu_mutex_unlock(&rcu_registry_lock); } diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 00a80431a0..5ba01177bf 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -163,7 +163,7 @@ void *qemu_vfio_pci_map_bar(QEMUVFIOState *s, int index, Error **errp) { void *p; - assert(QEMU_IS_ALIGNED(offset, qemu_real_host_page_size)); + assert(QEMU_IS_ALIGNED(offset, qemu_real_host_page_size())); assert_bar_index_valid(s, index); p = mmap(NULL, MIN(size, s->bar_region_info[index].size - offset), prot, MAP_SHARED, @@ -279,8 +279,8 @@ static void collect_usable_iova_ranges(QEMUVFIOState *s, void *buf) s->nb_iova_ranges = cap_iova_range->nr_iovas; if (s->nb_iova_ranges > 1) { s->usable_iova_ranges = - g_realloc(s->usable_iova_ranges, - s->nb_iova_ranges * sizeof(struct IOVARange)); + g_renew(struct IOVARange, s->usable_iova_ranges, + s->nb_iova_ranges); } for (i = 0; i < s->nb_iova_ranges; i++) { @@ -591,9 +591,9 @@ static IOVAMapping *qemu_vfio_add_mapping(QEMUVFIOState *s, IOVAMapping m = {.host = host, .size = size, .iova = iova}; IOVAMapping *insert; - assert(QEMU_IS_ALIGNED(size, qemu_real_host_page_size)); - assert(QEMU_IS_ALIGNED(s->low_water_mark, qemu_real_host_page_size)); - assert(QEMU_IS_ALIGNED(s->high_water_mark, qemu_real_host_page_size)); + assert(QEMU_IS_ALIGNED(size, qemu_real_host_page_size())); + assert(QEMU_IS_ALIGNED(s->low_water_mark, qemu_real_host_page_size())); + assert(QEMU_IS_ALIGNED(s->high_water_mark, qemu_real_host_page_size())); trace_qemu_vfio_new_mapping(s, host, size, index, iova); assert(index >= 0); @@ -644,7 +644,7 @@ static void qemu_vfio_undo_mapping(QEMUVFIOState *s, IOVAMapping *mapping, index = mapping - s->mappings; assert(mapping->size > 0); - assert(QEMU_IS_ALIGNED(mapping->size, qemu_real_host_page_size)); + assert(QEMU_IS_ALIGNED(mapping->size, qemu_real_host_page_size())); assert(index >= 0 && index < s->nr_mappings); if (ioctl(s->container, VFIO_IOMMU_UNMAP_DMA, &unmap)) { error_setg_errno(errp, errno, "VFIO_UNMAP_DMA failed"); @@ -752,8 +752,8 @@ int qemu_vfio_dma_map(QEMUVFIOState *s, void *host, size_t size, IOVAMapping *mapping; uint64_t iova0; - assert(QEMU_PTR_IS_ALIGNED(host, qemu_real_host_page_size)); - assert(QEMU_IS_ALIGNED(size, qemu_real_host_page_size)); + assert(QEMU_PTR_IS_ALIGNED(host, qemu_real_host_page_size())); + assert(QEMU_IS_ALIGNED(size, qemu_real_host_page_size())); trace_qemu_vfio_dma_map(s, host, size, temporary, iova); QEMU_LOCK_GUARD(&s->lock); mapping = qemu_vfio_find_mapping(s, host, &index); diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index f66fbba710..232984ace6 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -65,7 +65,7 @@ static void vmsg_unblock_fds(VhostUserMsg *vmsg) { int i; for (i = 0; i < vmsg->fd_num; i++) { - qemu_set_nonblock(vmsg->fds[i]); + qemu_socket_set_nonblock(vmsg->fds[i]); } } @@ -270,7 +270,7 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_fd_watch->fd = fd; vu_fd_watch->cb = cb; - qemu_set_nonblock(fd); + qemu_socket_set_nonblock(fd); aio_set_fd_handler(server->ioc->ctx, fd, true, kick_handler, NULL, NULL, NULL, vu_fd_watch); vu_fd_watch->vu_dev = vu_dev;