diff options
348 files changed, 9747 insertions, 6066 deletions
diff --git a/.gitlab-ci.d/base.yml b/.gitlab-ci.d/base.yml index 69b36c148a..50fb59e147 100644 --- a/.gitlab-ci.d/base.yml +++ b/.gitlab-ci.d/base.yml @@ -6,6 +6,11 @@ # most restrictive to least restrictive # .base_job_template: + variables: + # Each script line from will be in a collapsible section in the job output + # and show the duration of each line. + FF_SCRIPT_SECTIONS: 1 + rules: ############################################################# # Stage 1: exclude scenarios where we definitely don't diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index f09a898c3e..0aa149a352 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -316,8 +316,7 @@ clang-system: IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined - TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu - ppc-softmmu s390x-softmmu + TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu s390x-softmmu MAKE_CHECK_ARGS: check-qtest check-tcg clang-user: @@ -511,6 +510,7 @@ build-oss-fuzz: IMAGE: fedora script: - mkdir build-oss-fuzz + - export LSAN_OPTIONS=suppressions=scripts/oss-fuzz/lsan_suppressions.txt - CC="clang" CXX="clang++" CFLAGS="-fsanitize=address" ./scripts/oss-fuzz/build.sh - export ASAN_OPTIONS="fast_unwind_on_malloc=0" @@ -558,29 +558,22 @@ build-coroutine-sigaltstack: MAKE_CHECK_ARGS: check-unit # Check our reduced build configurations -build-without-default-devices: +build-without-defaults: extends: .native_build_job_template needs: job: amd64-centos8-container variables: IMAGE: centos8 - CONFIGURE_ARGS: --without-default-devices --disable-user - -build-without-default-features: - extends: .native_build_job_template - needs: - job: amd64-fedora-container - variables: - IMAGE: fedora CONFIGURE_ARGS: + --without-default-devices --without-default-features - --disable-capstone + --disable-fdt --disable-pie --disable-qom-cast-debug --disable-strip - TARGETS: avr-softmmu i386-softmmu mips64-softmmu s390x-softmmu sh4-softmmu + TARGETS: avr-softmmu mips64-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user - MAKE_CHECK_ARGS: check-unit check-qtest SPEED=slow + MAKE_CHECK_ARGS: check-unit check-qtest-avr check-qtest-mips64 build-libvhost-user: extends: .base_job_template diff --git a/.gitlab-ci.d/cirrus/freebsd-12.vars b/.gitlab-ci.d/cirrus/freebsd-12.vars index e3fc3235b9..8934e5d57f 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 bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract 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 9f56babd9c..65ce456c48 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 bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract usbredir virglrenderer vte3 zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-12.vars b/.gitlab-ci.d/cirrus/macos-12.vars index ef9e14b373..65b78fa08f 100644 --- a/.gitlab-ci.d/cirrus/macos-12.vars +++ b/.gitlab-ci.d/cirrus/macos-12.vars @@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake' NINJA='/opt/homebrew/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/opt/homebrew/bin/pip3' -PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo json-c 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 bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract usbredir vde vte3 zlib zstd' PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme' PYTHON='/opt/homebrew/bin/python3' diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 8dbbb8f881..74d6259b90 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -187,7 +187,9 @@ cross-win64-system: job: win64-fedora-cross-container variables: IMAGE: fedora-win64-cross - CROSS_SKIP_TARGETS: or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu + CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu + m68k-softmmu microblazeel-softmmu nios2-softmmu + or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu tricore-softmmu xtensaeb-softmmu artifacts: paths: diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 97f99e29c2..9fdc476c48 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -13,6 +13,17 @@ variables: GIT_STRATEGY: clone +# All custom runners can extend this template to upload the testlog +# data as an artifact and also feed the junit report +.custom_artifacts_template: + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + expire_in: 7 days + paths: + - build/meson-logs/testlog.txt + reports: + junit: build/meson-logs/testlog.junit.xml + include: - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml index fcaef9e5ef..f512eaeaa3 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml @@ -3,6 +3,7 @@ # "Install basic packages to build QEMU on Ubuntu 20.04/20.04" ubuntu-20.04-s390x-all-linux-static: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -19,12 +20,11 @@ ubuntu-20.04-s390x-all-linux-static: - ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` + - make --output-sync check-tcg - make --output-sync -j`nproc` check - || { cat meson-logs/testlog.txt; exit 1; } ; - - make --output-sync -j`nproc` check-tcg - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-20.04-s390x-all: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -41,9 +41,9 @@ ubuntu-20.04-s390x-all: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync -j`nproc` check - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-20.04-s390x-alldbg: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -64,9 +64,9 @@ ubuntu-20.04-s390x-alldbg: - make clean - make --output-sync -j`nproc` - make --output-sync -j`nproc` check - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-20.04-s390x-clang: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -86,7 +86,6 @@ ubuntu-20.04-s390x-clang: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync -j`nproc` check - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-20.04-s390x-tci: needs: [] @@ -109,6 +108,7 @@ ubuntu-20.04-s390x-tci: - make --output-sync -j`nproc` ubuntu-20.04-s390x-notcg: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -128,4 +128,3 @@ ubuntu-20.04-s390x-notcg: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync -j`nproc` check - || { cat meson-logs/testlog.txt; exit 1; } ; diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml index 2c386fa3e9..42137aaf2a 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml @@ -3,6 +3,7 @@ # "Install basic packages to build QEMU on Ubuntu 20.04" ubuntu-22.04-aarch32-all: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -22,4 +23,3 @@ ubuntu-22.04-aarch32-all: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check - || { cat meson-logs/testlog.txt; exit 1; } ; diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml index abeb33eaff..8ba85be440 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml @@ -3,6 +3,7 @@ # "Install basic packages to build QEMU on Ubuntu 20.04" ubuntu-22.04-aarch64-all-linux-static: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -19,12 +20,11 @@ ubuntu-22.04-aarch64-all-linux-static: - ../configure --enable-debug --static --disable-system --disable-pie || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc --ignore=40` + - make check-tcg - make --output-sync -j`nproc --ignore=40` check - || { cat meson-logs/testlog.txt; exit 1; } ; - - make --output-sync -j`nproc --ignore=40` check-tcg - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-22.04-aarch64-all: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -44,9 +44,9 @@ ubuntu-22.04-aarch64-all: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-22.04-aarch64-alldbg: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -63,9 +63,9 @@ ubuntu-22.04-aarch64-alldbg: - make clean - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-22.04-aarch64-clang: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -81,11 +81,10 @@ ubuntu-22.04-aarch64-clang: script: - mkdir build - cd build - - ../configure --disable-libssh --cc=clang-10 --cxx=clang++-10 --enable-sanitizers + - ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check - || { cat meson-logs/testlog.txt; exit 1; } ; ubuntu-22.04-aarch64-tci: needs: [] @@ -108,6 +107,7 @@ ubuntu-22.04-aarch64-tci: - make --output-sync -j`nproc --ignore=40` ubuntu-22.04-aarch64-notcg: + extends: .custom_artifacts_template needs: [] stage: build tags: @@ -127,4 +127,3 @@ ubuntu-22.04-aarch64-notcg: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc --ignore=40` - make --output-sync -j`nproc --ignore=40` check - || { cat meson-logs/testlog.txt; exit 1; } ; diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index a1d5790580..cf445b77f6 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -71,7 +71,7 @@ msys2-64bit: # for the msys2 64-bit job, due to the build could not complete within # the project timeout. - ..\msys64\usr\bin\bash -lc '../configure --target-list=x86_64-softmmu - --without-default-devices --disable-opengl' + --without-default-devices' - ..\msys64\usr\bin\bash -lc 'make' # qTests don't run successfully with "--without-default-devices", # so let's exclude the qtests from CI for now. @@ -113,8 +113,7 @@ msys2-32bit: - $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink - mkdir output - cd output - - ..\msys64\usr\bin\bash -lc '../configure --target-list=ppc64-softmmu - --disable-opengl' + - ..\msys64\usr\bin\bash -lc '../configure --target-list=ppc64-softmmu' - ..\msys64\usr\bin\bash -lc 'make' - ..\msys64\usr\bin\bash -lc 'make check MTESTARGS=\"--no-suite qtest\" || { cat meson-logs/testlog.txt; exit 1; }' diff --git a/.travis.yml b/.travis.yml index fb3baabca9..cf088ba4cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,43 +16,6 @@ cache: - $HOME/avocado/data/cache -addons: - apt: - packages: - # Build dependencies - - libaio-dev - - libattr1-dev - - libbrlapi-dev - - libcap-ng-dev - - libcacard-dev - - libgcc-7-dev - - libgnutls28-dev - - libgtk-3-dev - - libiscsi-dev - - liblttng-ust-dev - - libncurses5-dev - - libnfs-dev - - libpixman-1-dev - - libpng-dev - - librados-dev - - libsdl2-dev - - libsdl2-image-dev - - libseccomp-dev - - libspice-protocol-dev - - libspice-server-dev - - libssh-dev - - liburcu-dev - - libusb-1.0-0-dev - - libvdeplug-dev - - libvte-2.91-dev - - libzstd-dev - - ninja-build - - sparse - - uuid-dev - # Tests dependencies - - genisoimage - - # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu # to prevent IRC notifications from forks. This was created using: # $ travis encrypt -r "qemu/qemu" "irc.oftc.net#qemu" @@ -128,6 +91,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -149,7 +113,8 @@ jobs: - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false" + - CONFIG="--disable-containers --enable-fdt=system + --target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false" - UNRELIABLE=true - name: "[ppc64] GCC check-tcg" @@ -162,6 +127,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -183,7 +149,8 @@ jobs: - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=ppc64-softmmu,ppc64le-linux-user" + - CONFIG="--disable-containers --enable-fdt=system + --target-list=ppc64-softmmu,ppc64le-linux-user" - name: "[s390x] GCC check-tcg" arch: s390x @@ -195,6 +162,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -216,7 +184,8 @@ jobs: - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" + - CONFIG="--disable-containers --enable-fdt=system + --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" - UNRELIABLE=true script: - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? @@ -237,6 +206,7 @@ jobs: - libattr1-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgnutls28-dev - libiscsi-dev - liblttng-ust-dev @@ -255,8 +225,8 @@ jobs: # Tests dependencies - genisoimage env: - - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user - --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" + - CONFIG="--disable-containers --enable-fdt=system --audio-drv-list=sdl + --disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" - name: "[s390x] GCC (user)" arch: s390x @@ -281,6 +251,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -300,6 +271,6 @@ jobs: - ninja-build env: - TEST_CMD="make check-unit" - - CONFIG="--disable-containers --disable-tcg --enable-kvm - --disable-tools --host-cc=clang --cxx=clang++" + - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools + --enable-fdt=system --host-cc=clang --cxx=clang++" - UNRELIABLE=true diff --git a/MAINTAINERS b/MAINTAINERS index c581c11a64..fa10ecaeb9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -240,7 +240,6 @@ F: target/microblaze/ F: hw/microblaze/ F: disas/microblaze.c F: tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh -F: tests/tcg/nios2/Makefile.target MIPS TCG CPUs M: Philippe Mathieu-Daudé <philmd@linaro.org> @@ -262,6 +261,7 @@ F: hw/nios2/ F: disas/nios2.c F: configs/devices/nios2-softmmu/default.mak F: tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh +F: tests/tcg/nios2/ OpenRISC TCG CPUs M: Stafford Horne <shorne@gmail.com> @@ -437,7 +437,7 @@ M: Richard Henderson <richard.henderson@linaro.org> R: Paolo Bonzini <pbonzini@redhat.com> S: Maintained F: include/qemu/accel.h -F: include/sysemu/accel-ops.h +F: include/sysemu/accel-*.h F: include/hw/core/accel-cpu.h F: accel/accel-*.c F: accel/Makefile.objs @@ -500,10 +500,7 @@ F: stubs/xen-hw-stub.c Guest CPU Cores (HAXM) --------------------- X86 HAXM CPUs -M: Wenchao Wang <wenchao.wang@intel.com> -L: haxm-team@intel.com -W: https://github.com/intel/haxm/issues -S: Maintained +S: Orphan F: accel/stubs/hax-stub.c F: include/sysemu/hax.h F: target/i386/hax/ @@ -2804,8 +2801,7 @@ F: softmmu/cpus.c F: softmmu/cpu-throttle.c F: softmmu/cpu-timers.c F: softmmu/icount.c -F: softmmu/runstate-action.c -F: softmmu/runstate.c +F: softmmu/runstate* F: qapi/run-state.json Read, Copy, Update (RCU) @@ -3039,6 +3035,11 @@ F: net/slirp.c F: include/net/slirp.h T: git https://people.debian.org/~sthibault/qemu.git slirp +Stats +S: Orphan +F: include/sysemu/stats.h +F: stats/ + Streams M: Edgar E. Iglesias <edgar.iglesias@gmail.com> S: Maintained @@ -3067,7 +3068,7 @@ T: git https://github.com/stefanha/qemu.git tracing TPM M: Stefan Berger <stefanb@linux.ibm.com> S: Maintained -F: tpm.c +F: softmmu/tpm* F: hw/tpm/* F: include/hw/acpi/tpm.h F: include/sysemu/tpm* @@ -3088,6 +3089,7 @@ S: Maintained F: hw/core/vmstate-if.c F: include/hw/vmstate-if.h F: include/migration/ +F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ @@ -3095,6 +3097,7 @@ F: tests/qtest/migration-test.c F: docs/devel/migration.rst F: qapi/migration.json F: tests/migration/ +F: util/userfaultfd.c D-Bus M: Marc-André Lureau <marcandre.lureau@redhat.com> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 7e6a6076b1..9b26582655 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -50,7 +50,7 @@ #include "qemu/range.h" #include "hw/boards.h" -#include "monitor/stats.h" +#include "sysemu/stats.h" /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 04cd1f3092..9c857eeb07 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -504,6 +504,7 @@ static void cpu_exec_exit(CPUState *cpu) if (cc->tcg_ops->cpu_exec_exit) { cc->tcg_ops->cpu_exec_exit(cpu); } + QEMU_PLUGIN_ASSERT(cpu->plugin_mem_cbs == NULL); } void cpu_exec_step_atomic(CPUState *cpu) @@ -980,6 +981,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit); + QEMU_PLUGIN_ASSERT(cpu->plugin_mem_cbs == NULL); /* Try to align the host and virtual clocks if the guest is in advance */ align_clocks(sc, cpu); @@ -1064,13 +1066,12 @@ void tcg_exec_realizefn(CPUState *cpu, Error **errp) /* undo the initializations in reverse order */ void tcg_exec_unrealizefn(CPUState *cpu) { - qemu_plugin_vcpu_exit_hook(cpu); #ifndef CONFIG_USER_ONLY tcg_iommu_free_notifier_list(cpu); #endif /* !CONFIG_USER_ONLY */ tlb_destroy(cpu); - g_free(cpu->tb_jmp_cache); + g_free_rcu(cpu->tb_jmp_cache, rcu); } #ifndef CONFIG_USER_ONLY diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index c7d6514840..17a686bd9e 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -579,7 +579,8 @@ static void inject_mem_helper(TCGOp *begin_op, GArray *arr) * is possible that the code we generate after the instruction is * dead, we also add checks before generating tb_exit etc. */ -static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn, +static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb, + struct qemu_plugin_insn *plugin_insn, TCGOp *begin_op) { GArray *cbs[2]; @@ -599,6 +600,7 @@ static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn, rm_ops(begin_op); return; } + ptb->mem_helper = true; arr = g_array_sized_new(false, false, sizeof(struct qemu_plugin_dyn_cb), n_cbs); @@ -626,15 +628,22 @@ void plugin_gen_disable_mem_helpers(void) { TCGv_ptr ptr; - if (likely(tcg_ctx->plugin_insn == NULL || - !tcg_ctx->plugin_insn->mem_helper)) { + /* + * We could emit the clearing unconditionally and be done. However, this can + * be wasteful if for instance plugins don't track memory accesses, or if + * most TBs don't use helpers. Instead, emit the clearing iff the TB calls + * helpers that might access guest memory. + * + * Note: we do not reset plugin_tb->mem_helper here; a TB might have several + * exit points, and we want to emit the clearing from all of them. + */ + if (!tcg_ctx->plugin_tb->mem_helper) { return; } ptr = tcg_const_ptr(NULL); tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env)); tcg_temp_free_ptr(ptr); - tcg_ctx->plugin_insn->mem_helper = false; } static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb, @@ -682,14 +691,14 @@ static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb, inject_inline_cb(cbs, begin_op, op_rw); } -static void plugin_gen_enable_mem_helper(const struct qemu_plugin_tb *ptb, +static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb, TCGOp *begin_op, int insn_idx) { struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - inject_mem_enable_helper(insn, begin_op); + inject_mem_enable_helper(ptb, insn, begin_op); } -static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb, +static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb, TCGOp *begin_op, int insn_idx) { struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); @@ -750,7 +759,7 @@ static void pr_ops(void) #endif } -static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) +static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { TCGOp *op; int insn_idx = -1; @@ -870,6 +879,7 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, ptb->haddr1 = db->host_addr[0]; ptb->haddr2 = NULL; ptb->mem_only = mem_only; + ptb->mem_helper = false; plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); } diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h index 9829abe4a9..8e685e0654 100644 --- a/accel/tcg/plugin-helpers.h +++ b/accel/tcg/plugin-helpers.h @@ -1,4 +1,4 @@ #ifdef CONFIG_PLUGIN -DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb, TCG_CALL_NO_RWG, void, i32, ptr) -DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG, void, i32, i32, i64, ptr) +DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, ptr) +DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, i32, i64, ptr) #endif diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h index ff5ffc8fc2..b3f6e78835 100644 --- a/accel/tcg/tb-jmp-cache.h +++ b/accel/tcg/tb-jmp-cache.h @@ -18,6 +18,7 @@ * a load_acquire/store_release to 'tb'. */ struct CPUJumpCache { + struct rcu_head rcu; struct { TranslationBlock *tb; #if TARGET_TB_PCREL diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 061519691f..ef5193c67e 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -100,19 +100,24 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns, ops->translate_insn(db, cpu); } - /* Stop translation if translate_insn so indicated. */ - if (db->is_jmp != DISAS_NEXT) { - break; - } - /* * We can't instrument after instructions that change control * flow although this only really affects post-load operations. + * + * Calling plugin_gen_insn_end() before we possibly stop translation + * is important. Even if this ends up as dead code, plugin generation + * needs to see a matching plugin_gen_insn_{start,end}() pair in order + * to accurately track instrumented helpers that might access memory. */ if (plugin_enabled) { plugin_gen_insn_end(); } + /* Stop translation if translate_insn so indicated. */ + if (db->is_jmp != DISAS_NEXT) { + break; + } + /* Stop translation if the output buffer is full, or we have executed all of the allowed instructions. */ if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { diff --git a/audio/audio-hmp-cmds.c b/audio/audio-hmp-cmds.c new file mode 100644 index 0000000000..1237ce9e75 --- /dev/null +++ b/audio/audio-hmp-cmds.c @@ -0,0 +1,83 @@ +/* + * HMP commands related to audio backends + * + * Copyright (c) 2003-2004 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. + */ + +#include "qemu/osdep.h" +#include "audio/audio.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/qmp/qdict.h" + +static QLIST_HEAD (capture_list_head, CaptureState) capture_head; + +void hmp_info_capture(Monitor *mon, const QDict *qdict) +{ + int i; + CaptureState *s; + + for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { + monitor_printf(mon, "[%d]: ", i); + s->ops.info (s->opaque); + } +} + +void hmp_stopcapture(Monitor *mon, const QDict *qdict) +{ + int i; + int n = qdict_get_int(qdict, "n"); + CaptureState *s; + + for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { + if (i == n) { + s->ops.destroy (s->opaque); + QLIST_REMOVE (s, entries); + g_free (s); + return; + } + } +} + +void hmp_wavcapture(Monitor *mon, const QDict *qdict) +{ + const char *path = qdict_get_str(qdict, "path"); + int freq = qdict_get_try_int(qdict, "freq", 44100); + int bits = qdict_get_try_int(qdict, "bits", 16); + int nchannels = qdict_get_try_int(qdict, "nchannels", 2); + const char *audiodev = qdict_get_str(qdict, "audiodev"); + CaptureState *s; + AudioState *as = audio_state_by_name(audiodev); + + if (!as) { + monitor_printf(mon, "Audiodev '%s' not found\n", audiodev); + return; + } + + s = g_malloc0 (sizeof (*s)); + + if (wav_start_capture(as, s, path, freq, bits, nchannels)) { + monitor_printf(mon, "Failed to add wave capture\n"); + g_free (s); + return; + } + QLIST_INSERT_HEAD (&capture_head, s, entries); +} diff --git a/audio/audio.c b/audio/audio.c index d849a94a81..4290309d18 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -28,8 +28,10 @@ #include "monitor/monitor.h" #include "qemu/timer.h" #include "qapi/error.h" +#include "qapi/clone-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-audio.h" +#include "qapi/qapi-commands-audio.h" #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/help_option.h" @@ -2046,16 +2048,36 @@ void audio_create_pdos(Audiodev *dev) break CASE(NONE, none, ); +#ifdef CONFIG_AUDIO_ALSA CASE(ALSA, alsa, Alsa); +#endif +#ifdef CONFIG_AUDIO_COREAUDIO CASE(COREAUDIO, coreaudio, Coreaudio); +#endif +#ifdef CONFIG_DBUS_DISPLAY CASE(DBUS, dbus, ); +#endif +#ifdef CONFIG_AUDIO_DSOUND CASE(DSOUND, dsound, ); +#endif +#ifdef CONFIG_AUDIO_JACK CASE(JACK, jack, Jack); +#endif +#ifdef CONFIG_AUDIO_OSS CASE(OSS, oss, Oss); +#endif +#ifdef CONFIG_AUDIO_PA CASE(PA, pa, Pa); +#endif +#ifdef CONFIG_AUDIO_SDL CASE(SDL, sdl, Sdl); +#endif +#ifdef CONFIG_AUDIO_SNDIO CASE(SNDIO, sndio, ); +#endif +#ifdef CONFIG_SPICE CASE(SPICE, spice, ); +#endif CASE(WAV, wav, ); case AUDIODEV_DRIVER__MAX: @@ -2311,3 +2333,13 @@ size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info, return bytes; } + +AudiodevList *qmp_query_audiodevs(Error **errp) +{ + AudiodevList *ret = NULL; + AudiodevListEntry *e; + QSIMPLEQ_FOREACH(e, &audiodevs, next) { + QAPI_LIST_PREPEND(ret, QAPI_CLONE(Audiodev, e->dev)); + } + return ret; +} diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c index 18a89ffffb..b848001ff7 100644 --- a/audio/audio_legacy.c +++ b/audio/audio_legacy.c @@ -90,6 +90,7 @@ static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst) } +#if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_DSOUND) static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst) { const char *val = getenv(env); @@ -98,15 +99,20 @@ static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst) *has_dst = true; } } +#endif +#if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_COREAUDIO) || \ + defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \ + defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS) static uint32_t frames_to_usecs(uint32_t frames, AudiodevPerDirectionOptions *pdo) { uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100; return (frames * 1000000 + freq / 2) / freq; } +#endif - +#ifdef CONFIG_AUDIO_COREAUDIO static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst, AudiodevPerDirectionOptions *pdo) { @@ -116,14 +122,19 @@ static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst, *has_dst = true; } } +#endif +#if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \ + defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS) static uint32_t samples_to_usecs(uint32_t samples, AudiodevPerDirectionOptions *pdo) { uint32_t channels = pdo->has_channels ? pdo->channels : 2; return frames_to_usecs(samples / channels, pdo); } +#endif +#if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst, AudiodevPerDirectionOptions *pdo) { @@ -133,7 +144,9 @@ static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst, *has_dst = true; } } +#endif +#if defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS) static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo) { AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16; @@ -150,8 +163,11 @@ static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst, *has_dst = true; } } +#endif /* backend specific functions */ + +#ifdef CONFIG_AUDIO_ALSA /* ALSA */ static void handle_alsa_per_direction( AudiodevAlsaPerDirectionOptions *apdo, const char *prefix) @@ -197,7 +213,9 @@ static void handle_alsa(Audiodev *dev) get_millis_to_usecs("QEMU_ALSA_THRESHOLD", &aopt->threshold, &aopt->has_threshold); } +#endif +#ifdef CONFIG_AUDIO_COREAUDIO /* coreaudio */ static void handle_coreaudio(Audiodev *dev) { @@ -210,7 +228,9 @@ static void handle_coreaudio(Audiodev *dev) &dev->u.coreaudio.out->buffer_count, &dev->u.coreaudio.out->has_buffer_count); } +#endif +#ifdef CONFIG_AUDIO_DSOUND /* dsound */ static void handle_dsound(Audiodev *dev) { @@ -225,7 +245,9 @@ static void handle_dsound(Audiodev *dev) &dev->u.dsound.in->has_buffer_length, dev->u.dsound.in); } +#endif +#ifdef CONFIG_AUDIO_OSS /* OSS */ static void handle_oss_per_direction( AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env, @@ -253,7 +275,9 @@ static void handle_oss(Audiodev *dev) get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive); get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy); } +#endif +#ifdef CONFIG_AUDIO_PA /* pulseaudio */ static void handle_pa_per_direction( AudiodevPaPerDirectionOptions *ppdo, const char *env) @@ -277,7 +301,9 @@ static void handle_pa(Audiodev *dev) get_str("QEMU_PA_SERVER", &dev->u.pa.server); } +#endif +#ifdef CONFIG_AUDIO_SDL /* SDL */ static void handle_sdl(Audiodev *dev) { @@ -286,6 +312,7 @@ static void handle_sdl(Audiodev *dev) &dev->u.sdl.out->has_buffer_length, qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.out)); } +#endif /* wav */ static void handle_wav(Audiodev *dev) @@ -345,29 +372,41 @@ static AudiodevListEntry *legacy_opt(const char *drvname) } switch (e->dev->driver) { +#ifdef CONFIG_AUDIO_ALSA case AUDIODEV_DRIVER_ALSA: handle_alsa(e->dev); break; +#endif +#ifdef CONFIG_AUDIO_COREAUDIO case AUDIODEV_DRIVER_COREAUDIO: handle_coreaudio(e->dev); break; +#endif +#ifdef CONFIG_AUDIO_DSOUND case AUDIODEV_DRIVER_DSOUND: handle_dsound(e->dev); break; +#endif +#ifdef CONFIG_AUDIO_OSS case AUDIODEV_DRIVER_OSS: handle_oss(e->dev); break; +#endif +#ifdef CONFIG_AUDIO_PA case AUDIODEV_DRIVER_PA: handle_pa(e->dev); break; +#endif +#ifdef CONFIG_AUDIO_SDL case AUDIODEV_DRIVER_SDL: handle_sdl(e->dev); break; +#endif case AUDIODEV_DRIVER_WAV: handle_wav(e->dev); diff --git a/audio/audio_template.h b/audio/audio_template.h index 720a32e57e..42b4712acb 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -326,27 +326,47 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) switch (dev->driver) { case AUDIODEV_DRIVER_NONE: return dev->u.none.TYPE; +#ifdef CONFIG_AUDIO_ALSA case AUDIODEV_DRIVER_ALSA: return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE); +#endif +#ifdef CONFIG_AUDIO_COREAUDIO case AUDIODEV_DRIVER_COREAUDIO: return qapi_AudiodevCoreaudioPerDirectionOptions_base( dev->u.coreaudio.TYPE); +#endif +#ifdef CONFIG_DBUS_DISPLAY case AUDIODEV_DRIVER_DBUS: return dev->u.dbus.TYPE; +#endif +#ifdef CONFIG_AUDIO_DSOUND case AUDIODEV_DRIVER_DSOUND: return dev->u.dsound.TYPE; +#endif +#ifdef CONFIG_AUDIO_JACK case AUDIODEV_DRIVER_JACK: return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE); +#endif +#ifdef CONFIG_AUDIO_OSS case AUDIODEV_DRIVER_OSS: return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); +#endif +#ifdef CONFIG_AUDIO_PA case AUDIODEV_DRIVER_PA: return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); +#endif +#ifdef CONFIG_AUDIO_SDL case AUDIODEV_DRIVER_SDL: return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE); +#endif +#ifdef CONFIG_AUDIO_SNDIO case AUDIODEV_DRIVER_SNDIO: return dev->u.sndio.TYPE; +#endif +#ifdef CONFIG_SPICE case AUDIODEV_DRIVER_SPICE: return dev->u.spice.TYPE; +#endif case AUDIODEV_DRIVER_WAV: return dev->u.wav.TYPE; diff --git a/audio/meson.build b/audio/meson.build index 34aed78342..0722224ba9 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -1,5 +1,6 @@ softmmu_ss.add([spice_headers, files('audio.c')]) softmmu_ss.add(files( + 'audio-hmp-cmds.c', 'audio_legacy.c', 'mixeng.c', 'noaudio.c', diff --git a/block.c b/block.c index b4a89207ad..aa9062f2c1 100644 --- a/block.c +++ b/block.c @@ -1035,7 +1035,8 @@ static int find_image_format(BlockBackend *file, const char *filename, * Set the current 'total_sectors' value * Return 0 on success, -errno on error. */ -int refresh_total_sectors(BlockDriverState *bs, int64_t hint) +int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs, + int64_t hint) { BlockDriver *drv = bs->drv; IO_CODE(); @@ -1044,13 +1045,13 @@ int refresh_total_sectors(BlockDriverState *bs, int64_t hint) return -ENOMEDIUM; } - /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */ + /* Do not attempt drv->bdrv_co_getlength() on scsi-generic devices */ if (bdrv_is_sg(bs)) return 0; /* query actual device if possible, otherwise just trust the hint */ - if (drv->bdrv_getlength) { - int64_t length = drv->bdrv_getlength(bs); + if (drv->bdrv_co_getlength) { + int64_t length = drv->bdrv_co_getlength(bs); if (length < 0) { return length; } @@ -1601,6 +1602,11 @@ out: g_free(gen_node_name); } +/* + * The caller must always hold @bs AioContext lock, because this function calls + * bdrv_refresh_total_sectors() which polls when called from non-coroutine + * context. + */ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, QDict *options, int open_flags, Error **errp) @@ -1652,7 +1658,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF; bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF; - ret = refresh_total_sectors(bs, bs->total_sectors); + ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); return ret; @@ -3796,6 +3802,10 @@ out: * The reference parameter may be used to specify an existing block device which * should be opened. If specified, neither options nor a filename may be given, * nor can an existing BDS be reused (that is, *pbs has to be NULL). + * + * The caller must always hold @filename AioContext lock, because this + * function eventually calls bdrv_refresh_total_sectors() which polls + * when called from non-coroutine context. */ static BlockDriverState *bdrv_open_inherit(const char *filename, const char *reference, @@ -4084,6 +4094,11 @@ close_and_fail: return NULL; } +/* + * The caller must always hold @filename AioContext lock, because this + * function eventually calls bdrv_refresh_total_sectors() which polls + * when called from non-coroutine context. + */ BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) { @@ -5705,7 +5720,7 @@ exit: } /** - * Implementation of BlockDriver.bdrv_get_allocated_file_size() that + * Implementation of BlockDriver.bdrv_co_get_allocated_file_size() that * sums the size of all data-bearing children. (This excludes backing * children.) */ @@ -5718,7 +5733,7 @@ static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs) if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | BDRV_CHILD_FILTERED)) { - child_size = bdrv_get_allocated_file_size(child->bs); + child_size = bdrv_co_get_allocated_file_size(child->bs); if (child_size < 0) { return child_size; } @@ -5733,7 +5748,7 @@ static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs) * Length of a allocated file in bytes. Sparse files are counted by actual * allocated space. Return < 0 if error or unknown. */ -int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) +int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs) { BlockDriver *drv = bs->drv; IO_CODE(); @@ -5741,8 +5756,8 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) if (!drv) { return -ENOMEDIUM; } - if (drv->bdrv_get_allocated_file_size) { - return drv->bdrv_get_allocated_file_size(bs); + if (drv->bdrv_co_get_allocated_file_size) { + return drv->bdrv_co_get_allocated_file_size(bs); } if (drv->bdrv_file_open) { @@ -5754,7 +5769,7 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) return -ENOTSUP; } else if (drv->is_filter) { /* Filter drivers default to the size of their filtered child */ - return bdrv_get_allocated_file_size(bdrv_filter_bs(bs)); + return bdrv_co_get_allocated_file_size(bdrv_filter_bs(bs)); } else { /* Other drivers default to summing their children's sizes */ return bdrv_sum_allocated_file_size(bs); @@ -5800,7 +5815,7 @@ BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, /** * Return number of sectors on success, -errno on error. */ -int64_t bdrv_nb_sectors(BlockDriverState *bs) +int64_t coroutine_fn bdrv_co_nb_sectors(BlockDriverState *bs) { BlockDriver *drv = bs->drv; IO_CODE(); @@ -5809,7 +5824,7 @@ int64_t bdrv_nb_sectors(BlockDriverState *bs) return -ENOMEDIUM; if (drv->has_variable_length) { - int ret = refresh_total_sectors(bs, bs->total_sectors); + int ret = bdrv_co_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { return ret; } @@ -5821,11 +5836,12 @@ int64_t bdrv_nb_sectors(BlockDriverState *bs) * Return length in bytes on success, -errno on error. * The length is always a multiple of BDRV_SECTOR_SIZE. */ -int64_t bdrv_getlength(BlockDriverState *bs) +int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs) { - int64_t ret = bdrv_nb_sectors(bs); + int64_t ret; IO_CODE(); + ret = bdrv_co_nb_sectors(bs); if (ret < 0) { return ret; } @@ -6285,7 +6301,7 @@ void bdrv_get_backing_filename(BlockDriverState *bs, pstrcpy(filename, filename_size, bs->backing_file); } -int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { int ret; BlockDriver *drv = bs->drv; @@ -6294,15 +6310,15 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) if (!drv) { return -ENOMEDIUM; } - if (!drv->bdrv_get_info) { + if (!drv->bdrv_co_get_info) { BlockDriverState *filtered = bdrv_filter_bs(bs); if (filtered) { - return bdrv_get_info(filtered, bdi); + return bdrv_co_get_info(filtered, bdi); } return -ENOTSUP; } memset(bdi, 0, sizeof(*bdi)); - ret = drv->bdrv_get_info(bs, bdi); + ret = drv->bdrv_co_get_info(bs, bdi); if (ret < 0) { return ret; } @@ -6335,14 +6351,14 @@ BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) return drv->bdrv_get_specific_stats(bs); } -void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) +void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event) { IO_CODE(); - if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { + if (!bs || !bs->drv || !bs->drv->bdrv_co_debug_event) { return; } - bs->drv->bdrv_debug_event(bs, event); + bs->drv->bdrv_co_debug_event(bs, event); } static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs) @@ -6591,7 +6607,7 @@ int bdrv_activate(BlockDriverState *bs, Error **errp) bdrv_dirty_bitmap_skip_store(bm, false); } - ret = refresh_total_sectors(bs, bs->total_sectors); + ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { bs->open_flags |= BDRV_O_INACTIVE; error_setg_errno(errp, -ret, "Could not refresh total sector count"); @@ -6782,7 +6798,7 @@ out: /** * Return TRUE if the media is present */ -bool bdrv_is_inserted(BlockDriverState *bs) +bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs) { BlockDriver *drv = bs->drv; BdrvChild *child; @@ -6791,11 +6807,11 @@ bool bdrv_is_inserted(BlockDriverState *bs) if (!drv) { return false; } - if (drv->bdrv_is_inserted) { - return drv->bdrv_is_inserted(bs); + if (drv->bdrv_co_is_inserted) { + return drv->bdrv_co_is_inserted(bs); } QLIST_FOREACH(child, &bs->children, next) { - if (!bdrv_is_inserted(child->bs)) { + if (!bdrv_co_is_inserted(child->bs)) { return false; } } @@ -6805,13 +6821,13 @@ bool bdrv_is_inserted(BlockDriverState *bs) /** * If eject_flag is TRUE, eject the media. Otherwise, close the tray */ -void bdrv_eject(BlockDriverState *bs, bool eject_flag) +void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag) { BlockDriver *drv = bs->drv; IO_CODE(); - if (drv && drv->bdrv_eject) { - drv->bdrv_eject(bs, eject_flag); + if (drv && drv->bdrv_co_eject) { + drv->bdrv_co_eject(bs, eject_flag); } } @@ -6819,14 +6835,14 @@ void bdrv_eject(BlockDriverState *bs, bool eject_flag) * Lock or unlock the media (if it is locked, the user won't be able * to eject it manually). */ -void bdrv_lock_medium(BlockDriverState *bs, bool locked) +void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked) { BlockDriver *drv = bs->drv; IO_CODE(); trace_bdrv_lock_medium(bs, locked); - if (drv && drv->bdrv_lock_medium) { - drv->bdrv_lock_medium(bs, locked); + if (drv && drv->bdrv_co_lock_medium) { + drv->bdrv_co_lock_medium(bs, locked); } } @@ -7178,12 +7194,6 @@ 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(); diff --git a/block/blkdebug.c b/block/blkdebug.c index fa38c1cf7d..28772be73f 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -836,7 +836,8 @@ static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, } } -static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event) +static void coroutine_fn +blkdebug_co_debug_event(BlockDriverState *bs, BlkdebugEvent event) { BDRVBlkdebugState *s = bs->opaque; struct BlkdebugRule *rule, *next; @@ -966,9 +967,9 @@ static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) return false; } -static int64_t blkdebug_getlength(BlockDriverState *bs) +static int64_t coroutine_fn blkdebug_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } static void blkdebug_refresh_filename(BlockDriverState *bs) @@ -1075,7 +1076,7 @@ static BlockDriver bdrv_blkdebug = { .bdrv_reopen_prepare = blkdebug_reopen_prepare, .bdrv_child_perm = blkdebug_child_perm, - .bdrv_getlength = blkdebug_getlength, + .bdrv_co_getlength = blkdebug_co_getlength, .bdrv_refresh_filename = blkdebug_refresh_filename, .bdrv_refresh_limits = blkdebug_refresh_limits, @@ -1086,7 +1087,7 @@ static BlockDriver bdrv_blkdebug = { .bdrv_co_pdiscard = blkdebug_co_pdiscard, .bdrv_co_block_status = blkdebug_co_block_status, - .bdrv_debug_event = blkdebug_debug_event, + .bdrv_co_debug_event = blkdebug_co_debug_event, .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, .bdrv_debug_remove_breakpoint = blkdebug_debug_remove_breakpoint, diff --git a/block/blkio.c b/block/blkio.c index 6ad86b23d1..0cdc99a729 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -479,7 +479,7 @@ static int coroutine_fn blkio_co_pwrite_zeroes(BlockDriverState *bs, return cod.ret; } -static void blkio_io_unplug(BlockDriverState *bs) +static void coroutine_fn blkio_co_io_unplug(BlockDriverState *bs) { BDRVBlkioState *s = bs->opaque; @@ -839,7 +839,7 @@ static void blkio_close(BlockDriverState *bs) } } -static int64_t blkio_getlength(BlockDriverState *bs) +static int64_t coroutine_fn blkio_co_getlength(BlockDriverState *bs) { BDRVBlkioState *s = bs->opaque; uint64_t capacity; @@ -867,7 +867,7 @@ static int coroutine_fn blkio_truncate(BlockDriverState *bs, int64_t offset, return -ENOTSUP; } - current_length = blkio_getlength(bs); + current_length = blkio_co_getlength(bs); if (offset > current_length) { error_setg(errp, "Cannot grow device"); @@ -880,7 +880,8 @@ static int coroutine_fn blkio_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int blkio_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +blkio_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { return 0; } @@ -998,9 +999,9 @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp) .instance_size = sizeof(BDRVBlkioState), \ .bdrv_file_open = blkio_file_open, \ .bdrv_close = blkio_close, \ - .bdrv_getlength = blkio_getlength, \ + .bdrv_co_getlength = blkio_co_getlength, \ .bdrv_co_truncate = blkio_truncate, \ - .bdrv_get_info = blkio_get_info, \ + .bdrv_co_get_info = blkio_co_get_info, \ .bdrv_attach_aio_context = blkio_attach_aio_context, \ .bdrv_detach_aio_context = blkio_detach_aio_context, \ .bdrv_co_pdiscard = blkio_co_pdiscard, \ @@ -1008,7 +1009,7 @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp) .bdrv_co_pwritev = blkio_co_pwritev, \ .bdrv_co_flush_to_disk = blkio_co_flush, \ .bdrv_co_pwrite_zeroes = blkio_co_pwrite_zeroes, \ - .bdrv_io_unplug = blkio_io_unplug, \ + .bdrv_co_io_unplug = blkio_co_io_unplug, \ .bdrv_refresh_limits = blkio_refresh_limits, \ .bdrv_register_buf = blkio_register_buf, \ .bdrv_unregister_buf = blkio_unregister_buf, \ diff --git a/block/blklogwrites.c b/block/blklogwrites.c index a5bf767184..b00b8a6dd0 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -267,9 +267,9 @@ static void blk_log_writes_close(BlockDriverState *bs) s->log_file = NULL; } -static int64_t blk_log_writes_getlength(BlockDriverState *bs) +static int64_t coroutine_fn blk_log_writes_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, @@ -498,7 +498,7 @@ static BlockDriver bdrv_blk_log_writes = { .bdrv_open = blk_log_writes_open, .bdrv_close = blk_log_writes_close, - .bdrv_getlength = blk_log_writes_getlength, + .bdrv_co_getlength = blk_log_writes_co_getlength, .bdrv_child_perm = blk_log_writes_child_perm, .bdrv_refresh_limits = blk_log_writes_refresh_limits, diff --git a/block/blkreplay.c b/block/blkreplay.c index e3b6a3efb2..16543f585a 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -40,9 +40,9 @@ fail: return ret; } -static int64_t blkreplay_getlength(BlockDriverState *bs) +static int64_t coroutine_fn blkreplay_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } /* This bh is used for synchronization of return from coroutines. @@ -136,7 +136,7 @@ static BlockDriver bdrv_blkreplay = { .bdrv_open = blkreplay_open, .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = blkreplay_getlength, + .bdrv_co_getlength = blkreplay_co_getlength, .bdrv_co_preadv = blkreplay_co_preadv, .bdrv_co_pwritev = blkreplay_co_pwritev, diff --git a/block/blkverify.c b/block/blkverify.c index 0e78bc9dd6..edf1a550f2 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -155,11 +155,11 @@ static void blkverify_close(BlockDriverState *bs) s->test_file = NULL; } -static int64_t blkverify_getlength(BlockDriverState *bs) +static int64_t coroutine_fn blkverify_co_getlength(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - return bdrv_getlength(s->test_file->bs); + return bdrv_co_getlength(s->test_file->bs); } static void coroutine_fn blkverify_do_test_req(void *opaque) @@ -314,7 +314,7 @@ static BlockDriver bdrv_blkverify = { .bdrv_file_open = blkverify_open, .bdrv_close = blkverify_close, .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = blkverify_getlength, + .bdrv_co_getlength = blkverify_co_getlength, .bdrv_refresh_filename = blkverify_refresh_filename, .bdrv_dirname = blkverify_dirname, diff --git a/block/block-backend.c b/block/block-backend.c index ba7bf1d6bc..ef512f7c48 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1235,8 +1235,8 @@ void blk_set_disable_request_queuing(BlockBackend *blk, bool disable) blk->disable_request_queuing = disable; } -static int blk_check_byte_request(BlockBackend *blk, int64_t offset, - int64_t bytes) +static coroutine_fn int blk_check_byte_request(BlockBackend *blk, + int64_t offset, int64_t bytes) { int64_t len; @@ -1253,7 +1253,7 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, } if (!blk->allow_write_beyond_eof) { - len = blk_getlength(blk); + len = bdrv_co_getlength(blk_bs(blk)); if (len < 0) { return len; } @@ -1555,7 +1555,7 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, acb->has_returned = false; co = qemu_coroutine_create(co_entry, acb); - bdrv_coroutine_enter(blk_bs(blk), co); + aio_co_enter(blk_get_aio_context(blk), co); acb->has_returned = true; if (acb->rwco.ret != NOT_DONE) { @@ -1599,14 +1599,15 @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE, cb, opaque); } -int64_t blk_getlength(BlockBackend *blk) +int64_t coroutine_fn blk_co_getlength(BlockBackend *blk) { IO_CODE(); + if (!blk_is_available(blk)) { return -ENOMEDIUM; } - return bdrv_getlength(blk_bs(blk)); + return bdrv_co_getlength(blk_bs(blk)); } void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) @@ -1619,14 +1620,15 @@ void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) } } -int64_t blk_nb_sectors(BlockBackend *blk) +int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk) { IO_CODE(); + if (!blk_is_available(blk)) { return -ENOMEDIUM; } - return bdrv_nb_sectors(blk_bs(blk)); + return bdrv_co_nb_sectors(blk_bs(blk)); } BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, @@ -1983,12 +1985,12 @@ void blk_activate(BlockBackend *blk, Error **errp) bdrv_activate(bs, errp); } -bool blk_is_inserted(BlockBackend *blk) +bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); IO_CODE(); - return bs && bdrv_is_inserted(bs); + return bs && bdrv_co_is_inserted(bs); } bool blk_is_available(BlockBackend *blk) @@ -1997,24 +1999,24 @@ bool blk_is_available(BlockBackend *blk) return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk); } -void blk_lock_medium(BlockBackend *blk, bool locked) +void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked) { BlockDriverState *bs = blk_bs(blk); IO_CODE(); if (bs) { - bdrv_lock_medium(bs, locked); + bdrv_co_lock_medium(bs, locked); } } -void blk_eject(BlockBackend *blk, bool eject_flag) +void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) { BlockDriverState *bs = blk_bs(blk); char *id; IO_CODE(); if (bs) { - bdrv_eject(bs, eject_flag); + bdrv_co_eject(bs, eject_flag); } /* Whether or not we ejected on the backend, @@ -2315,23 +2317,23 @@ void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) notifier_list_add(&blk->insert_bs_notifiers, notify); } -void blk_io_plug(BlockBackend *blk) +void coroutine_fn blk_co_io_plug(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); IO_CODE(); if (bs) { - bdrv_io_plug(bs); + bdrv_co_io_plug(bs); } } -void blk_io_unplug(BlockBackend *blk) +void coroutine_fn blk_co_io_unplug(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); IO_CODE(); if (bs) { - bdrv_io_unplug(bs); + bdrv_co_io_unplug(bs); } } diff --git a/block/commit.c b/block/commit.c index b346341767..41e3599281 100644 --- a/block/commit.c +++ b/block/commit.c @@ -123,13 +123,13 @@ static int coroutine_fn commit_run(Job *job, Error **errp) QEMU_AUTO_VFREE void *buf = NULL; int64_t len, base_len; - len = blk_getlength(s->top); + len = blk_co_getlength(s->top); if (len < 0) { return len; } job_progress_set_remaining(&s->common.job, len); - base_len = blk_getlength(s->base); + base_len = blk_co_getlength(s->base); if (base_len < 0) { return base_len; } diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 13ed4150a6..3280eb2feb 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -121,9 +121,9 @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, } -static int64_t cor_getlength(BlockDriverState *bs) +static int64_t coroutine_fn cor_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } @@ -217,15 +217,15 @@ static int coroutine_fn cor_co_pwritev_compressed(BlockDriverState *bs, } -static void cor_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn cor_co_eject(BlockDriverState *bs, bool eject_flag) { - bdrv_eject(bs->file->bs, eject_flag); + bdrv_co_eject(bs->file->bs, eject_flag); } -static void cor_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn cor_co_lock_medium(BlockDriverState *bs, bool locked) { - bdrv_lock_medium(bs->file->bs, locked); + bdrv_co_lock_medium(bs->file->bs, locked); } @@ -250,7 +250,7 @@ static BlockDriver bdrv_copy_on_read = { .bdrv_close = cor_close, .bdrv_child_perm = cor_child_perm, - .bdrv_getlength = cor_getlength, + .bdrv_co_getlength = cor_co_getlength, .bdrv_co_preadv_part = cor_co_preadv_part, .bdrv_co_pwritev_part = cor_co_pwritev_part, @@ -258,8 +258,8 @@ static BlockDriver bdrv_copy_on_read = { .bdrv_co_pdiscard = cor_co_pdiscard, .bdrv_co_pwritev_compressed = cor_co_pwritev_compressed, - .bdrv_eject = cor_eject, - .bdrv_lock_medium = cor_lock_medium, + .bdrv_co_eject = cor_co_eject, + .bdrv_co_lock_medium = cor_co_lock_medium, .has_variable_length = true, .is_filter = true, diff --git a/block/crypto.c b/block/crypto.c index bbeb9f437c..b70cec97c7 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -531,10 +531,10 @@ static void block_crypto_refresh_limits(BlockDriverState *bs, Error **errp) } -static int64_t block_crypto_getlength(BlockDriverState *bs) +static int64_t coroutine_fn block_crypto_co_getlength(BlockDriverState *bs) { BlockCrypto *crypto = bs->opaque; - int64_t len = bdrv_getlength(bs->file->bs); + int64_t len = bdrv_co_getlength(bs->file->bs); uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); assert(offset < INT64_MAX); @@ -737,13 +737,13 @@ fail: return ret; } -static int block_crypto_get_info_luks(BlockDriverState *bs, - BlockDriverInfo *bdi) +static int coroutine_fn +block_crypto_co_get_info_luks(BlockDriverState *bs, BlockDriverInfo *bdi) { BlockDriverInfo subbdi; int ret; - ret = bdrv_get_info(bs->file->bs, &subbdi); + ret = bdrv_co_get_info(bs->file->bs, &subbdi); if (ret != 0) { return ret; } @@ -953,9 +953,9 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_refresh_limits = block_crypto_refresh_limits, .bdrv_co_preadv = block_crypto_co_preadv, .bdrv_co_pwritev = block_crypto_co_pwritev, - .bdrv_getlength = block_crypto_getlength, + .bdrv_co_getlength = block_crypto_co_getlength, .bdrv_measure = block_crypto_measure, - .bdrv_get_info = block_crypto_get_info_luks, + .bdrv_co_get_info = block_crypto_co_get_info_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, diff --git a/block/curl.c b/block/curl.c index bf45fa3244..cbada22e9e 100644 --- a/block/curl.c +++ b/block/curl.c @@ -958,7 +958,7 @@ static void curl_close(BlockDriverState *bs) g_free(s->proxypassword); } -static int64_t curl_getlength(BlockDriverState *bs) +static int64_t coroutine_fn curl_co_getlength(BlockDriverState *bs) { BDRVCURLState *s = bs->opaque; return s->len; @@ -1002,7 +1002,7 @@ static BlockDriver bdrv_http = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1021,7 +1021,7 @@ static BlockDriver bdrv_https = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1040,7 +1040,7 @@ static BlockDriver bdrv_ftp = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1059,7 +1059,7 @@ static BlockDriver bdrv_ftps = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, diff --git a/block/file-posix.c b/block/file-posix.c index fa227d9d14..d3073a7caa 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -189,7 +189,7 @@ static int fd_open(BlockDriverState *bs) return -EIO; } -static int64_t raw_getlength(BlockDriverState *bs); +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs); typedef struct RawPosixAIOData { BlockDriverState *bs; @@ -2132,7 +2132,7 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_WRITE); } -static void raw_aio_plug(BlockDriverState *bs) +static void coroutine_fn raw_co_io_plug(BlockDriverState *bs) { BDRVRawState __attribute__((unused)) *s = bs->opaque; #ifdef CONFIG_LINUX_AIO @@ -2149,7 +2149,7 @@ static void raw_aio_plug(BlockDriverState *bs) #endif } -static void raw_aio_unplug(BlockDriverState *bs) +static void coroutine_fn raw_co_io_unplug(BlockDriverState *bs) { BDRVRawState __attribute__((unused)) *s = bs->opaque; #ifdef CONFIG_LINUX_AIO @@ -2280,7 +2280,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, } if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { - int64_t cur_length = raw_getlength(bs); + int64_t cur_length = raw_co_getlength(bs); if (offset != cur_length && exact) { error_setg(errp, "Cannot resize device files"); @@ -2298,7 +2298,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, } #ifdef __OpenBSD__ -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int fd = s->fd; @@ -2317,7 +2317,7 @@ static int64_t raw_getlength(BlockDriverState *bs) return st.st_size; } #elif defined(__NetBSD__) -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int fd = s->fd; @@ -2342,7 +2342,7 @@ static int64_t raw_getlength(BlockDriverState *bs) return st.st_size; } #elif defined(__sun__) -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; struct dk_minfo minfo; @@ -2373,7 +2373,7 @@ static int64_t raw_getlength(BlockDriverState *bs) return size; } #elif defined(CONFIG_BSD) -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int fd = s->fd; @@ -2445,7 +2445,7 @@ again: return size; } #else -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int ret; @@ -2464,7 +2464,7 @@ static int64_t raw_getlength(BlockDriverState *bs) } #endif -static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs) { struct stat st; BDRVRawState *s = bs->opaque; @@ -2830,7 +2830,7 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, * round up if necessary. */ if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) { - int64_t file_length = raw_getlength(bs); + int64_t file_length = raw_co_getlength(bs); if (file_length > 0) { /* Ignore errors, this is just a safeguard */ assert(hole == file_length); @@ -2852,7 +2852,7 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, #if defined(__linux__) /* Verify that the file is not in the page cache */ -static void check_cache_dropped(BlockDriverState *bs, Error **errp) +static void coroutine_fn check_cache_dropped(BlockDriverState *bs, Error **errp) { const size_t window_size = 128 * 1024 * 1024; BDRVRawState *s = bs->opaque; @@ -2867,7 +2867,7 @@ static void check_cache_dropped(BlockDriverState *bs, Error **errp) page_size = sysconf(_SC_PAGESIZE); vec = g_malloc(DIV_ROUND_UP(window_size, page_size)); - end = raw_getlength(bs); + end = raw_co_getlength(bs); for (offset = 0; offset < end; offset += window_size) { void *new_window; @@ -3086,11 +3086,40 @@ static int coroutine_fn raw_co_pwrite_zeroes( return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false); } -static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { return 0; } +static ImageInfoSpecific *raw_get_specific_info(BlockDriverState *bs, + Error **errp) +{ + ImageInfoSpecificFile *file_info = g_new0(ImageInfoSpecificFile, 1); + ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); + + *spec_info = (ImageInfoSpecific){ + .type = IMAGE_INFO_SPECIFIC_KIND_FILE, + .u.file.data = file_info, + }; + +#ifdef FS_IOC_FSGETXATTR + { + BDRVRawState *s = bs->opaque; + struct fsxattr attr; + int ret; + + ret = ioctl(s->fd, FS_IOC_FSGETXATTR, &attr); + if (!ret && attr.fsx_extsize != 0) { + file_info->has_extent_size_hint = true; + file_info->extent_size_hint = attr.fsx_extsize; + } + } +#endif + + return spec_info; +} + static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; @@ -3317,15 +3346,15 @@ BlockDriver bdrv_file = { .bdrv_co_copy_range_from = raw_co_copy_range_from, .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, + .bdrv_co_io_plug = raw_co_io_plug, + .bdrv_co_io_unplug = raw_co_io_unplug, .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .bdrv_get_info = raw_get_info, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_info = raw_co_get_info, + .bdrv_get_specific_info = raw_get_specific_info, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, .bdrv_get_specific_stats = raw_get_specific_stats, .bdrv_check_perm = raw_check_perm, .bdrv_set_perm = raw_set_perm, @@ -3689,15 +3718,15 @@ static BlockDriver bdrv_host_device = { .bdrv_co_copy_range_from = raw_co_copy_range_from, .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, + .bdrv_co_io_plug = raw_co_io_plug, + .bdrv_co_io_unplug = raw_co_io_unplug, .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .bdrv_get_info = raw_get_info, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_info = raw_co_get_info, + .bdrv_get_specific_info = raw_get_specific_info, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, .bdrv_get_specific_stats = hdev_get_specific_stats, .bdrv_check_perm = raw_check_perm, .bdrv_set_perm = raw_set_perm, @@ -3757,7 +3786,7 @@ out: return prio; } -static bool cdrom_is_inserted(BlockDriverState *bs) +static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int ret; @@ -3766,7 +3795,7 @@ static bool cdrom_is_inserted(BlockDriverState *bs) return ret == CDS_DISC_OK; } -static void cdrom_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) { BDRVRawState *s = bs->opaque; @@ -3779,7 +3808,7 @@ static void cdrom_eject(BlockDriverState *bs, bool eject_flag) } } -static void cdrom_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) { BDRVRawState *s = bs->opaque; @@ -3813,20 +3842,19 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_pwritev = raw_co_pwritev, .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, + .bdrv_co_io_plug = raw_co_io_plug, + .bdrv_co_io_unplug = raw_co_io_unplug, .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .has_variable_length = true, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .has_variable_length = true, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, /* removable device support */ - .bdrv_is_inserted = cdrom_is_inserted, - .bdrv_eject = cdrom_eject, - .bdrv_lock_medium = cdrom_lock_medium, + .bdrv_co_is_inserted = cdrom_co_is_inserted, + .bdrv_co_eject = cdrom_co_eject, + .bdrv_co_lock_medium = cdrom_co_lock_medium, /* generic scsi device */ .bdrv_co_ioctl = hdev_co_ioctl, @@ -3883,12 +3911,12 @@ static int cdrom_reopen(BlockDriverState *bs) return 0; } -static bool cdrom_is_inserted(BlockDriverState *bs) +static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) { - return raw_getlength(bs) > 0; + return raw_co_getlength(bs) > 0; } -static void cdrom_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) { BDRVRawState *s = bs->opaque; @@ -3908,7 +3936,7 @@ static void cdrom_eject(BlockDriverState *bs, bool eject_flag) cdrom_reopen(bs); } -static void cdrom_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) { BDRVRawState *s = bs->opaque; @@ -3943,20 +3971,19 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_pwritev = raw_co_pwritev, .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, + .bdrv_co_io_plug = raw_co_io_plug, + .bdrv_co_io_unplug = raw_co_io_unplug, .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .has_variable_length = true, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .has_variable_length = true, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, /* removable device support */ - .bdrv_is_inserted = cdrom_is_inserted, - .bdrv_eject = cdrom_eject, - .bdrv_lock_medium = cdrom_lock_medium, + .bdrv_co_is_inserted = cdrom_co_is_inserted, + .bdrv_co_eject = cdrom_co_eject, + .bdrv_co_lock_medium = cdrom_co_lock_medium, }; #endif /* __FreeBSD__ */ diff --git a/block/file-win32.c b/block/file-win32.c index 12be9c3d0f..200d244116 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -526,7 +526,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; LARGE_INTEGER l; @@ -559,7 +559,7 @@ static int64_t raw_getlength(BlockDriverState *bs) return l.QuadPart; } -static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs) { typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD * high); @@ -764,9 +764,9 @@ BlockDriver bdrv_file = { .bdrv_aio_flush = raw_aio_flush, .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_allocated_file_size + = raw_co_get_allocated_file_size, .create_opts = &raw_create_opts, }; @@ -933,11 +933,9 @@ static BlockDriver bdrv_host_device = { .bdrv_detach_aio_context = raw_detach_aio_context, .bdrv_attach_aio_context = raw_attach_aio_context, - .bdrv_getlength = raw_getlength, - .has_variable_length = true, - - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_getlength = raw_co_getlength, + .has_variable_length = true, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, }; static void bdrv_file_init(void) diff --git a/block/filter-compress.c b/block/filter-compress.c index 0ff8d28661..2e2a65966c 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -55,9 +55,9 @@ static int compress_open(BlockDriverState *bs, QDict *options, int flags, } -static int64_t compress_getlength(BlockDriverState *bs) +static int64_t coroutine_fn compress_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } @@ -117,15 +117,17 @@ static void compress_refresh_limits(BlockDriverState *bs, Error **errp) } -static void compress_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn +compress_co_eject(BlockDriverState *bs, bool eject_flag) { - bdrv_eject(bs->file->bs, eject_flag); + bdrv_co_eject(bs->file->bs, eject_flag); } -static void compress_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn +compress_co_lock_medium(BlockDriverState *bs, bool locked) { - bdrv_lock_medium(bs->file->bs, locked); + bdrv_co_lock_medium(bs->file->bs, locked); } @@ -135,7 +137,7 @@ static BlockDriver bdrv_compress = { .bdrv_open = compress_open, .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = compress_getlength, + .bdrv_co_getlength = compress_co_getlength, .bdrv_co_preadv_part = compress_co_preadv_part, .bdrv_co_pwritev_part = compress_co_pwritev_part, @@ -143,8 +145,8 @@ static BlockDriver bdrv_compress = { .bdrv_co_pdiscard = compress_co_pdiscard, .bdrv_refresh_limits = compress_refresh_limits, - .bdrv_eject = compress_eject, - .bdrv_lock_medium = compress_lock_medium, + .bdrv_co_eject = compress_co_eject, + .bdrv_co_lock_medium = compress_co_lock_medium, .has_variable_length = true, .is_filter = true, diff --git a/block/gluster.c b/block/gluster.c index 1ad19ae915..185a83e5e5 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1318,7 +1318,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs, } #endif -static int64_t qemu_gluster_getlength(BlockDriverState *bs) +static int64_t coroutine_fn qemu_gluster_co_getlength(BlockDriverState *bs) { BDRVGlusterState *s = bs->opaque; int64_t ret; @@ -1331,7 +1331,8 @@ static int64_t qemu_gluster_getlength(BlockDriverState *bs) } } -static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn +qemu_gluster_co_get_allocated_file_size(BlockDriverState *bs) { BDRVGlusterState *s = bs->opaque; struct stat st; @@ -1510,7 +1511,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, * round up if necessary. */ if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) { - int64_t file_length = qemu_gluster_getlength(bs); + int64_t file_length = qemu_gluster_co_getlength(bs); if (file_length > 0) { /* Ignore errors, this is just a safeguard */ assert(hole == file_length); @@ -1559,8 +1560,8 @@ static BlockDriver bdrv_gluster = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, @@ -1588,8 +1589,8 @@ static BlockDriver bdrv_gluster_tcp = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, @@ -1617,8 +1618,8 @@ static BlockDriver bdrv_gluster_unix = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, @@ -1652,8 +1653,8 @@ static BlockDriver bdrv_gluster_rdma = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, diff --git a/block/io.c b/block/io.c index a09a19f7a7..2dc0c13e41 100644 --- a/block/io.c +++ b/block/io.c @@ -722,14 +722,14 @@ BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs) /** * Round a region to cluster boundaries */ -void bdrv_round_to_clusters(BlockDriverState *bs, +void coroutine_fn bdrv_round_to_clusters(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *cluster_offset, int64_t *cluster_bytes) { BlockDriverInfo bdi; IO_CODE(); - if (bdrv_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) { + if (bdrv_co_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) { *cluster_offset = offset; *cluster_bytes = bytes; } else { @@ -739,12 +739,12 @@ void bdrv_round_to_clusters(BlockDriverState *bs, } } -static int bdrv_get_cluster_size(BlockDriverState *bs) +static coroutine_fn int bdrv_get_cluster_size(BlockDriverState *bs) { BlockDriverInfo bdi; int ret; - ret = bdrv_get_info(bs, &bdi); + ret = bdrv_co_get_info(bs, &bdi); if (ret < 0 || bdi.cluster_size == 0) { return bs->bl.request_alignment; } else { @@ -1251,7 +1251,7 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, goto err; } - bdrv_debug_event(bs, BLKDBG_COR_WRITE); + bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); if (drv->bdrv_co_pwrite_zeroes && buffer_is_zero(bounce_buffer, pnum)) { /* FIXME: Should we (perhaps conditionally) be setting @@ -1496,10 +1496,10 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); if (pad->head) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); } if (pad->merge_reads && pad->tail) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); } ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes, align, &local_qiov, 0, 0); @@ -1507,10 +1507,10 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, return ret; } if (pad->head) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); } if (pad->merge_reads && pad->tail) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); } if (pad->merge_reads) { @@ -1521,7 +1521,7 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, if (pad->tail) { qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align); - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ret = bdrv_aligned_preadv( child, req, req->overlap_offset + req->overlap_bytes - align, @@ -1529,7 +1529,7 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, if (ret < 0) { return ret; } - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); } zero_mem: @@ -1622,7 +1622,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, trace_bdrv_co_preadv_part(bs, offset, bytes, flags); - if (!bdrv_is_inserted(bs)) { + if (!bdrv_co_is_inserted(bs)) { return -ENOMEDIUM; } @@ -1931,16 +1931,16 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, if (ret < 0) { /* Do nothing, write notifier decided to fail this request */ } else if (flags & BDRV_REQ_ZERO_WRITE) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); } else if (flags & BDRV_REQ_WRITE_COMPRESSED) { ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, qiov, qiov_offset); } else if (bytes <= max_transfer) { - bdrv_debug_event(bs, BLKDBG_PWRITEV); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags); } else { - bdrv_debug_event(bs, BLKDBG_PWRITEV); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV); while (bytes_remaining) { int num = MIN(bytes_remaining, max_transfer); int local_flags = flags; @@ -1963,7 +1963,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, bytes_remaining -= num; } } - bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); if (ret >= 0) { ret = 0; @@ -2067,7 +2067,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); - if (!bdrv_is_inserted(bs)) { + if (!bdrv_co_is_inserted(bs)) { return -ENOMEDIUM; } @@ -2720,8 +2720,8 @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) bdrv_inc_in_flight(bs); - if (drv->bdrv_load_vmstate) { - ret = drv->bdrv_load_vmstate(bs, qiov, pos); + if (drv->bdrv_co_load_vmstate) { + ret = drv->bdrv_co_load_vmstate(bs, qiov, pos); } else if (child_bs) { ret = bdrv_co_readv_vmstate(child_bs, qiov, pos); } else { @@ -2753,8 +2753,8 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) bdrv_inc_in_flight(bs); - if (drv->bdrv_save_vmstate) { - ret = drv->bdrv_save_vmstate(bs, qiov, pos); + if (drv->bdrv_co_save_vmstate) { + ret = drv->bdrv_co_save_vmstate(bs, qiov, pos); } else if (child_bs) { ret = bdrv_co_writev_vmstate(child_bs, qiov, pos); } else { @@ -2835,7 +2835,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) bdrv_inc_in_flight(bs); - if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs) || + if (!bdrv_co_is_inserted(bs) || bdrv_is_read_only(bs) || bdrv_is_sg(bs)) { goto early_exit; } @@ -2959,7 +2959,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, BlockDriverState *bs = child->bs; IO_CODE(); - if (!bs || !bs->drv || !bdrv_is_inserted(bs)) { + if (!bs || !bs->drv || !bdrv_co_is_inserted(bs)) { return -ENOMEDIUM; } @@ -3137,24 +3137,24 @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size) return mem; } -void bdrv_io_plug(BlockDriverState *bs) +void coroutine_fn bdrv_co_io_plug(BlockDriverState *bs) { BdrvChild *child; IO_CODE(); QLIST_FOREACH(child, &bs->children, next) { - bdrv_io_plug(child->bs); + bdrv_co_io_plug(child->bs); } if (qatomic_fetch_inc(&bs->io_plugged) == 0) { BlockDriver *drv = bs->drv; - if (drv && drv->bdrv_io_plug) { - drv->bdrv_io_plug(bs); + if (drv && drv->bdrv_co_io_plug) { + drv->bdrv_co_io_plug(bs); } } } -void bdrv_io_unplug(BlockDriverState *bs) +void coroutine_fn bdrv_co_io_unplug(BlockDriverState *bs) { BdrvChild *child; IO_CODE(); @@ -3162,13 +3162,13 @@ void bdrv_io_unplug(BlockDriverState *bs) assert(bs->io_plugged); if (qatomic_fetch_dec(&bs->io_plugged) == 1) { BlockDriver *drv = bs->drv; - if (drv && drv->bdrv_io_unplug) { - drv->bdrv_io_unplug(bs); + if (drv && drv->bdrv_co_io_unplug) { + drv->bdrv_co_io_unplug(bs); } } QLIST_FOREACH(child, &bs->children, next) { - bdrv_io_unplug(child->bs); + bdrv_co_io_unplug(child->bs); } } @@ -3241,7 +3241,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( assert(!(read_flags & BDRV_REQ_NO_WAIT)); assert(!(write_flags & BDRV_REQ_NO_WAIT)); - if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) { + if (!dst || !dst->bs || !bdrv_co_is_inserted(dst->bs)) { return -ENOMEDIUM; } ret = bdrv_check_request32(dst_offset, bytes, NULL, 0); @@ -3252,7 +3252,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); } - if (!src || !src->bs || !bdrv_is_inserted(src->bs)) { + if (!src || !src->bs || !bdrv_co_is_inserted(src->bs)) { return -ENOMEDIUM; } ret = bdrv_check_request32(src_offset, bytes, NULL, 0); @@ -3444,7 +3444,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, if (new_bytes && backing) { int64_t backing_len; - backing_len = bdrv_getlength(backing->bs); + backing_len = bdrv_co_getlength(backing->bs); if (backing_len < 0) { ret = backing_len; error_setg_errno(errp, -ret, "Could not get backing file size"); @@ -3474,15 +3474,17 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, goto out; } - ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + ret = bdrv_co_refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); } else { offset = bs->total_sectors * BDRV_SECTOR_SIZE; } - /* It's possible that truncation succeeded but refresh_total_sectors + /* + * It's possible that truncation succeeded but bdrv_refresh_total_sectors * failed, but the latter doesn't affect how we should finish the request. - * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. */ + * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. + */ bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0); out: diff --git a/block/iscsi.c b/block/iscsi.c index c16c592042..b3e10f40b6 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1127,8 +1127,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, #endif -static int64_t -iscsi_getlength(BlockDriverState *bs) +static int64_t coroutine_fn +iscsi_co_getlength(BlockDriverState *bs) { IscsiLun *iscsilun = bs->opaque; int64_t len; @@ -2155,7 +2155,7 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, return -EIO; } - cur_length = iscsi_getlength(bs); + cur_length = iscsi_co_getlength(bs); if (offset != cur_length && exact) { error_setg(errp, "Cannot resize iSCSI devices"); return -ENOTSUP; @@ -2171,7 +2171,8 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +iscsi_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { IscsiLun *iscsilun = bs->opaque; bdi->cluster_size = iscsilun->cluster_size; @@ -2434,8 +2435,8 @@ static BlockDriver bdrv_iscsi = { .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, - .bdrv_getlength = iscsi_getlength, - .bdrv_get_info = iscsi_get_info, + .bdrv_co_getlength = iscsi_co_getlength, + .bdrv_co_get_info = iscsi_co_get_info, .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, @@ -2473,8 +2474,8 @@ static BlockDriver bdrv_iser = { .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, - .bdrv_getlength = iscsi_getlength, - .bdrv_get_info = iscsi_get_info, + .bdrv_co_getlength = iscsi_co_getlength, + .bdrv_co_get_info = iscsi_co_get_info, .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, diff --git a/block/meson.build b/block/meson.build index 90011a2805..3662852dc2 100644 --- a/block/meson.build +++ b/block/meson.build @@ -139,6 +139,7 @@ block_gen_c = custom_target('block-gen.c', input: files( '../include/block/block-io.h', '../include/block/dirty-bitmap.h', + '../include/block/block_int-io.h', '../include/block/block-global-state.h', '../include/sysemu/block-backend-io.h', 'coroutines.h' diff --git a/block/mirror.c b/block/mirror.c index 634815d78d..ab326b67c9 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -910,13 +910,13 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) goto immediate_exit; } - s->bdev_length = bdrv_getlength(bs); + s->bdev_length = bdrv_co_getlength(bs); if (s->bdev_length < 0) { ret = s->bdev_length; goto immediate_exit; } - target_length = blk_getlength(s->target); + target_length = blk_co_getlength(s->target); if (target_length < 0) { ret = target_length; goto immediate_exit; @@ -957,7 +957,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) */ bdrv_get_backing_filename(target_bs, backing_filename, sizeof(backing_filename)); - if (!bdrv_get_info(target_bs, &bdi) && bdi.cluster_size) { + if (!bdrv_co_get_info(target_bs, &bdi) && bdi.cluster_size) { s->target_cluster_size = bdi.cluster_size; } else { s->target_cluster_size = BDRV_SECTOR_SIZE; diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 0ff7c84039..6aa5f1be0c 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -725,7 +725,7 @@ static void print_block_info(Monitor *mon, BlockInfo *info, monitor_printf(mon, "\nImages:\n"); image_info = inserted->image; while (1) { - bdrv_image_info_dump(image_info); + bdrv_node_info_dump(qapi_ImageInfo_base(image_info), 0, false); if (image_info->backing_image) { image_info = image_info->backing_image; } else { @@ -1005,3 +1005,24 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) g_free(sn_tab); g_free(global_snapshots); } + +void hmp_change_medium(Monitor *mon, const char *device, const char *target, + const char *arg, const char *read_only, bool force, + Error **errp) +{ + ERRP_GUARD(); + BlockdevChangeReadOnlyMode read_only_mode = 0; + + if (read_only) { + read_only_mode = + qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup, + read_only, + BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, errp); + if (*errp) { + return; + } + } + + qmp_blockdev_change_medium(device, NULL, target, arg, true, force, + !!read_only, read_only_mode, errp); +} diff --git a/block/nbd.c b/block/nbd.c index 7d485c86d2..bf2894ad5c 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1992,7 +1992,7 @@ static int coroutine_fn nbd_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int64_t nbd_getlength(BlockDriverState *bs) +static int64_t coroutine_fn nbd_co_getlength(BlockDriverState *bs) { BDRVNBDState *s = bs->opaque; @@ -2124,7 +2124,7 @@ static BlockDriver bdrv_nbd = { .bdrv_co_pdiscard = nbd_client_co_pdiscard, .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, - .bdrv_getlength = nbd_getlength, + .bdrv_co_getlength = nbd_co_getlength, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, @@ -2152,7 +2152,7 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_co_pdiscard = nbd_client_co_pdiscard, .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, - .bdrv_getlength = nbd_getlength, + .bdrv_co_getlength = nbd_co_getlength, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, @@ -2180,7 +2180,7 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_co_pdiscard = nbd_client_co_pdiscard, .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, - .bdrv_getlength = nbd_getlength, + .bdrv_co_getlength = nbd_co_getlength, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, diff --git a/block/nfs.c b/block/nfs.c index 5e288dfc83..351dc6ec8d 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -732,7 +732,7 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, bdrv_wakeup(task->bs); } -static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs) { NFSClient *client = bs->opaque; NFSRPC task = {0}; @@ -885,7 +885,7 @@ static BlockDriver bdrv_nfs = { .bdrv_has_zero_init = nfs_has_zero_init, /* libnfs does not provide the allocated filesize of a file on win32. */ #if !defined(_WIN32) - .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, + .bdrv_co_get_allocated_file_size = nfs_co_get_allocated_file_size, #endif .bdrv_co_truncate = nfs_file_co_truncate, diff --git a/block/null.c b/block/null.c index 306e605fa1..4808704ffd 100644 --- a/block/null.c +++ b/block/null.c @@ -100,7 +100,7 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, return ret; } -static int64_t null_getlength(BlockDriverState *bs) +static int64_t coroutine_fn null_co_getlength(BlockDriverState *bs) { BDRVNullState *s = bs->opaque; return s->length; @@ -265,7 +265,8 @@ static void null_refresh_filename(BlockDriverState *bs) bs->drv->format_name); } -static int64_t null_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn +null_co_get_allocated_file_size(BlockDriverState *bs) { return 0; } @@ -284,8 +285,8 @@ static BlockDriver bdrv_null_co = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_co_parse_filename, - .bdrv_getlength = null_getlength, - .bdrv_get_allocated_file_size = null_allocated_file_size, + .bdrv_co_getlength = null_co_getlength, + .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, .bdrv_co_preadv = null_co_preadv, .bdrv_co_pwritev = null_co_pwritev, @@ -305,8 +306,8 @@ static BlockDriver bdrv_null_aio = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_aio_parse_filename, - .bdrv_getlength = null_getlength, - .bdrv_get_allocated_file_size = null_allocated_file_size, + .bdrv_co_getlength = null_co_getlength, + .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, .bdrv_aio_preadv = null_aio_preadv, .bdrv_aio_pwritev = null_aio_pwritev, diff --git a/block/nvme.c b/block/nvme.c index 1f1367640a..5b744c2bda 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1002,7 +1002,7 @@ fail: return ret; } -static int64_t nvme_getlength(BlockDriverState *bs) +static int64_t coroutine_fn nvme_co_getlength(BlockDriverState *bs) { BDRVNVMeState *s = bs->opaque; return s->nsze << s->blkshift; @@ -1486,7 +1486,7 @@ static int coroutine_fn nvme_co_truncate(BlockDriverState *bs, int64_t offset, return -ENOTSUP; } - cur_length = nvme_getlength(bs); + cur_length = nvme_co_getlength(bs); if (offset != cur_length && exact) { error_setg(errp, "Cannot resize NVMe devices"); return -ENOTSUP; @@ -1567,14 +1567,14 @@ static void nvme_attach_aio_context(BlockDriverState *bs, } } -static void nvme_aio_plug(BlockDriverState *bs) +static void coroutine_fn nvme_co_io_plug(BlockDriverState *bs) { BDRVNVMeState *s = bs->opaque; assert(!s->plugged); s->plugged = true; } -static void nvme_aio_unplug(BlockDriverState *bs) +static void coroutine_fn nvme_co_io_unplug(BlockDriverState *bs) { BDRVNVMeState *s = bs->opaque; assert(s->plugged); @@ -1643,7 +1643,7 @@ static BlockDriver bdrv_nvme = { .bdrv_parse_filename = nvme_parse_filename, .bdrv_file_open = nvme_file_open, .bdrv_close = nvme_close, - .bdrv_getlength = nvme_getlength, + .bdrv_co_getlength = nvme_co_getlength, .bdrv_probe_blocksizes = nvme_probe_blocksizes, .bdrv_co_truncate = nvme_co_truncate, @@ -1664,8 +1664,8 @@ static BlockDriver bdrv_nvme = { .bdrv_detach_aio_context = nvme_detach_aio_context, .bdrv_attach_aio_context = nvme_attach_aio_context, - .bdrv_io_plug = nvme_aio_plug, - .bdrv_io_unplug = nvme_aio_unplug, + .bdrv_co_io_plug = nvme_co_io_plug, + .bdrv_co_io_unplug = nvme_co_io_unplug, .bdrv_register_buf = nvme_register_buf, .bdrv_unregister_buf = nvme_unregister_buf, diff --git a/block/preallocate.c b/block/preallocate.c index a51fc08515..c0dcf8c346 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -287,7 +287,7 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, } if (s->data_end < 0) { - s->data_end = bdrv_getlength(bs->file->bs); + s->data_end = bdrv_co_getlength(bs->file->bs); if (s->data_end < 0) { return false; } @@ -309,7 +309,7 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, } if (s->file_end < 0) { - s->file_end = bdrv_getlength(bs->file->bs); + s->file_end = bdrv_co_getlength(bs->file->bs); if (s->file_end < 0) { return false; } @@ -381,7 +381,7 @@ preallocate_co_truncate(BlockDriverState *bs, int64_t offset, if (s->data_end >= 0 && offset > s->data_end) { if (s->file_end < 0) { - s->file_end = bdrv_getlength(bs->file->bs); + s->file_end = bdrv_co_getlength(bs->file->bs); if (s->file_end < 0) { error_setg(errp, "failed to get file length"); return s->file_end; @@ -442,7 +442,7 @@ static int coroutine_fn preallocate_co_flush(BlockDriverState *bs) return bdrv_co_flush(bs->file->bs); } -static int64_t preallocate_getlength(BlockDriverState *bs) +static int64_t coroutine_fn preallocate_co_getlength(BlockDriverState *bs) { int64_t ret; BDRVPreallocateState *s = bs->opaque; @@ -451,7 +451,7 @@ static int64_t preallocate_getlength(BlockDriverState *bs) return s->data_end; } - ret = bdrv_getlength(bs->file->bs); + ret = bdrv_co_getlength(bs->file->bs); if (has_prealloc_perms(bs)) { s->file_end = s->zero_start = s->data_end = ret; @@ -537,9 +537,9 @@ BlockDriver bdrv_preallocate_filter = { .format_name = "preallocate", .instance_size = sizeof(BDRVPreallocateState), - .bdrv_getlength = preallocate_getlength, - .bdrv_open = preallocate_open, - .bdrv_close = preallocate_close, + .bdrv_co_getlength = preallocate_co_getlength, + .bdrv_open = preallocate_open, + .bdrv_close = preallocate_close, .bdrv_reopen_prepare = preallocate_reopen_prepare, .bdrv_reopen_commit = preallocate_reopen_commit, diff --git a/block/qapi.c b/block/qapi.c index 9b4da12966..d52f1ab614 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -48,8 +48,10 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, Error **errp) { ImageInfo **p_image_info; + ImageInfo *backing_info; BlockDriverState *bs0, *backing; BlockDeviceInfo *info; + ERRP_GUARD(); if (!bs->drv) { error_setg(errp, "Block device %s is ejected", bs->node_name); @@ -147,37 +149,21 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, bs0 = bs; p_image_info = &info->image; info->backing_file_depth = 0; - while (1) { - Error *local_err = NULL; - bdrv_query_image_info(bs0, p_image_info, &local_err); - if (local_err) { - error_propagate(errp, local_err); - qapi_free_BlockDeviceInfo(info); - return NULL; - } - /* stop gathering data for flat output */ - if (flat) { - break; - } - - if (bs0->drv && bdrv_filter_or_cow_child(bs0)) { - /* - * Put any filtered child here (for backwards compatibility to when - * we put bs0->backing here, which might be any filtered child). - */ - info->backing_file_depth++; - bs0 = bdrv_filter_or_cow_bs(bs0); - p_image_info = &((*p_image_info)->backing_image); - } else { - break; - } + /* + * Skip automatically inserted nodes that the user isn't aware of for + * query-block (blk != NULL), but not for query-named-block-nodes + */ + bdrv_query_image_info(bs0, p_image_info, flat, blk != NULL, errp); + if (*errp) { + qapi_free_BlockDeviceInfo(info); + return NULL; + } - /* Skip automatically inserted nodes that the user isn't aware of for - * query-block (blk != NULL), but not for query-named-block-nodes */ - if (blk) { - bs0 = bdrv_skip_implicit_filters(bs0); - } + backing_info = info->image->backing_image; + while (backing_info) { + info->backing_file_depth++; + backing_info = backing_info->backing_image; } return info; @@ -238,30 +224,18 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, } /** - * bdrv_query_image_info: - * @bs: block device to examine - * @p_info: location to store image information - * @errp: location to store error information - * - * Store "flat" image information in @p_info. - * - * "Flat" means it does *not* query backing image information, - * i.e. (*pinfo)->has_backing_image will be set to false and - * (*pinfo)->backing_image to NULL even when the image does in fact have - * a backing image. - * - * @p_info will be set only on success. On error, store error in @errp. + * Helper function for other query info functions. Store information about @bs + * in @info, setting @errp on error. */ -void bdrv_query_image_info(BlockDriverState *bs, - ImageInfo **p_info, - Error **errp) +static void bdrv_do_query_node_info(BlockDriverState *bs, + BlockNodeInfo *info, + Error **errp) { int64_t size; const char *backing_filename; BlockDriverInfo bdi; int ret; Error *err = NULL; - ImageInfo *info; aio_context_acquire(bdrv_get_aio_context(bs)); @@ -274,7 +248,6 @@ void bdrv_query_image_info(BlockDriverState *bs, bdrv_refresh_filename(bs); - info = g_new0(ImageInfo, 1); info->filename = g_strdup(bs->filename); info->format = g_strdup(bdrv_get_format_name(bs)); info->virtual_size = size; @@ -295,7 +268,6 @@ void bdrv_query_image_info(BlockDriverState *bs, info->format_specific = bdrv_get_specific_info(bs, &err); if (err) { error_propagate(errp, err); - qapi_free_ImageInfo(info); goto out; } backing_filename = bs->backing_file; @@ -331,16 +303,154 @@ void bdrv_query_image_info(BlockDriverState *bs, break; default: error_propagate(errp, err); - qapi_free_ImageInfo(info); goto out; } - *p_info = info; - out: aio_context_release(bdrv_get_aio_context(bs)); } +/** + * bdrv_query_block_node_info: + * @bs: block node to examine + * @p_info: location to store node information + * @errp: location to store error information + * + * Store image information about @bs in @p_info. + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_block_node_info(BlockDriverState *bs, + BlockNodeInfo **p_info, + Error **errp) +{ + BlockNodeInfo *info; + ERRP_GUARD(); + + info = g_new0(BlockNodeInfo, 1); + bdrv_do_query_node_info(bs, info, errp); + if (*errp) { + qapi_free_BlockNodeInfo(info); + return; + } + + *p_info = info; +} + +/** + * bdrv_query_image_info: + * @bs: block node to examine + * @p_info: location to store image information + * @flat: skip backing node information + * @skip_implicit_filters: skip implicit filters in the backing chain + * @errp: location to store error information + * + * Store image information in @p_info, potentially recursively covering the + * backing chain. + * + * If @flat is true, do not query backing image information, i.e. + * (*p_info)->has_backing_image will be set to false and + * (*p_info)->backing_image to NULL even when the image does in fact have a + * backing image. + * + * If @skip_implicit_filters is true, implicit filter nodes in the backing chain + * will be skipped when querying backing image information. + * (@skip_implicit_filters is ignored when @flat is true.) + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_image_info(BlockDriverState *bs, + ImageInfo **p_info, + bool flat, + bool skip_implicit_filters, + Error **errp) +{ + ImageInfo *info; + ERRP_GUARD(); + + info = g_new0(ImageInfo, 1); + bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp); + if (*errp) { + goto fail; + } + + if (!flat) { + BlockDriverState *backing; + + /* + * Use any filtered child here (for backwards compatibility to when + * we always took bs->backing, which might be any filtered child). + */ + backing = bdrv_filter_or_cow_bs(bs); + if (skip_implicit_filters) { + backing = bdrv_skip_implicit_filters(backing); + } + + if (backing) { + bdrv_query_image_info(backing, &info->backing_image, false, + skip_implicit_filters, errp); + if (*errp) { + goto fail; + } + } + } + + *p_info = info; + return; + +fail: + assert(*errp); + qapi_free_ImageInfo(info); +} + +/** + * bdrv_query_block_graph_info: + * @bs: root node to start from + * @p_info: location to store image information + * @errp: location to store error information + * + * Store image information about the graph starting from @bs in @p_info. + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_block_graph_info(BlockDriverState *bs, + BlockGraphInfo **p_info, + Error **errp) +{ + BlockGraphInfo *info; + BlockChildInfoList **children_list_tail; + BdrvChild *c; + ERRP_GUARD(); + + info = g_new0(BlockGraphInfo, 1); + bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp); + if (*errp) { + goto fail; + } + + children_list_tail = &info->children; + + QLIST_FOREACH(c, &bs->children, next) { + BlockChildInfo *c_info; + + c_info = g_new0(BlockChildInfo, 1); + QAPI_LIST_APPEND(children_list_tail, c_info); + + c_info->name = g_strdup(c->name); + bdrv_query_block_graph_info(c->bs, &c_info->info, errp); + if (*errp) { + goto fail; + } + } + + *p_info = info; + return; + +fail: + assert(*errp != NULL); + qapi_free_BlockGraphInfo(info); +} + /* @p_info will be set only on success. */ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, Error **errp) @@ -760,7 +870,36 @@ static void dump_qdict(int indentation, QDict *dict) } } -void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec) +/* + * Return whether dumping the given QObject with dump_qobject() would + * yield an empty dump, i.e. not print anything. + */ +static bool qobject_is_empty_dump(const QObject *obj) +{ + switch (qobject_type(obj)) { + case QTYPE_QNUM: + case QTYPE_QSTRING: + case QTYPE_QBOOL: + return false; + + case QTYPE_QDICT: + return qdict_size(qobject_to(QDict, obj)) == 0; + + case QTYPE_QLIST: + return qlist_empty(qobject_to(QList, obj)); + + default: + abort(); + } +} + +/** + * Dumps the given ImageInfoSpecific object in a human-readable form, + * prepending an optional prefix if the dump is not empty. + */ +void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, + const char *prefix, + int indentation) { QObject *obj, *data; Visitor *v = qobject_output_visitor_new(&obj); @@ -768,45 +907,78 @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec) visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); visit_complete(v, &obj); data = qdict_get(qobject_to(QDict, obj), "data"); - dump_qobject(1, data); + if (!qobject_is_empty_dump(data)) { + if (prefix) { + qemu_printf("%*s%s", indentation * 4, "", prefix); + } + dump_qobject(indentation + 1, data); + } qobject_unref(obj); visit_free(v); } -void bdrv_image_info_dump(ImageInfo *info) +/** + * Print the given @info object in human-readable form. Every field is indented + * using the given @indentation (four spaces per indentation level). + * + * When using this to print a whole block graph, @protocol can be set to true to + * signify that the given information is associated with a protocol node, i.e. + * just data storage for an image, such that the data it presents is not really + * a full VM disk. If so, several fields change name: For example, "virtual + * size" is printed as "file length". + * (Consider a qcow2 image, which is represented by a qcow2 node and a file + * node. Printing a "virtual size" for the file node does not make sense, + * because without the qcow2 node, it is not really a guest disk, so it does not + * have a "virtual size". Therefore, we call it "file length" instead.) + * + * @protocol is ignored when @indentation is 0, because we take that to mean + * that the associated node is the root node in the queried block graph, and + * thus is always to be interpreted as a standalone guest disk. + */ +void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol) { char *size_buf, *dsize_buf; + g_autofree char *ind_s = g_strdup_printf("%*s", indentation * 4, ""); + + if (indentation == 0) { + /* Top level, consider this a normal image */ + protocol = false; + } + if (!info->has_actual_size) { dsize_buf = g_strdup("unavailable"); } else { dsize_buf = size_to_str(info->actual_size); } size_buf = size_to_str(info->virtual_size); - qemu_printf("image: %s\n" - "file format: %s\n" - "virtual size: %s (%" PRId64 " bytes)\n" - "disk size: %s\n", - info->filename, info->format, size_buf, - info->virtual_size, - dsize_buf); + qemu_printf("%s%s: %s\n" + "%s%s: %s\n" + "%s%s: %s (%" PRId64 " bytes)\n" + "%sdisk size: %s\n", + ind_s, protocol ? "filename" : "image", info->filename, + ind_s, protocol ? "protocol type" : "file format", + info->format, + ind_s, protocol ? "file length" : "virtual size", + size_buf, info->virtual_size, + ind_s, dsize_buf); g_free(size_buf); g_free(dsize_buf); if (info->has_encrypted && info->encrypted) { - qemu_printf("encrypted: yes\n"); + qemu_printf("%sencrypted: yes\n", ind_s); } if (info->has_cluster_size) { - qemu_printf("cluster_size: %" PRId64 "\n", - info->cluster_size); + qemu_printf("%scluster_size: %" PRId64 "\n", + ind_s, info->cluster_size); } if (info->has_dirty_flag && info->dirty_flag) { - qemu_printf("cleanly shut down: no\n"); + qemu_printf("%scleanly shut down: no\n", ind_s); } if (info->backing_filename) { - qemu_printf("backing file: %s", info->backing_filename); + qemu_printf("%sbacking file: %s", ind_s, info->backing_filename); if (!info->full_backing_filename) { qemu_printf(" (cannot determine actual path)"); } else if (strcmp(info->backing_filename, @@ -815,15 +987,16 @@ void bdrv_image_info_dump(ImageInfo *info) } qemu_printf("\n"); if (info->backing_filename_format) { - qemu_printf("backing file format: %s\n", - info->backing_filename_format); + qemu_printf("%sbacking file format: %s\n", + ind_s, info->backing_filename_format); } } if (info->has_snapshots) { SnapshotInfoList *elem; - qemu_printf("Snapshot list:\n"); + qemu_printf("%sSnapshot list:\n", ind_s); + qemu_printf("%s", ind_s); bdrv_snapshot_dump(NULL); qemu_printf("\n"); @@ -843,13 +1016,15 @@ void bdrv_image_info_dump(ImageInfo *info) pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); pstrcpy(sn.name, sizeof(sn.name), elem->value->name); + qemu_printf("%s", ind_s); bdrv_snapshot_dump(&sn); qemu_printf("\n"); } } if (info->format_specific) { - qemu_printf("Format specific information:\n"); - bdrv_image_info_specific_dump(info->format_specific); + bdrv_image_info_specific_dump(info->format_specific, + "Format specific information:\n", + indentation); } } diff --git a/block/qcow.c b/block/qcow.c index 5d99f00411..5f0801f545 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -1129,7 +1129,8 @@ fail: return ret; } -static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +qcow_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcowState *s = bs->opaque; bdi->cluster_size = s->cluster_size; @@ -1198,7 +1199,7 @@ static BlockDriver bdrv_qcow = { .bdrv_make_empty = qcow_make_empty, .bdrv_co_pwritev_compressed = qcow_co_pwritev_compressed, - .bdrv_get_info = qcow_get_info, + .bdrv_co_get_info = qcow_co_get_info, .create_opts = &qcow_create_opts, .strong_runtime_opts = qcow_strong_runtime_opts, diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 385260a1b5..5f456a2785 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -117,7 +117,7 @@ static int update_header_sync(BlockDriverState *bs) return bdrv_flush(bs->file->bs); } -static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size) +static inline void bitmap_table_bswap_be(uint64_t *bitmap_table, size_t size) { size_t i; @@ -1403,9 +1403,10 @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) goto fail; } - bitmap_table_to_be(tb, tb_size); + bitmap_table_bswap_be(tb, tb_size); ret = bdrv_pwrite(bs->file, tb_offset, tb_size * sizeof(tb[0]), tb, 0); if (ret < 0) { + bitmap_table_bswap_be(tb, tb_size); error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", bm_name); goto fail; diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 5ffbefee2e..b092f89da9 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -3720,7 +3720,7 @@ int coroutine_fn qcow2_detect_metadata_preallocation(BlockDriverState *bs) return file_length; } - real_allocation = bdrv_get_allocated_file_size(bs->file->bs); + real_allocation = bdrv_co_get_allocated_file_size(bs->file->bs); if (real_allocation < 0) { return real_allocation; } diff --git a/block/qcow2.c b/block/qcow2.c index 2e9c57e269..21aa4c6b7a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5143,7 +5143,8 @@ err: return NULL; } -static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcow2State *s = bs->opaque; bdi->cluster_size = s->cluster_size; @@ -5286,8 +5287,8 @@ static int64_t qcow2_check_vmstate_request(BlockDriverState *bs, return pos; } -static coroutine_fn int qcow2_save_vmstate(BlockDriverState *bs, - QEMUIOVector *qiov, int64_t pos) +static coroutine_fn int qcow2_co_save_vmstate(BlockDriverState *bs, + QEMUIOVector *qiov, int64_t pos) { int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); if (offset < 0) { @@ -5298,8 +5299,8 @@ static coroutine_fn int qcow2_save_vmstate(BlockDriverState *bs, return bs->drv->bdrv_co_pwritev_part(bs, offset, qiov->size, qiov, 0, 0); } -static coroutine_fn int qcow2_load_vmstate(BlockDriverState *bs, - QEMUIOVector *qiov, int64_t pos) +static coroutine_fn int qcow2_co_load_vmstate(BlockDriverState *bs, + QEMUIOVector *qiov, int64_t pos) { int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); if (offset < 0) { @@ -6077,11 +6078,11 @@ BlockDriver bdrv_qcow2 = { .bdrv_snapshot_list = qcow2_snapshot_list, .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, .bdrv_measure = qcow2_measure, - .bdrv_get_info = qcow2_get_info, + .bdrv_co_get_info = qcow2_co_get_info, .bdrv_get_specific_info = qcow2_get_specific_info, - .bdrv_save_vmstate = qcow2_save_vmstate, - .bdrv_load_vmstate = qcow2_load_vmstate, + .bdrv_co_save_vmstate = qcow2_co_save_vmstate, + .bdrv_co_load_vmstate = qcow2_co_load_vmstate, .is_format = true, .supports_backing = true, diff --git a/block/qed.c b/block/qed.c index faa606618e..4473465bba 100644 --- a/block/qed.c +++ b/block/qed.c @@ -424,7 +424,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, } /* Round down file size to the last cluster */ - file_size = bdrv_getlength(bs->file->bs); + file_size = bdrv_co_getlength(bs->file->bs); if (file_size < 0) { error_setg(errp, "Failed to get file length"); return file_size; @@ -1480,13 +1480,14 @@ static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, return ret; } -static int64_t bdrv_qed_getlength(BlockDriverState *bs) +static int64_t coroutine_fn bdrv_qed_co_getlength(BlockDriverState *bs) { BDRVQEDState *s = bs->opaque; return s->header.image_size; } -static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +bdrv_qed_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQEDState *s = bs->opaque; @@ -1653,8 +1654,8 @@ static BlockDriver bdrv_qed = { .bdrv_co_writev = bdrv_qed_co_writev, .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, .bdrv_co_truncate = bdrv_qed_co_truncate, - .bdrv_getlength = bdrv_qed_getlength, - .bdrv_get_info = bdrv_qed_get_info, + .bdrv_co_getlength = bdrv_qed_co_getlength, + .bdrv_co_get_info = bdrv_qed_co_get_info, .bdrv_refresh_limits = bdrv_qed_refresh_limits, .bdrv_change_backing_file = bdrv_qed_change_backing_file, .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache, diff --git a/block/quorum.c b/block/quorum.c index 7f21c03f1f..d1dcf2eaba 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -754,19 +754,19 @@ static int coroutine_fn quorum_co_pwrite_zeroes(BlockDriverState *bs, flags | BDRV_REQ_ZERO_WRITE); } -static int64_t quorum_getlength(BlockDriverState *bs) +static int64_t coroutine_fn quorum_co_getlength(BlockDriverState *bs) { BDRVQuorumState *s = bs->opaque; int64_t result; int i; /* check that all file have the same length */ - result = bdrv_getlength(s->children[0]->bs); + result = bdrv_co_getlength(s->children[0]->bs); if (result < 0) { return result; } for (i = 1; i < s->num_children; i++) { - int64_t value = bdrv_getlength(s->children[i]->bs); + int64_t value = bdrv_co_getlength(s->children[i]->bs); if (value < 0) { return value; } @@ -1283,7 +1283,7 @@ static BlockDriver bdrv_quorum = { .bdrv_co_flush = quorum_co_flush, - .bdrv_getlength = quorum_getlength, + .bdrv_co_getlength = quorum_co_getlength, .bdrv_co_preadv = quorum_co_preadv, .bdrv_co_pwritev = quorum_co_pwritev, diff --git a/block/raw-format.c b/block/raw-format.c index b6a0ce58f4..0dc469b629 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -317,14 +317,14 @@ static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, return bdrv_co_pdiscard(bs->file, offset, bytes); } -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { int64_t len; BDRVRawState *s = bs->opaque; /* Update size. It should not change unless the file was externally * modified. */ - len = bdrv_getlength(bs->file->bs); + len = bdrv_co_getlength(bs->file->bs); if (len < 0) { return len; } @@ -368,9 +368,10 @@ static BlockMeasureInfo *raw_measure(QemuOpts *opts, BlockDriverState *in_bs, return info; } -static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - return bdrv_get_info(bs->file->bs, bdi); + return bdrv_co_get_info(bs->file->bs, bdi); } static void raw_refresh_limits(BlockDriverState *bs, Error **errp) @@ -404,14 +405,14 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, return bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp); } -static void raw_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn raw_co_eject(BlockDriverState *bs, bool eject_flag) { - bdrv_eject(bs->file->bs, eject_flag); + bdrv_co_eject(bs->file->bs, eject_flag); } -static void raw_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn raw_co_lock_medium(BlockDriverState *bs, bool locked) { - bdrv_lock_medium(bs->file->bs, locked); + bdrv_co_lock_medium(bs->file->bs, locked); } static int coroutine_fn raw_co_ioctl(BlockDriverState *bs, @@ -622,16 +623,16 @@ BlockDriver bdrv_raw = { .bdrv_co_copy_range_from = &raw_co_copy_range_from, .bdrv_co_copy_range_to = &raw_co_copy_range_to, .bdrv_co_truncate = &raw_co_truncate, - .bdrv_getlength = &raw_getlength, + .bdrv_co_getlength = &raw_co_getlength, .is_format = true, .has_variable_length = true, .bdrv_measure = &raw_measure, - .bdrv_get_info = &raw_get_info, + .bdrv_co_get_info = &raw_co_get_info, .bdrv_refresh_limits = &raw_refresh_limits, .bdrv_probe_blocksizes = &raw_probe_blocksizes, .bdrv_probe_geometry = &raw_probe_geometry, - .bdrv_eject = &raw_eject, - .bdrv_lock_medium = &raw_lock_medium, + .bdrv_co_eject = &raw_co_eject, + .bdrv_co_lock_medium = &raw_co_lock_medium, .bdrv_co_ioctl = &raw_co_ioctl, .create_opts = &raw_create_opts, .bdrv_has_zero_init = &raw_has_zero_init, diff --git a/block/rbd.c b/block/rbd.c index 6167c5e424..5e102fea0d 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1240,7 +1240,8 @@ coroutine_fn qemu_rbd_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, } #endif -static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +qemu_rbd_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVRBDState *s = bs->opaque; bdi->cluster_size = s->object_size; @@ -1430,7 +1431,7 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, return status; } -static int64_t qemu_rbd_getlength(BlockDriverState *bs) +static int64_t coroutine_fn qemu_rbd_co_getlength(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; int r; @@ -1651,10 +1652,10 @@ static BlockDriver bdrv_rbd = { .bdrv_co_create = qemu_rbd_co_create, .bdrv_co_create_opts = qemu_rbd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_get_info = qemu_rbd_getinfo, + .bdrv_co_get_info = qemu_rbd_co_get_info, .bdrv_get_specific_info = qemu_rbd_get_specific_info, .create_opts = &qemu_rbd_create_opts, - .bdrv_getlength = qemu_rbd_getlength, + .bdrv_co_getlength = qemu_rbd_co_getlength, .bdrv_co_truncate = qemu_rbd_co_truncate, .protocol_name = "rbd", diff --git a/block/replication.c b/block/replication.c index c62f48a874..a27417d310 100644 --- a/block/replication.c +++ b/block/replication.c @@ -179,9 +179,9 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c, return; } -static int64_t replication_getlength(BlockDriverState *bs) +static int64_t coroutine_fn replication_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } static int replication_get_io_status(BDRVReplicationState *s) @@ -758,7 +758,7 @@ static BlockDriver bdrv_replication = { .bdrv_close = replication_close, .bdrv_child_perm = replication_child_perm, - .bdrv_getlength = replication_getlength, + .bdrv_co_getlength = replication_co_getlength, .bdrv_co_readv = replication_co_readv, .bdrv_co_writev = replication_co_writev, diff --git a/block/ssh.c b/block/ssh.c index 8bd2a134c1..b3b3352075 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -1253,7 +1253,7 @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs) return ret; } -static int64_t ssh_getlength(BlockDriverState *bs) +static int64_t coroutine_fn ssh_co_getlength(BlockDriverState *bs) { BDRVSSHState *s = bs->opaque; int64_t length; @@ -1364,7 +1364,7 @@ static BlockDriver bdrv_ssh = { .bdrv_has_zero_init = ssh_has_zero_init, .bdrv_co_readv = ssh_co_readv, .bdrv_co_writev = ssh_co_writev, - .bdrv_getlength = ssh_getlength, + .bdrv_co_getlength = ssh_co_getlength, .bdrv_co_truncate = ssh_co_truncate, .bdrv_co_flush_to_disk = ssh_co_flush, .bdrv_refresh_filename = ssh_refresh_filename, diff --git a/block/throttle.c b/block/throttle.c index 00cb46d0e5..64fa0f5acc 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -106,9 +106,9 @@ static void throttle_close(BlockDriverState *bs) } -static int64_t throttle_getlength(BlockDriverState *bs) +static int64_t coroutine_fn throttle_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, @@ -247,7 +247,7 @@ static BlockDriver bdrv_throttle = { .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = throttle_getlength, + .bdrv_co_getlength = throttle_co_getlength, .bdrv_co_preadv = throttle_co_preadv, .bdrv_co_pwritev = throttle_co_pwritev, diff --git a/block/vdi.c b/block/vdi.c index 479bcfe820..9c8736b26f 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -327,9 +327,10 @@ static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res, return 0; } -static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vdi_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - /* TODO: vdi_get_info would be needed for machine snapshots. + /* TODO: vdi_co_get_info would be needed for machine snapshots. vm_state_offset is still missing. */ BDRVVdiState *s = (BDRVVdiState *)bs->opaque; logout("\n"); @@ -1049,7 +1050,7 @@ static BlockDriver bdrv_vdi = { .bdrv_co_pwritev = vdi_co_pwritev, #endif - .bdrv_get_info = vdi_get_info, + .bdrv_co_get_info = vdi_co_get_info, .is_format = true, .create_opts = &vdi_create_opts, diff --git a/block/vhdx.c b/block/vhdx.c index 4c929800fe..ef1f65d917 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1161,7 +1161,8 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num, } -static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vhdx_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVVHDXState *s = bs->opaque; @@ -2245,7 +2246,7 @@ static BlockDriver bdrv_vhdx = { .bdrv_co_writev = vhdx_co_writev, .bdrv_co_create = vhdx_co_create, .bdrv_co_create_opts = vhdx_co_create_opts, - .bdrv_get_info = vhdx_get_info, + .bdrv_co_get_info = vhdx_co_get_info, .bdrv_co_check = vhdx_co_check, .bdrv_has_zero_init = vhdx_has_zero_init, diff --git a/block/vmdk.c b/block/vmdk.c index 8894dac2d4..5b0eae877e 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -2856,14 +2856,15 @@ static void vmdk_close(BlockDriverState *bs) error_free(s->migration_blocker); } -static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn +vmdk_co_get_allocated_file_size(BlockDriverState *bs) { int i; int64_t ret = 0; int64_t r; BDRVVmdkState *s = bs->opaque; - ret = bdrv_get_allocated_file_size(bs->file->bs); + ret = bdrv_co_get_allocated_file_size(bs->file->bs); if (ret < 0) { return ret; } @@ -2871,7 +2872,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) if (s->extents[i].file == bs->file) { continue; } - r = bdrv_get_allocated_file_size(s->extents[i].file->bs); + r = bdrv_co_get_allocated_file_size(s->extents[i].file->bs); if (r < 0) { return r; } @@ -2897,12 +2898,12 @@ static int vmdk_has_zero_init(BlockDriverState *bs) return 1; } -static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) +static VmdkExtentInfo *vmdk_get_extent_info(VmdkExtent *extent) { - ImageInfo *info = g_new0(ImageInfo, 1); + VmdkExtentInfo *info = g_new0(VmdkExtentInfo, 1); bdrv_refresh_filename(extent->file->bs); - *info = (ImageInfo){ + *info = (VmdkExtentInfo){ .filename = g_strdup(extent->file->bs->filename), .format = g_strdup(extent->type), .virtual_size = extent->sectors * BDRV_SECTOR_SIZE, @@ -2981,7 +2982,7 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs, int i; BDRVVmdkState *s = bs->opaque; ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1); - ImageInfoList **tail; + VmdkExtentInfoList **tail; *spec_info = (ImageInfoSpecific){ .type = IMAGE_INFO_SPECIFIC_KIND_VMDK, @@ -3011,7 +3012,8 @@ static bool vmdk_extents_type_eq(const VmdkExtent *a, const VmdkExtent *b) (a->flat || a->cluster_sectors == b->cluster_sectors); } -static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vmdk_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { int i; BDRVVmdkState *s = bs->opaque; @@ -3124,11 +3126,11 @@ static BlockDriver bdrv_vmdk = { .bdrv_co_create_opts = vmdk_co_create_opts, .bdrv_co_create = vmdk_co_create, .bdrv_co_block_status = vmdk_co_block_status, - .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, + .bdrv_co_get_allocated_file_size = vmdk_co_get_allocated_file_size, .bdrv_has_zero_init = vmdk_has_zero_init, .bdrv_get_specific_info = vmdk_get_specific_info, .bdrv_refresh_limits = vmdk_refresh_limits, - .bdrv_get_info = vmdk_get_info, + .bdrv_co_get_info = vmdk_co_get_info, .bdrv_gather_child_options = vmdk_gather_child_options, .is_format = true, diff --git a/block/vpc.c b/block/vpc.c index 6ee95dcb96..cfdea7db80 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -598,7 +598,8 @@ fail: return ret; } -static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vpc_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVVPCState *s = (BDRVVPCState *)bs->opaque; @@ -1240,7 +1241,7 @@ static BlockDriver bdrv_vpc = { .bdrv_co_pwritev = vpc_co_pwritev, .bdrv_co_block_status = vpc_co_block_status, - .bdrv_get_info = vpc_get_info, + .bdrv_co_get_info = vpc_co_get_info, .is_format = true, .create_opts = &vpc_create_opts, diff --git a/blockdev.c b/blockdev.c index fe9d8d89c0..d7b5c18f0a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1024,6 +1024,7 @@ fail: static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) { BlockDriverState *bs; + AioContext *aio_context; bs = bdrv_lookup_bs(name, name, errp); if (bs == NULL) { @@ -1035,11 +1036,16 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) return NULL; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (!bdrv_is_inserted(bs)) { error_setg(errp, "Device has no medium"); - return NULL; + bs = NULL; } + aio_context_release(aio_context); + return bs; } diff --git a/chardev/char-hmp-cmds.c b/chardev/char-hmp-cmds.c new file mode 100644 index 0000000000..287c2b1bcd --- /dev/null +++ b/chardev/char-hmp-cmds.c @@ -0,0 +1,220 @@ +/* + * HMP commands related to character devices + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "chardev/char.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-char.h" +#include "qapi/qmp/qdict.h" +#include "qemu/config-file.h" +#include "qemu/option.h" + +void hmp_info_chardev(Monitor *mon, const QDict *qdict) +{ + ChardevInfoList *char_info, *info; + + char_info = qmp_query_chardev(NULL); + for (info = char_info; info; info = info->next) { + monitor_printf(mon, "%s: filename=%s\n", info->value->label, + info->value->filename); + } + + qapi_free_ChardevInfoList(char_info); +} + +void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) +{ + const char *chardev = qdict_get_str(qdict, "device"); + const char *data = qdict_get_str(qdict, "data"); + Error *err = NULL; + + qmp_ringbuf_write(chardev, data, false, 0, &err); + + hmp_handle_error(mon, err); +} + +void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *chardev = qdict_get_str(qdict, "device"); + char *data; + Error *err = NULL; + int i; + + data = qmp_ringbuf_read(chardev, size, false, 0, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + for (i = 0; data[i]; i++) { + unsigned char ch = data[i]; + + if (ch == '\\') { + monitor_printf(mon, "\\\\"); + } else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) { + monitor_printf(mon, "\\u%04X", ch); + } else { + monitor_printf(mon, "%c", ch); + } + + } + monitor_printf(mon, "\n"); + g_free(data); +} + +void hmp_chardev_add(Monitor *mon, const QDict *qdict) +{ + const char *args = qdict_get_str(qdict, "args"); + Error *err = NULL; + QemuOpts *opts; + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, true); + if (opts == NULL) { + error_setg(&err, "Parsing chardev args failed"); + } else { + qemu_chr_new_from_opts(opts, NULL, &err); + qemu_opts_del(opts); + } + hmp_handle_error(mon, err); +} + +void hmp_chardev_change(Monitor *mon, const QDict *qdict) +{ + const char *args = qdict_get_str(qdict, "args"); + const char *id; + Error *err = NULL; + ChardevBackend *backend = NULL; + ChardevReturn *ret = NULL; + QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, + true); + if (!opts) { + error_setg(&err, "Parsing chardev args failed"); + goto end; + } + + id = qdict_get_str(qdict, "id"); + if (qemu_opts_id(opts)) { + error_setg(&err, "Unexpected 'id' parameter"); + goto end; + } + + backend = qemu_chr_parse_opts(opts, &err); + if (!backend) { + goto end; + } + + ret = qmp_chardev_change(id, backend, &err); + +end: + qapi_free_ChardevReturn(ret); + qapi_free_ChardevBackend(backend); + qemu_opts_del(opts); + hmp_handle_error(mon, err); +} + +void hmp_chardev_remove(Monitor *mon, const QDict *qdict) +{ + Error *local_err = NULL; + + qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); + hmp_handle_error(mon, local_err); +} + +void hmp_chardev_send_break(Monitor *mon, const QDict *qdict) +{ + Error *local_err = NULL; + + qmp_chardev_send_break(qdict_get_str(qdict, "id"), &local_err); + hmp_handle_error(mon, local_err); +} + +void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + ChardevBackendInfoList *list, *start; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev_backends(NULL); + while (list) { + const char *chr_name = list->value->name; + + if (!strncmp(chr_name, str, len)) { + readline_add_completion(rs, chr_name); + } + list = list->next; + } + qapi_free_ChardevBackendInfoList(start); +} + +void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + ChardevInfoList *list, *start; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev(NULL); + while (list) { + ChardevInfo *chr = list->value; + + if (!strncmp(chr->label, str, len)) { + readline_add_completion(rs, chr->label); + } + list = list->next; + } + qapi_free_ChardevInfoList(start); +} + +static void ringbuf_completion(ReadLineState *rs, const char *str) +{ + size_t len; + ChardevInfoList *list, *start; + + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev(NULL); + while (list) { + ChardevInfo *chr_info = list->value; + + if (!strncmp(chr_info->label, str, len)) { + Chardev *chr = qemu_chr_find(chr_info->label); + if (chr && CHARDEV_IS_RINGBUF(chr)) { + readline_add_completion(rs, chr_info->label); + } + } + list = list->next; + } + qapi_free_ChardevInfoList(start); +} + +void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args != 2) { + return; + } + ringbuf_completion(rs, str); +} diff --git a/chardev/char.c b/chardev/char.c index 87ab6efbcc..11eab7764c 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "monitor/monitor.h" +#include "monitor/qmp-helpers.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" @@ -1166,6 +1167,25 @@ void qmp_chardev_send_break(const char *id, Error **errp) qemu_chr_be_event(chr, CHR_EVENT_BREAK); } +bool qmp_add_client_char(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, const char *protocol, + Error **errp) +{ + Chardev *s = qemu_chr_find(protocol); + + if (!s) { + error_setg(errp, "protocol '%s' is invalid", protocol); + close(fd); + return false; + } + if (qemu_chr_add_client(s, fd) < 0) { + error_setg(errp, "failed to add client"); + close(fd); + return false; + } + return true; +} + /* * Add a timeout callback for the chardev (in milliseconds), return * the GSource object created. Please use this to add timeout hook for diff --git a/chardev/meson.build b/chardev/meson.build index 789b50056a..7a3ba777ab 100644 --- a/chardev/meson.build +++ b/chardev/meson.build @@ -28,7 +28,11 @@ chardev_ss.add(when: 'CONFIG_WIN32', if_true: files( chardev_ss = chardev_ss.apply(config_host, strict: false) -softmmu_ss.add(files('msmouse.c', 'wctablet.c', 'testdev.c')) +softmmu_ss.add(files( + 'char-hmp-cmds.c', + 'msmouse.c', + 'wctablet.c', + 'testdev.c')) chardev_modules = {} diff --git a/common-user/host/ppc/safe-syscall.inc.S b/common-user/host/ppc/safe-syscall.inc.S new file mode 100644 index 0000000000..0851f6c0b8 --- /dev/null +++ b/common-user/host/ppc/safe-syscall.inc.S @@ -0,0 +1,107 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by common-user/safe-syscall.S + * + * Copyright (C) 2022 Linaro, Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Standardize on the _CALL_FOO symbols used by GCC: + * Apple XCode does not define _CALL_DARWIN. + * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV (32-bit). + */ +#if !defined(_CALL_SYSV) && \ + !defined(_CALL_DARWIN) && \ + !defined(_CALL_AIX) && \ + !defined(_CALL_ELF) +# if defined(__APPLE__) +# define _CALL_DARWIN +# elif defined(__ELF__) && TCG_TARGET_REG_BITS == 32 +# define _CALL_SYSV +# else +# error "Unknown ABI" +# endif +#endif + +#ifndef _CALL_SYSV +# error "Unsupported ABI" +#endif + + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + .text + + /* + * This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + */ +safe_syscall_base: + .cfi_startproc + stwu 1, -8(1) + .cfi_def_cfa_offset 8 + stw 30, 4(1) + .cfi_offset 30, -4 + + /* + * We enter with r3 == &signal_pending + * r4 == syscall number + * r5 ... r10 == syscall arguments + * and return the result in r3 + * and the syscall instruction needs + * r0 == syscall number + * r3 ... r8 == syscall arguments + * and returns the result in r3 + * Shuffle everything around appropriately. + */ + mr 30, 3 /* signal_pending */ + mr 0, 4 /* syscall number */ + mr 3, 5 /* syscall arguments */ + mr 4, 6 + mr 5, 7 + mr 6, 8 + mr 7, 9 + mr 8, 10 + + /* + * This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + lwz 12, 0(30) + cmpwi 0, 12, 0 + bne- 2f + sc +safe_syscall_end: + /* code path when we did execute the syscall */ + lwz 30, 4(1) /* restore r30 */ + addi 1, 1, 8 /* restore stack */ + .cfi_restore 30 + .cfi_def_cfa_offset 0 + bnslr+ /* return on success */ + b safe_syscall_set_errno_tail + + /* code path when we didn't execute the syscall */ +2: lwz 30, 4(1) + addi 1, 1, 8 + addi 3, 0, QEMU_ERESTARTSYS + b safe_syscall_set_errno_tail + + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/configs/targets/nios2-softmmu.mak b/configs/targets/nios2-softmmu.mak index 1e93b54cd1..5823fc02c8 100644 --- a/configs/targets/nios2-softmmu.mak +++ b/configs/targets/nios2-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=nios2 TARGET_ALIGNED_ONLY=y +TARGET_NEED_FDT=y diff --git a/configure b/configure index 9e407ce2e3..64960c6000 100755 --- a/configure +++ b/configure @@ -2483,7 +2483,11 @@ for target in $target_list; do tcg_tests_targets="$tcg_tests_targets $target" fi done -echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> config-host.mak) + +if test "$tcg" = "enabled"; then + echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> config-host.mak +fi +) if test "$skip_meson" = no; then cross="config-meson.cross.new" diff --git a/cpu.c b/cpu.c index 4a7d865427..21cf809614 100644 --- a/cpu.c +++ b/cpu.c @@ -176,11 +176,20 @@ void cpu_exec_unrealizefn(CPUState *cpu) vmstate_unregister(NULL, &vmstate_cpu_common, cpu); } #endif + + /* Call the plugin hook before clearing cpu->cpu_index in cpu_list_remove */ if (tcg_enabled()) { - tcg_exec_unrealizefn(cpu); + qemu_plugin_vcpu_exit_hook(cpu); } cpu_list_remove(cpu); + /* + * Now that the vCPU has been removed from the RCU list, we can call + * tcg_exec_unrealizefn, which may free fields using call_rcu. + */ + if (tcg_enabled()) { + tcg_exec_unrealizefn(cpu); + } } /* diff --git a/disas.c b/disas.c index 3b31315f40..b087c12c47 100644 --- a/disas.c +++ b/disas.c @@ -198,6 +198,8 @@ static void initialize_debug_host(CPUDebug *s) s->info.cap_insn_split = 6; #elif defined(__hppa__) s->info.print_insn = print_insn_hppa; +#elif defined(__loongarch__) + s->info.print_insn = print_insn_loongarch; #endif } diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 9f1bbc495d..da2e6fe63d 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -87,18 +87,18 @@ as short-form boolean values, and passed to plugins as ``arg_name=on``. However, short-form booleans are deprecated and full explicit ``arg_name=on`` form is preferred. -``-drive if=none`` for the sifive_u OTP device (since 6.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Using ``-drive if=none`` to configure the OTP device of the sifive_u -RISC-V machine is deprecated. Use ``-drive if=pflash`` instead. - ``-no-hpet`` (since 8.0) '''''''''''''''''''''''' The HPET setting has been turned into a machine property. Use ``-machine hpet=off`` instead. +``-accel hax`` (since 8.0) +'''''''''''''''''''''''''' + +The HAXM project has been retired (see https://github.com/intel/haxm#status). +Use "whpx" (on Windows) or "hvf" (on macOS) instead. + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst new file mode 100644 index 0000000000..b510a54418 --- /dev/null +++ b/docs/about/emulation.rst @@ -0,0 +1,190 @@ +Emulation +========= + +QEMU's Tiny Code Generator (TCG) provides the ability to emulate a +number of CPU architectures on any supported host platform. Both +:ref:`System Emulation` and :ref:`User Mode Emulation` are supported +depending on the guest architecture. + +.. list-table:: Supported Guest Architectures for Emulation + :widths: 30 10 10 50 + :header-rows: 1 + + * - Architecture (qemu name) + - System + - User + - Notes + * - Alpha + - Yes + - Yes + - Legacy 64 bit RISC ISA developed by DEC + * - Arm (arm, aarch64) + - :ref:`Yes<ARM-System-emulator>` + - Yes + - Wide range of features, see :ref:`Arm Emulation` for details + * - AVR + - :ref:`Yes<AVR-System-emulator>` + - No + - 8 bit micro controller, often used in maker projects + * - Cris + - Yes + - Yes + - Embedded RISC chip developed by AXIS + * - Hexagon + - No + - Yes + - Family of DSPs by Qualcomm + * - PA-RISC (hppa) + - Yes + - Yes + - A legacy RISC system used in HP's old minicomputers + * - x86 (i386, x86_64) + - :ref:`Yes<QEMU-PC-System-emulator>` + - Yes + - The ubiquitous desktop PC CPU architecture, 32 and 64 bit. + * - Loongarch + - Yes + - Yes + - A MIPS-like 64bit RISC architecture developed in China + * - m68k + - :ref:`Yes<ColdFire-System-emulator>` + - Yes + - Motorola 68000 variants and ColdFire + * - Microblaze + - Yes + - Yes + - RISC based soft-core by Xilinx + * - MIPS (mips*) + - :ref:`Yes<MIPS-System-emulator>` + - Yes + - Venerable RISC architecture originally out of Stanford University + * - Nios2 + - Yes + - Yes + - 32 bit embedded soft-core by Altera + * - OpenRISC + - :ref:`Yes<OpenRISC-System-emulator>` + - Yes + - Open source RISC architecture developed by the OpenRISC community + * - Power (ppc, ppc64) + - :ref:`Yes<PowerPC-System-emulator>` + - Yes + - A general purpose RISC architecture now managed by IBM + * - RISC-V + - :ref:`Yes<RISC-V-System-emulator>` + - Yes + - An open standard RISC ISA maintained by RISC-V International + * - RX + - :ref:`Yes<RX-System-emulator>` + - No + - A 32 bit micro controller developed by Renesas + * - s390x + - :ref:`Yes<s390x-System-emulator>` + - Yes + - A 64 bit CPU found in IBM's System Z mainframes + * - sh4 + - Yes + - Yes + - A 32 bit RISC embedded CPU developed by Hitachi + * - SPARC (sparc, sparc64) + - :ref:`Yes<Sparc32-System-emulator>` + - Yes + - A RISC ISA originally developed by Sun Microsystems + * - Tricore + - Yes + - No + - A 32 bit RISC/uController/DSP developed by Infineon + * - Xtensa + - :ref:`Yes<Xtensa-System-emulator>` + - Yes + - A configurable 32 bit soft core now owned by Cadence + +A number of features are are only available when running under +emulation including :ref:`Record/Replay<replay>` and :ref:`TCG Plugins`. + +.. _Semihosting: + +Semihosting +----------- + +Semihosting is a feature defined by the owner of the architecture to +allow programs to interact with a debugging host system. On real +hardware this is usually provided by an In-circuit emulator (ICE) +hooked directly to the board. QEMU's implementation allows for +semihosting calls to be passed to the host system or via the +``gdbstub``. + +Generally semihosting makes it easier to bring up low level code before a +more fully functional operating system has been enabled. On QEMU it +also allows for embedded micro-controller code which typically doesn't +have a full libc to be run as "bare-metal" code under QEMU's user-mode +emulation. It is also useful for writing test cases and indeed a +number of compiler suites as well as QEMU itself use semihosting calls +to exit test code while reporting the success state. + +Semihosting is only available using TCG emulation. This is because the +instructions to trigger a semihosting call are typically reserved +causing most hypervisors to trap and fault on them. + +.. warning:: + Semihosting inherently bypasses any isolation there may be between + the guest and the host. As a result a program using semihosting can + happily trash your host system. You should only ever run trusted + code with semihosting enabled. + +Redirection +~~~~~~~~~~~ + +Semihosting calls can be re-directed to a (potentially remote) gdb +during debugging via the :ref:`gdbstub<GDB usage>`. Output to the +semihosting console is configured as a ``chardev`` so can be +redirected to a file, pipe or socket like any other ``chardev`` +device. + +Supported Targets +~~~~~~~~~~~~~~~~~ + +Most targets offer similar semihosting implementations with some +minor changes to define the appropriate instruction to encode the +semihosting call and which registers hold the parameters. They tend to +presents a simple POSIX-like API which allows your program to read and +write files, access the console and some other basic interactions. + +For full details of the ABI for a particular target, and the set of +calls it provides, you should consult the semihosting specification +for that architecture. + +.. note:: + QEMU makes an implementation decision to implement all file + access in ``O_BINARY`` mode. The user-visible effect of this is + regardless of the text/binary mode the program sets QEMU will + always select a binary mode ensuring no line-terminator conversion + is performed on input or output. This is because gdb semihosting + support doesn't make the distinction between the modes and + magically processing line endings can be confusing. + +.. list-table:: Guest Architectures supporting Semihosting + :widths: 10 10 80 + :header-rows: 1 + + * - Architecture + - Modes + - Specification + * - Arm + - System and User-mode + - https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst + * - m68k + - System + - https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=libgloss/m68k/m68k-semi.txt;hb=HEAD + * - MIPS + - System + - Unified Hosting Interface (MD01069) + * - Nios II + - System + - https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD + * - RISC-V + - System and User-mode + - https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc + * - Xtensa + - System + - Tensilica ISS SIMCALL diff --git a/docs/about/index.rst b/docs/about/index.rst index 5bea653c07..b00b584b31 100644 --- a/docs/about/index.rst +++ b/docs/about/index.rst @@ -5,24 +5,25 @@ About QEMU QEMU is a generic and open source machine emulator and virtualizer. QEMU can be used in several different ways. The most common is for -"system emulation", where it provides a virtual model of an +:ref:`System Emulation`, where it provides a virtual model of an entire machine (CPU, memory and emulated devices) to run a guest OS. -In this mode the CPU may be fully emulated, or it may work with -a hypervisor such as KVM, Xen, Hax or Hypervisor.Framework to -allow the guest to run directly on the host CPU. +In this mode the CPU may be fully emulated, or it may work with a +hypervisor such as KVM, Xen, Hax or Hypervisor.Framework to allow the +guest to run directly on the host CPU. -The second supported way to use QEMU is "user mode emulation", +The second supported way to use QEMU is :ref:`User Mode Emulation`, where QEMU can launch processes compiled for one CPU on another CPU. In this mode the CPU is always emulated. -QEMU also provides a number of standalone commandline utilities, -such as the ``qemu-img`` disk image utility that allows you to create, -convert and modify disk images. +QEMU also provides a number of standalone :ref:`command line +utilities<Tools>`, such as the ``qemu-img`` disk image utility that +allows you to create, convert and modify disk images. .. toctree:: :maxdepth: 2 build-platforms + emulation deprecated removed-features license diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 6c3aa5097f..a17d0554d6 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -422,6 +422,13 @@ the value is hexadecimal. That is, '0x20M' should be written either as ``tty`` and ``parport`` used to be aliases for ``serial`` and ``parallel`` respectively. The actual backend names should be used instead. +``-drive if=none`` for the sifive_u OTP device (removed in 8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``-drive if=pflash`` to configure the OTP device of the sifive_u +RISC-V machine instead. + + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/conf.py b/docs/conf.py index e33cf3d381..73a287a4f2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -297,19 +297,6 @@ man_pages = [ ] man_make_section_directory = False -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'QEMU', u'QEMU Documentation', - author, 'QEMU', 'One line description of project.', - 'Miscellaneous'), -] - - - # We use paths starting from qemu_docdir here so that you can run # sphinx-build from anywhere and the kerneldoc extension can still # find everything. diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 9740a70406..81dcd43a61 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -3,6 +3,8 @@ Copyright (c) 2019, Linaro Limited Written by Emilio Cota and Alex Bennée +.. _TCG Plugins: + QEMU TCG Plugins ================ diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst index 135784ab33..691429c7af 100644 --- a/docs/interop/live-block-operations.rst +++ b/docs/interop/live-block-operations.rst @@ -4,6 +4,8 @@ This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. +.. _Live Block Operations: + ============================ Live Block Device Operations ============================ diff --git a/docs/interop/qemu-qmp-ref.rst b/docs/interop/qemu-qmp-ref.rst index 357effd64f..f94614a0b2 100644 --- a/docs/interop/qemu-qmp-ref.rst +++ b/docs/interop/qemu-qmp-ref.rst @@ -1,3 +1,5 @@ +.. _QMP Ref: + QEMU QMP Reference Manual ========================= diff --git a/docs/pcie.txt b/docs/pcie.txt index 89e3502075..df49178311 100644 --- a/docs/pcie.txt +++ b/docs/pcie.txt @@ -48,8 +48,8 @@ Place only the following kinds of devices directly on the Root Complex: strangely when PCI Express devices are integrated with the Root Complex. - (2) PCI Express Root Ports (ioh3420), for starting exclusively PCI Express - hierarchies. + (2) PCI Express Root Ports (pcie-root-port), for starting exclusively + PCI Express hierarchies. (3) PCI Express to PCI Bridge (pcie-pci-bridge), for starting legacy PCI hierarchies. @@ -70,7 +70,7 @@ Place only the following kinds of devices directly on the Root Complex: -device pxb-pcie,id=pcie.1,bus_nr=x[,numa_node=y][,addr=z] PCI Express Root Ports and PCI Express to PCI bridges can be connected to the pcie.1 bus: - -device ioh3420,id=root_port1[,bus=pcie.1][,chassis=x][,slot=y][,addr=z] \ + -device pcie-root-port,id=root_port1[,bus=pcie.1][,chassis=x][,slot=y][,addr=z] \ -device pcie-pci-bridge,id=pcie_pci_bridge1,bus=pcie.1 @@ -112,14 +112,14 @@ Plug only PCI Express devices into PCI Express Ports. ------------ 2.2.1 Plugging a PCI Express device into a PCI Express Root Port: - -device ioh3420,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ + -device pcie-root-port,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ -device <dev>,bus=root_port1 2.2.2 Using multi-function PCI Express Root Ports: - -device ioh3420,id=root_port1,multifunction=on,chassis=x,addr=z.0[,slot=y][,bus=pcie.0] \ - -device ioh3420,id=root_port2,chassis=x1,addr=z.1[,slot=y1][,bus=pcie.0] \ - -device ioh3420,id=root_port3,chassis=x2,addr=z.2[,slot=y2][,bus=pcie.0] \ + -device pcie-root-port,id=root_port1,multifunction=on,chassis=x,addr=z.0[,slot=y][,bus=pcie.0] \ + -device pcie-root-port,id=root_port2,chassis=x1,addr=z.1[,slot=y1][,bus=pcie.0] \ + -device pcie-root-port,id=root_port3,chassis=x2,addr=z.2[,slot=y2][,bus=pcie.0] \ 2.2.3 Plugging a PCI Express device into a Switch: - -device ioh3420,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ + -device pcie-root-port,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ -device x3130-upstream,id=upstream_port1,bus=root_port1[,addr=x] \ -device xio3130-downstream,id=downstream_port1,bus=upstream_port1,chassis=x1,slot=y1[,addr=z1]] \ -device <dev>,bus=downstream_port1 diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index b33d7c28dc..2062d71261 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -1,3 +1,5 @@ +.. _Arm Emulation: + A-profile CPU architecture support ================================== @@ -28,6 +30,7 @@ the following architecture extensions: - FEAT_ETS (Enhanced Translation Synchronization) - FEAT_EVT (Enhanced Virtualization Traps) - FEAT_FCMA (Floating-point complex number instructions) +- FEAT_FGT (Fine-Grained Traps) - FEAT_FHM (Floating-point half-precision multiplication instructions) - FEAT_FP16 (Half-precision floating-point data processing) - FEAT_FRINTTS (Floating-point to integer instructions) diff --git a/docs/system/index.rst b/docs/system/index.rst index e3695649c5..3605bbe1ce 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -1,3 +1,5 @@ +.. _System Emulation: + ---------------- System Emulation ---------------- @@ -10,7 +12,7 @@ or Hypervisor.Framework. .. toctree:: :maxdepth: 3 - quickstart + introduction invocation device-emulation keys diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst new file mode 100644 index 0000000000..c8a9fe6c1d --- /dev/null +++ b/docs/system/introduction.rst @@ -0,0 +1,220 @@ +Introduction +============ + +Virtualisation Accelerators +--------------------------- + +QEMU's system emulation provides a virtual model of a machine (CPU, +memory and emulated devices) to run a guest OS. It supports a number +of hypervisors (known as accelerators) as well as a JIT known as the +Tiny Code Generator (TCG) capable of emulating many CPUs. + +.. list-table:: Supported Accelerators + :header-rows: 1 + + * - Accelerator + - Host OS + - Host Architectures + * - KVM + - Linux + - Arm (64 bit only), MIPS, PPC, RISC-V, s390x, x86 + * - Xen + - Linux (as dom0) + - Arm, x86 + * - Intel HAXM (hax) + - Linux, Windows + - x86 + * - Hypervisor Framework (hvf) + - MacOS + - x86 (64 bit only), Arm (64 bit only) + * - Windows Hypervisor Platform (wphx) + - Windows + - x86 + * - NetBSD Virtual Machine Monitor (nvmm) + - NetBSD + - x86 + * - Tiny Code Generator (tcg) + - Linux, other POSIX, Windows, MacOS + - Arm, x86, Loongarch64, MIPS, PPC, s390x, Sparc64 + +Feature Overview +---------------- + +System emulation provides a wide range of device models to emulate +various hardware components you may want to add to your machine. This +includes a wide number of VirtIO devices which are specifically tuned +for efficient operation under virtualisation. Some of the device +emulation can be offloaded from the main QEMU process using either +vhost-user (for VirtIO) or :ref:`Multi-process QEMU`. If the platform +supports it QEMU also supports directly passing devices through to +guest VMs to eliminate the device emulation overhead. See +:ref:`device-emulation` for more details. + +There is a full :ref:`featured block layer<Live Block Operations>` +which allows for construction of complex storage topology which can be +stacked across multiple layers supporting redirection, networking, +snapshots and migration support. + +The flexible ``chardev`` system allows for handling IO from character +like devices using stdio, files, unix sockets and TCP networking. + +QEMU provides a number of management interfaces including a line based +:ref:`Human Monitor Protocol (HMP)<QEMU monitor>` that allows you to +dynamically add and remove devices as well as introspect the system +state. The :ref:`QEMU Monitor Protocol<QMP Ref>` (QMP) is a well +defined, versioned, machine usable API that presents a rich interface +to other tools to create, control and manage Virtual Machines. This is +the interface used by higher level tools interfaces such as `Virt +Manager <https://virt-manager.org/>`_ using the `libvirt framework +<https://libvirt.org>`_. + +For the common accelerators QEMU, supported debugging with its +:ref:`gdbstub<GDB usage>` which allows users to connect GDB and debug +system software images. + +Running +------- + +QEMU provides a rich and complex API which can be overwhelming to +understand. While some architectures can boot something with just a +disk image, those examples elide a lot of details with defaults that +may not be optimal for modern systems. + +For a non-x86 system where we emulate a broad range of machine types, +the command lines are generally more explicit in defining the machine +and boot behaviour. You will find often find example command lines in +the :ref:`system-targets-ref` section of the manual. + +While the project doesn't want to discourage users from using the +command line to launch VMs, we do want to highlight that there are a +number of projects dedicated to providing a more user friendly +experience. Those built around the ``libvirt`` framework can make use +of feature probing to build modern VM images tailored to run on the +hardware you have. + +That said, the general form of a QEMU command line can be expressed +as: + +.. parsed-literal:: + + $ |qemu_system| [machine opts] \\ + [cpu opts] \\ + [accelerator opts] \\ + [device opts] \\ + [backend opts] \\ + [interface opts] \\ + [boot opts] + +Most options will generate some help information. So for example: + +.. parsed-literal:: + + $ |qemu_system| -M help + +will list the machine types supported by that QEMU binary. ``help`` +can also be passed as an argument to another option. For example: + +.. parsed-literal:: + + $ |qemu_system| -device scsi-hd,help + +will list the arguments and their default values of additional options +that can control the behaviour of the ``scsi-hd`` device. + +.. list-table:: Options Overview + :header-rows: 1 + :widths: 10, 90 + + * - Options + - + * - Machine + - Define the machine type, amount of memory etc + * - CPU + - Type and number/topology of vCPUs. Most accelerators offer + a ``host`` cpu option which simply passes through your host CPU + configuration without filtering out any features. + * - Accelerator + - This will depend on the hypervisor you run. Note that the + default is TCG, which is purely emulated, so you must specify an + accelerator type to take advantage of hardware virtualization. + * - Devices + - Additional devices that are not defined by default with the + machine type. + * - Backends + - Backends are how QEMU deals with the guest's data, for example + how a block device is stored, how network devices see the + network or how a serial device is directed to the outside world. + * - Interfaces + - How the system is displayed, how it is managed and controlled or + debugged. + * - Boot + - How the system boots, via firmware or direct kernel boot. + +In the following example we first define a ``virt`` machine which is a +general purpose platform for running Aarch64 guests. We enable +virtualisation so we can use KVM inside the emulated guest. As the +``virt`` machine comes with some built in pflash devices we give them +names so we can override the defaults later. + +.. code:: + + $ qemu-system-aarch64 \ + -machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars \ + -m 4096 \ + +We then define the 4 vCPUs using the ``max`` option which gives us all +the Arm features QEMU is capable of emulating. We enable a more +emulation friendly implementation of Arm's pointer authentication +algorithm. We explicitly specify TCG acceleration even though QEMU +would default to it anyway. + +.. code:: + + -cpu max,pauth-impdef=on \ + -smp 4 \ + -accel tcg \ + +As the ``virt`` platform doesn't have any default network or storage +devices we need to define them. We give them ids so we can link them +with the backend later on. + +.. code:: + + -device virtio-net-pci,netdev=unet \ + -device virtio-scsi-pci \ + -device scsi-hd,drive=hd \ + +We connect the user-mode networking to our network device. As +user-mode networking isn't directly accessible from the outside world +we forward localhost port 2222 to the ssh port on the guest. + +.. code:: + + -netdev user,id=unet,hostfwd=tcp::2222-:22 \ + +We connect the guest visible block device to an LVM partition we have +set aside for our guest. + +.. code:: + + -blockdev driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/lvm-disk/debian-bullseye-arm64 \ + +We then tell QEMU to multiplex the :ref:`QEMU monitor` with the serial +port output (we can switch between the two using :ref:`keys in the +character backend multiplexer`). As there is no default graphical +device we disable the display as we can work entirely in the terminal. + +.. code:: + + -serial mon:stdio \ + -display none \ + +Finally we override the default firmware to ensure we have some +storage for EFI to persist its configuration. That firmware is +responsible for finding the disk, booting grub and eventually running +our system. + +.. code:: + + -blockdev node-name=rom,driver=file,filename=(pwd)/pc-bios/edk2-aarch64-code.fd,read-only=true \ + -blockdev node-name=efivars,driver=file,filename=$HOME/images/qemu-arm64-efivars diff --git a/docs/system/multi-process.rst b/docs/system/multi-process.rst index 210531ee17..16f0352416 100644 --- a/docs/system/multi-process.rst +++ b/docs/system/multi-process.rst @@ -1,3 +1,5 @@ +.. _Multi-process QEMU: + Multi-process QEMU ================== diff --git a/docs/system/quickstart.rst b/docs/system/quickstart.rst deleted file mode 100644 index 681678c86e..0000000000 --- a/docs/system/quickstart.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _pcsys_005fquickstart: - -Quick Start ------------ - -Download and uncompress a PC hard disk image with Linux installed (e.g. -``linux.img``) and type: - -.. parsed-literal:: - - |qemu_system| linux.img - -Linux should boot and give you a prompt. - -Users should be aware the above example elides a lot of the complexity -of setting up a VM with x86_64 specific defaults and assumes the -first non switch argument is a PC compatible disk image with a boot -sector. For a non-x86 system where we emulate a broad range of machine -types, the command lines are generally more explicit in defining the -machine and boot behaviour. You will find more example command lines -in the :ref:`system-targets-ref` section of the manual. diff --git a/docs/system/s390x/pcidevices.rst b/docs/system/s390x/pcidevices.rst new file mode 100644 index 0000000000..628effa2f4 --- /dev/null +++ b/docs/system/s390x/pcidevices.rst @@ -0,0 +1,41 @@ +PCI devices on s390x +==================== + +PCI devices on s390x work differently than on other architectures and need to +be configured in a slightly different way. + +Every PCI device is linked with an additional ``zpci`` device. +While the ``zpci`` device will be autogenerated if not specified, it is +recommended to specify it explicitly so that you can pass s390-specific +PCI configuration. + +For example, in order to pass a PCI device ``0000:00:00.0`` through to the +guest, you would specify:: + + qemu-system-s390x ... \ + -device zpci,uid=1,fid=0,target=hostdev0,id=zpci1 \ + -device vfio-pci,host=0000:00:00.0,id=hostdev0 + +Here, the zpci device is joined with the PCI device via the ``target`` property. + +Note that we don't set bus, slot or function here for the guest as is common in +other PCI implementations. Topology information is not available on s390x, and +the guest will not see any of the bus, slot or function information specified +on the command line. + +Instead, ``uid`` and ``fid`` determine how the device is presented to the guest +operating system. + +In case of Linux, ``uid`` will be used in the ``domain`` part of the PCI +identifier, and ``fid`` identifies the physical slot, i.e.:: + + qemu-system-s390x ... \ + -device zpci,uid=7,fid=8,target=hostdev0,id=zpci1 \ + ... + +will be presented in the guest as:: + + # lspci -v + 0007:00:00.0 ... + Physical Slot: 00000008 + ... diff --git a/docs/system/target-s390x.rst b/docs/system/target-s390x.rst index c636f64113..f6f11433c7 100644 --- a/docs/system/target-s390x.rst +++ b/docs/system/target-s390x.rst @@ -26,6 +26,7 @@ or vfio-ap is also available. s390x/css s390x/3270 s390x/vfio-ccw + s390x/pcidevices Architectural features ====================== diff --git a/docs/tools/index.rst b/docs/tools/index.rst index 1edd5a8054..2151adcf78 100644 --- a/docs/tools/index.rst +++ b/docs/tools/index.rst @@ -1,3 +1,5 @@ +.. _Tools: + ----- Tools ----- diff --git a/docs/user/index.rst b/docs/user/index.rst index 2c4e29f3db..782d27cda2 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -1,3 +1,5 @@ +.. _User Mode Emulation: + ------------------- User Mode Emulation ------------------- diff --git a/hmp-commands.hx b/hmp-commands.hx index 673e39a697..fbb5daf09b 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -11,7 +11,7 @@ HXCOMM HXCOMM can be used for comments, discarded from both rST and C. .args_type = "name:S?", .params = "[cmd]", .help = "show the help", - .cmd = do_help_cmd, + .cmd = hmp_help, .flags = "p", }, @@ -563,7 +563,7 @@ ERST .args_type = "fmt:/,val:l", .params = "/fmt expr", .help = "print expression value (use $reg for CPU register access)", - .cmd = do_print, + .cmd = hmp_print, }, SRST diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 1f7803fdab..e07d3204eb 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -39,6 +39,10 @@ config ACPI_PCIHP bool depends on ACPI +config ACPI_PCI_BRIDGE + bool + depends on ACPI && PCI && ACPI_PCIHP + config ACPI_HMAT bool depends on ACPI diff --git a/hw/acpi/acpi-qmp-cmds.c b/hw/acpi/acpi-qmp-cmds.c new file mode 100644 index 0000000000..2d47cac52c --- /dev/null +++ b/hw/acpi/acpi-qmp-cmds.c @@ -0,0 +1,30 @@ +/* + * QMP commands related to ACPI + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "hw/acpi/acpi_dev_interface.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-acpi.h" + +ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp) +{ + bool ambig; + ACPIOSTInfoList *head = NULL; + ACPIOSTInfoList **prev = &head; + Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, &ambig); + + if (obj) { + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); + AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj); + + adevc->ospm_status(adev, &prev); + } else { + error_setg(errp, "command is not supported, missing ACPI device"); + } + + return head; +} diff --git a/hw/acpi/acpi-x86-stub.c b/hw/acpi/acpi-x86-stub.c index 3df1e090f4..d0d399d26b 100644 --- a/hw/acpi/acpi-x86-stub.c +++ b/hw/acpi/acpi-x86-stub.c @@ -2,9 +2,8 @@ #include "hw/i386/pc.h" #include "hw/i386/acpi-build.h" -void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, - bool force_enabled) +void pc_madt_cpu_entry(int uid, const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled) { } diff --git a/hw/acpi/acpi_interface.c b/hw/acpi/acpi_interface.c index c668d361f6..8637ff18fc 100644 --- a/hw/acpi/acpi_interface.c +++ b/hw/acpi/acpi_interface.c @@ -2,6 +2,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/acpi/acpi_aml_interface.h" #include "qemu/module.h" +#include "qemu/queue.h" void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event) { @@ -12,6 +13,15 @@ void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event) } } +void qbus_build_aml(BusState *bus, Aml *scope) +{ + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + call_dev_aml_func(DEVICE(kid->child), scope); + } +} + static void register_types(void) { static const TypeInfo acpi_dev_if_info = { diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 4e580959a2..19c154d78f 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -355,7 +355,6 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, char *cphp_res_path = g_strdup_printf("%s." CPUHP_RES_DEVICE, res_root); Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); - AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj); cpu_ctrl_dev = aml_device("%s", cphp_res_path); { @@ -666,7 +665,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, /* build _MAT object */ assert(adevc && adevc->madt_cpu); - adevc->madt_cpu(adev, i, arch_ids, madt_buf, + adevc->madt_cpu(i, arch_ids, madt_buf, true); /* set enabled flag */ aml_append(dev, aml_name_decl("_MAT", aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data))); diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index 30054a8cdc..e0bf39bf4c 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -19,6 +19,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device acpi_ss.add(when: 'CONFIG_ACPI_HMAT', if_true: files('hmat.c')) acpi_ss.add(when: 'CONFIG_ACPI_APEI', if_true: files('ghes.c'), if_false: files('ghes-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_PIIX4', if_true: files('piix4.c')) +acpi_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_true: files('pci-bridge.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: files('acpi-pci-hotplug-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_VIOT', if_true: files('viot.c')) @@ -30,9 +31,11 @@ if have_tpm acpi_ss.add(files('tpm.c')) endif softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c')) +softmmu_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-stub.c')) softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c', 'acpi-x86-stub.c', 'ipmi-stub.c', 'ghes-stub.c', 'acpi-mem-hotplug-stub.c', 'acpi-cpu-hotplug-stub.c', 'acpi-pci-hotplug-stub.c', 'acpi-nvdimm-stub.c', - 'cxl-stub.c')) + 'cxl-stub.c', 'pci-bridge-stub.c')) +softmmu_ss.add(files('acpi-qmp-cmds.c')) diff --git a/hw/acpi/pci-bridge-stub.c b/hw/acpi/pci-bridge-stub.c new file mode 100644 index 0000000000..9d78638c48 --- /dev/null +++ b/hw/acpi/pci-bridge-stub.c @@ -0,0 +1,20 @@ +/* + * QEMU ACPI PCI bridge stub + * + * Copyright (c) 2023 Red Hat, Inc. + * + * Author: + * Igor Mammedov <imammedo@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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/acpi/pci.h" + +void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope) +{ +} diff --git a/hw/acpi/pci-bridge.c b/hw/acpi/pci-bridge.c new file mode 100644 index 0000000000..5f3ee5157f --- /dev/null +++ b/hw/acpi/pci-bridge.c @@ -0,0 +1,27 @@ +/* + * QEMU ACPI PCI bridge + * + * Copyright (c) 2023 Red Hat, Inc. + * + * Author: + * Igor Mammedov <imammedo@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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/acpi/pci.h" +#include "hw/pci/pci_bridge.h" +#include "hw/acpi/pcihp.h" + +void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + PCIBridge *br = PCI_BRIDGE(adev); + + if (object_property_find(OBJECT(&br->sec_bus), ACPI_PCIHP_PROP_BSEL)) { + build_append_pci_bus_devices(scope, pci_bridge_get_sec_bus(br)); + } +} diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 99a898d9ae..5dc7377411 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -85,31 +85,40 @@ static int acpi_pcihp_get_bsel(PCIBus *bus) } } -/* Assign BSEL property to all buses. In the future, this can be changed - * to only assign to buses that support hotplug. - */ +typedef struct { + unsigned bsel_alloc; + bool has_bridge_hotplug; +} BSELInfo; + +/* Assign BSEL property only to buses that support hotplug. */ static void *acpi_set_bsel(PCIBus *bus, void *opaque) { - unsigned *bsel_alloc = opaque; + BSELInfo *info = opaque; unsigned *bus_bsel; + DeviceState *br = bus->qbus.parent; + bool is_bridge = IS_PCI_BRIDGE(br); + /* hotplugged bridges can't be described in ACPI ignore them */ if (qbus_is_hotpluggable(BUS(bus))) { - bus_bsel = g_malloc(sizeof *bus_bsel); + if (!is_bridge || (!br->hotplugged && info->has_bridge_hotplug)) { + bus_bsel = g_malloc(sizeof *bus_bsel); - *bus_bsel = (*bsel_alloc)++; - object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, OBJ_PROP_FLAG_READ); + *bus_bsel = info->bsel_alloc++; + object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, + bus_bsel, OBJ_PROP_FLAG_READ); + } } - return bsel_alloc; + return info; } -static void acpi_set_pci_info(void) +static void acpi_set_pci_info(bool has_bridge_hotplug) { static bool bsel_is_set; Object *host = acpi_get_i386_pci_host(); PCIBus *bus; - unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT; + BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT, + .has_bridge_hotplug = has_bridge_hotplug }; if (bsel_is_set) { return; @@ -123,7 +132,7 @@ static void acpi_set_pci_info(void) bus = PCI_HOST_BRIDGE(host)->bus; if (bus) { /* Scan all PCI buses. Set property to enable acpi based hotplug. */ - pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); + pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info); } } @@ -287,7 +296,7 @@ void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off) if (acpihp_root_off) { acpi_pcihp_disable_root_bus(); } - acpi_set_pci_info(); + acpi_set_pci_info(!s->legacy_piix); acpi_pcihp_update(s); } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 0a81f1ad93..724294b378 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "hw/i386/pc.h" -#include "hw/southbridge/piix.h" #include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/i2c/pm_smbus.h" @@ -305,7 +304,9 @@ static void piix4_pm_reset(DeviceState *dev) acpi_update_sci(&s->ar, s->irq); pm_io_space_update(s); - acpi_pcihp_reset(&s->acpi_pci_hotplug, !s->use_acpi_root_pci_hotplug); + if (s->use_acpi_hotplug_bridge || s->use_acpi_root_pci_hotplug) { + acpi_pcihp_reset(&s->acpi_pci_hotplug, !s->use_acpi_root_pci_hotplug); + } } static void piix4_pm_powerdown_req(Notifier *n, void *opaque) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 4bb444684f..f778cb6d09 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -29,6 +29,7 @@ #include "exec/hwaddr.h" #include "kvm_arm.h" #include "hw/arm/boot.h" +#include "hw/arm/smmuv3.h" #include "hw/block/flash.h" #include "hw/boards.h" #include "hw/ide/internal.h" @@ -145,7 +146,6 @@ static const int sbsa_ref_irqmap[] = { static const char * const valid_cpus[] = { ARM_CPU_TYPE_NAME("cortex-a57"), ARM_CPU_TYPE_NAME("cortex-a72"), - ARM_CPU_TYPE_NAME("cortex-a76"), ARM_CPU_TYPE_NAME("neoverse-n1"), ARM_CPU_TYPE_NAME("max"), }; @@ -574,7 +574,7 @@ static void create_smmu(const SBSAMachineState *sms, PCIBus *bus) DeviceState *dev; int i; - dev = qdev_new("arm-smmuv3"); + dev = qdev_new(TYPE_ARM_SMMUV3); object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 54186f31cb..733c964778 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -535,7 +535,8 @@ static void smmu_base_reset_hold(Object *obj) static Property smmu_dev_properties[] = { DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), - DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, "PCI", PCIBus *), + DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, + TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ea2413a0ba..ba47728288 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -47,8 +47,10 @@ #include "sysemu/numa.h" #include "sysemu/runstate.h" #include "sysemu/tpm.h" +#include "sysemu/tcg.h" #include "sysemu/kvm.h" #include "sysemu/hvf.h" +#include "sysemu/qtest.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" @@ -1343,7 +1345,7 @@ static void create_smmu(const VirtMachineState *vms, return; } - dev = qdev_new("arm-smmuv3"); + dev = qdev_new(TYPE_ARM_SMMUV3); object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); @@ -1820,6 +1822,84 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) } } +static VirtGICType finalize_gic_version_do(const char *accel_name, + VirtGICType gic_version, + int gics_supported, + unsigned int max_cpus) +{ + /* Convert host/max/nosel to GIC version number */ + switch (gic_version) { + case VIRT_GIC_VERSION_HOST: + if (!kvm_enabled()) { + error_report("gic-version=host requires KVM"); + exit(1); + } + + /* For KVM, gic-version=host means gic-version=max */ + return finalize_gic_version_do(accel_name, VIRT_GIC_VERSION_MAX, + gics_supported, max_cpus); + case VIRT_GIC_VERSION_MAX: + if (gics_supported & VIRT_GIC_VERSION_4_MASK) { + gic_version = VIRT_GIC_VERSION_4; + } else if (gics_supported & VIRT_GIC_VERSION_3_MASK) { + gic_version = VIRT_GIC_VERSION_3; + } else { + gic_version = VIRT_GIC_VERSION_2; + } + break; + case VIRT_GIC_VERSION_NOSEL: + if ((gics_supported & VIRT_GIC_VERSION_2_MASK) && + max_cpus <= GIC_NCPU) { + gic_version = VIRT_GIC_VERSION_2; + } else if (gics_supported & VIRT_GIC_VERSION_3_MASK) { + /* + * in case the host does not support v2 emulation or + * the end-user requested more than 8 VCPUs we now default + * to v3. In any case defaulting to v2 would be broken. + */ + gic_version = VIRT_GIC_VERSION_3; + } else if (max_cpus > GIC_NCPU) { + error_report("%s only supports GICv2 emulation but more than 8 " + "vcpus are requested", accel_name); + exit(1); + } + break; + case VIRT_GIC_VERSION_2: + case VIRT_GIC_VERSION_3: + case VIRT_GIC_VERSION_4: + break; + } + + /* Check chosen version is effectively supported */ + switch (gic_version) { + case VIRT_GIC_VERSION_2: + if (!(gics_supported & VIRT_GIC_VERSION_2_MASK)) { + error_report("%s does not support GICv2 emulation", accel_name); + exit(1); + } + break; + case VIRT_GIC_VERSION_3: + if (!(gics_supported & VIRT_GIC_VERSION_3_MASK)) { + error_report("%s does not support GICv3 emulation", accel_name); + exit(1); + } + break; + case VIRT_GIC_VERSION_4: + if (!(gics_supported & VIRT_GIC_VERSION_4_MASK)) { + error_report("%s does not support GICv4 emulation, is virtualization=on?", + accel_name); + exit(1); + } + break; + default: + error_report("logic error in finalize_gic_version"); + exit(1); + break; + } + + return gic_version; +} + /* * finalize_gic_version - Determines the final gic_version * according to the gic-version property @@ -1828,118 +1908,49 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) */ static void finalize_gic_version(VirtMachineState *vms) { + const char *accel_name = current_accel_name(); unsigned int max_cpus = MACHINE(vms)->smp.max_cpus; + int gics_supported = 0; - if (kvm_enabled()) { - int probe_bitmap; - - if (!kvm_irqchip_in_kernel()) { - switch (vms->gic_version) { - case VIRT_GIC_VERSION_HOST: - warn_report( - "gic-version=host not relevant with kernel-irqchip=off " - "as only userspace GICv2 is supported. Using v2 ..."); - return; - case VIRT_GIC_VERSION_MAX: - case VIRT_GIC_VERSION_NOSEL: - vms->gic_version = VIRT_GIC_VERSION_2; - return; - case VIRT_GIC_VERSION_2: - return; - case VIRT_GIC_VERSION_3: - 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); - } - } + /* Determine which GIC versions the current environment supports */ + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + int probe_bitmap = kvm_arm_vgic_probe(); - probe_bitmap = kvm_arm_vgic_probe(); if (!probe_bitmap) { error_report("Unable to determine GIC version supported by host"); exit(1); } - switch (vms->gic_version) { - case VIRT_GIC_VERSION_HOST: - case VIRT_GIC_VERSION_MAX: - if (probe_bitmap & KVM_ARM_VGIC_V3) { - vms->gic_version = VIRT_GIC_VERSION_3; - } else { - vms->gic_version = VIRT_GIC_VERSION_2; - } - return; - case VIRT_GIC_VERSION_NOSEL: - if ((probe_bitmap & KVM_ARM_VGIC_V2) && max_cpus <= GIC_NCPU) { - vms->gic_version = VIRT_GIC_VERSION_2; - } else if (probe_bitmap & KVM_ARM_VGIC_V3) { - /* - * in case the host does not support v2 in-kernel emulation or - * the end-user requested more than 8 VCPUs we now default - * to v3. In any case defaulting to v2 would be broken. - */ - vms->gic_version = VIRT_GIC_VERSION_3; - } else if (max_cpus > GIC_NCPU) { - error_report("host only supports in-kernel GICv2 emulation " - "but more than 8 vcpus are requested"); - exit(1); - } - break; - 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); + if (probe_bitmap & KVM_ARM_VGIC_V2) { + gics_supported |= VIRT_GIC_VERSION_2_MASK; } - - /* Check chosen version is effectively supported by the host */ - if (vms->gic_version == VIRT_GIC_VERSION_2 && - !(probe_bitmap & KVM_ARM_VGIC_V2)) { - error_report("host does not support in-kernel GICv2 emulation"); - exit(1); - } else if (vms->gic_version == VIRT_GIC_VERSION_3 && - !(probe_bitmap & KVM_ARM_VGIC_V3)) { - error_report("host does not support in-kernel GICv3 emulation"); - exit(1); + if (probe_bitmap & KVM_ARM_VGIC_V3) { + gics_supported |= VIRT_GIC_VERSION_3_MASK; } - return; - } - - /* TCG mode */ - switch (vms->gic_version) { - case VIRT_GIC_VERSION_NOSEL: - vms->gic_version = VIRT_GIC_VERSION_2; - break; - case VIRT_GIC_VERSION_MAX: + } else if (kvm_enabled() && !kvm_irqchip_in_kernel()) { + /* KVM w/o kernel irqchip can only deal with GICv2 */ + gics_supported |= VIRT_GIC_VERSION_2_MASK; + accel_name = "KVM with kernel-irqchip=off"; + } else if (tcg_enabled() || hvf_enabled() || qtest_enabled()) { + gics_supported |= VIRT_GIC_VERSION_2_MASK; if (module_object_class_by_name("arm-gicv3")) { - /* CONFIG_ARM_GICV3_TCG was set */ + gics_supported |= VIRT_GIC_VERSION_3_MASK; 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; + gics_supported |= VIRT_GIC_VERSION_4_MASK; } - } else { - vms->gic_version = VIRT_GIC_VERSION_2; } - break; - case VIRT_GIC_VERSION_HOST: - error_report("gic-version=host requires KVM"); + } else { + error_report("Unsupported accelerator, can not determine GIC support"); 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; } + + /* + * Then convert helpers like host/max to concrete GIC versions and ensure + * the desired version is supported + */ + vms->gic_version = finalize_gic_version_do(accel_name, vms->gic_version, + gics_supported, max_cpus); } /* diff --git a/hw/block/block.c b/hw/block/block.c index ddcef71f80..af0710e477 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -16,6 +16,40 @@ #include "qapi/qapi-types-block.h" /* + * Read the non-zeroes parts of @blk into @buf + * Reading all of the @blk is expensive if the zeroes parts of @blk + * is large enough. Therefore check the block status and only write + * the non-zeroes block into @buf. + * + * Return 0 on success, non-zero on error. + */ +static int blk_pread_nonzeroes(BlockBackend *blk, hwaddr size, void *buf) +{ + int ret; + int64_t bytes, offset = 0; + BlockDriverState *bs = blk_bs(blk); + + for (;;) { + bytes = MIN(size - offset, BDRV_REQUEST_MAX_SECTORS); + if (bytes <= 0) { + return 0; + } + ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); + if (ret < 0) { + return ret; + } + if (!(ret & BDRV_BLOCK_ZERO)) { + ret = bdrv_pread(bs->file, offset, bytes, + (uint8_t *) buf + offset, 0); + if (ret < 0) { + return ret; + } + } + offset += bytes; + } +} + +/* * Read the entire contents of @blk into @buf. * @blk's contents must be @size bytes, and @size must be at most * BDRV_REQUEST_MAX_BYTES. @@ -54,7 +88,7 @@ bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, * block device and read only on demand. */ assert(size <= BDRV_REQUEST_MAX_BYTES); - ret = blk_pread(blk, 0, size, buf, 0); + ret = blk_pread_nonzeroes(blk, size, buf); if (ret < 0) { error_setg_errno(errp, -ret, "can't read block backend"); return false; diff --git a/hw/char/pl011.c b/hw/char/pl011.c index c076813423..c15cb7af20 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -81,6 +81,27 @@ static void pl011_update(PL011State *s) } } +static bool pl011_is_fifo_enabled(PL011State *s) +{ + return (s->lcr & 0x10) != 0; +} + +static inline unsigned pl011_get_fifo_depth(PL011State *s) +{ + /* Note: FIFO depth is expected to be power-of-2 */ + return pl011_is_fifo_enabled(s) ? PL011_FIFO_DEPTH : 1; +} + +static inline void pl011_reset_fifo(PL011State *s) +{ + s->read_count = 0; + s->read_pos = 0; + + /* Reset FIFO flags */ + s->flags &= ~(PL011_FLAG_RXFF | PL011_FLAG_TXFF); + s->flags |= PL011_FLAG_RXFE | PL011_FLAG_TXFE; +} + static uint64_t pl011_read(void *opaque, hwaddr offset, unsigned size) { @@ -94,8 +115,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset, c = s->read_fifo[s->read_pos]; if (s->read_count > 0) { s->read_count--; - if (++s->read_pos == 16) - s->read_pos = 0; + s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1); } if (s->read_count == 0) { s->flags |= PL011_FLAG_RXFE; @@ -229,8 +249,7 @@ static void pl011_write(void *opaque, hwaddr offset, case 11: /* UARTLCR_H */ /* Reset the FIFO state on FIFO enable or disable */ if ((s->lcr ^ value) & 0x10) { - s->read_count = 0; - s->read_pos = 0; + pl011_reset_fifo(s); } if ((s->lcr ^ value) & 0x1) { int break_enable = value & 0x1; @@ -273,11 +292,7 @@ static int pl011_can_receive(void *opaque) PL011State *s = (PL011State *)opaque; int r; - if (s->lcr & 0x10) { - r = s->read_count < 16; - } else { - r = s->read_count < 1; - } + r = s->read_count < pl011_get_fifo_depth(s); trace_pl011_can_receive(s->lcr, s->read_count, r); return r; } @@ -286,15 +301,15 @@ static void pl011_put_fifo(void *opaque, uint32_t value) { PL011State *s = (PL011State *)opaque; int slot; + unsigned pipe_depth; - slot = s->read_pos + s->read_count; - if (slot >= 16) - slot -= 16; + pipe_depth = pl011_get_fifo_depth(s); + slot = (s->read_pos + s->read_count) & (pipe_depth - 1); s->read_fifo[slot] = value; s->read_count++; s->flags &= ~PL011_FLAG_RXFE; trace_pl011_put_fifo(value, s->read_count); - if (!(s->lcr & 0x10) || s->read_count == 16) { + if (s->read_count == pipe_depth) { trace_pl011_put_fifo_full(); s->flags |= PL011_FLAG_RXFF; } @@ -346,10 +361,35 @@ static const VMStateDescription vmstate_pl011_clock = { } }; +static int pl011_post_load(void *opaque, int version_id) +{ + PL011State* s = opaque; + + /* Sanity-check input state */ + if (s->read_pos >= ARRAY_SIZE(s->read_fifo) || + s->read_count > ARRAY_SIZE(s->read_fifo)) { + return -1; + } + + if (!pl011_is_fifo_enabled(s) && s->read_count > 0 && s->read_pos > 0) { + /* + * Older versions of PL011 didn't ensure that the single + * character in the FIFO in FIFO-disabled mode is in + * element 0 of the array; convert to follow the current + * code's assumptions. + */ + s->read_fifo[0] = s->read_fifo[s->read_pos]; + s->read_pos = 0; + } + + return 0; +} + static const VMStateDescription vmstate_pl011 = { .name = "pl011", .version_id = 2, .minimum_version_id = 2, + .post_load = pl011_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(readbuff, PL011State), VMSTATE_UINT32(flags, PL011State), @@ -359,7 +399,7 @@ static const VMStateDescription vmstate_pl011 = { VMSTATE_UINT32(dmacr, PL011State), VMSTATE_UINT32(int_enabled, PL011State), VMSTATE_UINT32(int_level, PL011State), - VMSTATE_UINT32_ARRAY(read_fifo, PL011State, 16), + VMSTATE_UINT32_ARRAY(read_fifo, PL011State, PL011_FIFO_DEPTH), VMSTATE_UINT32(ilpr, PL011State), VMSTATE_UINT32(ibrd, PL011State), VMSTATE_UINT32(fbrd, PL011State), @@ -396,11 +436,6 @@ static void pl011_init(Object *obj) s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s, ClockUpdate); - s->read_trigger = 1; - s->ifl = 0x12; - s->cr = 0x300; - s->flags = 0x90; - s->id = pl011_id_arm; } @@ -412,11 +447,31 @@ static void pl011_realize(DeviceState *dev, Error **errp) pl011_event, NULL, s, NULL, true); } +static void pl011_reset(DeviceState *dev) +{ + PL011State *s = PL011(dev); + + s->lcr = 0; + s->rsr = 0; + s->dmacr = 0; + s->int_enabled = 0; + s->int_level = 0; + s->ilpr = 0; + s->ibrd = 0; + s->fbrd = 0; + s->read_trigger = 1; + s->ifl = 0x12; + s->cr = 0x300; + s->flags = 0; + pl011_reset_fifo(s); +} + static void pl011_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pl011_realize; + dc->reset = pl011_reset; dc->vmsd = &vmstate_pl011; device_class_set_props(dc, pl011_properties); } diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index a1a51e9778..c3e55ef9e9 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -134,3 +134,211 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) qapi_free_MemdevList(memdev_list); hmp_handle_error(mon, err); } + +void hmp_info_kvm(Monitor *mon, const QDict *qdict) +{ + KvmInfo *info; + + info = qmp_query_kvm(NULL); + monitor_printf(mon, "kvm support: "); + if (info->present) { + monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled"); + } else { + monitor_printf(mon, "not compiled\n"); + } + + qapi_free_KvmInfo(info); +} + +void hmp_info_uuid(Monitor *mon, const QDict *qdict) +{ + UuidInfo *info; + + info = qmp_query_uuid(NULL); + monitor_printf(mon, "%s\n", info->UUID); + qapi_free_UuidInfo(info); +} + +void hmp_info_balloon(Monitor *mon, const QDict *qdict) +{ + BalloonInfo *info; + Error *err = NULL; + + info = qmp_query_balloon(&err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); + + qapi_free_BalloonInfo(info); +} + +void hmp_system_reset(Monitor *mon, const QDict *qdict) +{ + qmp_system_reset(NULL); +} + +void hmp_system_powerdown(Monitor *mon, const QDict *qdict) +{ + qmp_system_powerdown(NULL); +} + +void hmp_memsave(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *filename = qdict_get_str(qdict, "filename"); + uint64_t addr = qdict_get_int(qdict, "val"); + Error *err = NULL; + int cpu_index = monitor_get_cpu_index(mon); + + if (cpu_index < 0) { + monitor_printf(mon, "No CPU available\n"); + return; + } + + qmp_memsave(addr, size, filename, true, cpu_index, &err); + hmp_handle_error(mon, err); +} + +void hmp_pmemsave(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *filename = qdict_get_str(qdict, "filename"); + uint64_t addr = qdict_get_int(qdict, "val"); + Error *err = NULL; + + qmp_pmemsave(addr, size, filename, &err); + hmp_handle_error(mon, err); +} + +void hmp_system_wakeup(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_system_wakeup(&err); + hmp_handle_error(mon, err); +} + +void hmp_nmi(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_inject_nmi(&err); + hmp_handle_error(mon, err); +} + +void hmp_balloon(Monitor *mon, const QDict *qdict) +{ + int64_t value = qdict_get_int(qdict, "value"); + Error *err = NULL; + + qmp_balloon(value, &err); + hmp_handle_error(mon, err); +} + +void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + MemoryDeviceInfoList *info_list = qmp_query_memory_devices(&err); + MemoryDeviceInfoList *info; + VirtioPMEMDeviceInfo *vpi; + VirtioMEMDeviceInfo *vmi; + MemoryDeviceInfo *value; + PCDIMMDeviceInfo *di; + SgxEPCDeviceInfo *se; + + for (info = info_list; info; info = info->next) { + value = info->value; + + if (value) { + switch (value->type) { + case MEMORY_DEVICE_INFO_KIND_DIMM: + case MEMORY_DEVICE_INFO_KIND_NVDIMM: + di = value->type == MEMORY_DEVICE_INFO_KIND_DIMM ? + value->u.dimm.data : value->u.nvdimm.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + di->id ? di->id : ""); + monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr); + monitor_printf(mon, " slot: %" PRId64 "\n", di->slot); + monitor_printf(mon, " node: %" PRId64 "\n", di->node); + monitor_printf(mon, " size: %" PRIu64 "\n", di->size); + monitor_printf(mon, " memdev: %s\n", di->memdev); + monitor_printf(mon, " hotplugged: %s\n", + di->hotplugged ? "true" : "false"); + monitor_printf(mon, " hotpluggable: %s\n", + di->hotpluggable ? "true" : "false"); + break; + case MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM: + vpi = value->u.virtio_pmem.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + vpi->id ? vpi->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vpi->memaddr); + monitor_printf(mon, " size: %" PRIu64 "\n", vpi->size); + monitor_printf(mon, " memdev: %s\n", vpi->memdev); + break; + case MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM: + vmi = value->u.virtio_mem.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + vmi->id ? vmi->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vmi->memaddr); + monitor_printf(mon, " node: %" PRId64 "\n", vmi->node); + monitor_printf(mon, " requested-size: %" PRIu64 "\n", + vmi->requested_size); + monitor_printf(mon, " size: %" PRIu64 "\n", vmi->size); + monitor_printf(mon, " max-size: %" PRIu64 "\n", vmi->max_size); + monitor_printf(mon, " block-size: %" PRIu64 "\n", + vmi->block_size); + monitor_printf(mon, " memdev: %s\n", vmi->memdev); + break; + case MEMORY_DEVICE_INFO_KIND_SGX_EPC: + se = value->u.sgx_epc.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + se->id ? se->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", se->memaddr); + monitor_printf(mon, " size: %" PRIu64 "\n", se->size); + monitor_printf(mon, " node: %" PRId64 "\n", se->node); + monitor_printf(mon, " memdev: %s\n", se->memdev); + break; + default: + g_assert_not_reached(); + } + } + } + + qapi_free_MemoryDeviceInfoList(info_list); + hmp_handle_error(mon, err); +} + +void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + GuidInfo *info = qmp_query_vm_generation_id(&err); + if (info) { + monitor_printf(mon, "%s\n", info->guid); + } + hmp_handle_error(mon, err); + qapi_free_GuidInfo(info); +} + +void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + MemoryInfo *info = qmp_query_memory_size_summary(&err); + if (info) { + monitor_printf(mon, "base memory: %" PRIu64 "\n", + info->base_memory); + + if (info->has_plugged_memory) { + monitor_printf(mon, "plugged memory: %" PRIu64 "\n", + info->plugged_memory); + } + + qapi_free_MemoryInfo(info); + } + hmp_handle_error(mon, err); +} diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 80d5e59651..44b5da8880 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -9,6 +9,9 @@ #include "qemu/osdep.h" #include "hw/boards.h" +#include "hw/intc/intc.h" +#include "hw/mem/memory-device.h" +#include "hw/rdma/rdma.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" @@ -17,11 +20,13 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" #include "qemu/main-loop.h" +#include "qemu/uuid.h" #include "qom/qom-qobject.h" #include "sysemu/hostmem.h" #include "sysemu/hw_accel.h" #include "sysemu/numa.h" #include "sysemu/runstate.h" +#include "sysemu/sysemu.h" static void cpustate_to_cpuinfo_s390(CpuInfoS390 *info, const CPUState *cpu) { @@ -239,3 +244,142 @@ HumanReadableText *qmp_x_query_numa(Error **errp) done: return human_readable_text_from_str(buf); } + +KvmInfo *qmp_query_kvm(Error **errp) +{ + KvmInfo *info = g_malloc0(sizeof(*info)); + + info->enabled = kvm_enabled(); + info->present = accel_find("kvm"); + + return info; +} + +UuidInfo *qmp_query_uuid(Error **errp) +{ + UuidInfo *info = g_malloc0(sizeof(*info)); + + info->UUID = qemu_uuid_unparse_strdup(&qemu_uuid); + return info; +} + +void qmp_system_reset(Error **errp) +{ + qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET); +} + +void qmp_system_powerdown(Error **errp) +{ + qemu_system_powerdown_request(); +} + +void qmp_system_wakeup(Error **errp) +{ + if (!qemu_wakeup_suspend_enabled()) { + error_setg(errp, + "wake-up from suspend is not supported by this guest"); + return; + } + + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); +} + +MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) +{ + return qmp_memory_device_list(); +} + +MemoryInfo *qmp_query_memory_size_summary(Error **errp) +{ + MemoryInfo *mem_info = g_new0(MemoryInfo, 1); + MachineState *ms = MACHINE(qdev_get_machine()); + + mem_info->base_memory = ms->ram_size; + + mem_info->plugged_memory = get_plugged_memory_size(); + mem_info->has_plugged_memory = + mem_info->plugged_memory != (uint64_t)-1; + + return mem_info; +} + +static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) +{ + RdmaProvider *rdma; + RdmaProviderClass *k; + GString *buf = opaque; + + if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { + rdma = RDMA_PROVIDER(obj); + k = RDMA_PROVIDER_GET_CLASS(obj); + if (k->format_statistics) { + k->format_statistics(rdma, buf); + } else { + g_string_append_printf(buf, + "RDMA statistics not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_rdma(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), + qmp_x_query_rdma_foreach, buf); + + return human_readable_text_from_str(buf); +} + +HumanReadableText *qmp_x_query_ramblock(Error **errp) +{ + g_autoptr(GString) buf = ram_block_format(); + + return human_readable_text_from_str(buf); +} + +static int qmp_x_query_irq_foreach(Object *obj, void *opaque) +{ + InterruptStatsProvider *intc; + InterruptStatsProviderClass *k; + GString *buf = opaque; + + if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { + intc = INTERRUPT_STATS_PROVIDER(obj); + k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); + uint64_t *irq_counts; + unsigned int nb_irqs, i; + if (k->get_statistics && + k->get_statistics(intc, &irq_counts, &nb_irqs)) { + if (nb_irqs > 0) { + g_string_append_printf(buf, "IRQ statistics for %s:\n", + object_get_typename(obj)); + for (i = 0; i < nb_irqs; i++) { + if (irq_counts[i] > 0) { + g_string_append_printf(buf, "%2d: %" PRId64 "\n", i, + irq_counts[i]); + } + } + } + } else { + g_string_append_printf(buf, + "IRQ statistics not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_irq(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), + qmp_x_query_irq_foreach, buf); + + return human_readable_text_from_str(buf); +} diff --git a/hw/core/machine.c b/hw/core/machine.c index 616f3a207c..f7761baab5 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -46,6 +46,7 @@ const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2); GlobalProperty hw_compat_7_1[] = { { "virtio-device", "queue_reset", "false" }, + { "virtio-rng-pci", "vectors", "0" }, }; const size_t hw_compat_7_1_len = G_N_ELEMENTS(hw_compat_7_1); diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index ee50ba1f2c..52ba77f3fc 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -97,13 +97,10 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp) static void build_ich9_smb_aml(AcpiDevAmlIf *adev, Aml *scope) { - BusChild *kid; ICH9SMBState *s = ICH9_SMB_DEVICE(adev); BusState *bus = BUS(s->smb.smbus); - QTAILQ_FOREACH(kid, &bus->children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } + qbus_build_aml(bus, scope); } static void ich9_smb_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index c4fb5b49bd..1bf47b0b0b 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -58,6 +58,7 @@ config PC_ACPI select ACPI_X86 select ACPI_CPU_HOTPLUG select ACPI_MEMORY_HOTPLUG + select ACPI_PCI_BRIDGE select ACPI_VIOT select SMBUS_EEPROM select PFLASH_CFI01 diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 127c4e2d50..145389aa58 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -117,8 +117,6 @@ typedef struct AcpiMiscInfo { #ifdef CONFIG_TPM TPMVersion tpm_version; #endif - const unsigned char *dsdt_code; - unsigned dsdt_size; } AcpiMiscInfo; typedef struct FwCfgTPMConfig { @@ -385,151 +383,185 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot) aml_append(method, if_ctx); } -static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, - bool pcihp_bridge_en) +static bool is_devfn_ignored_generic(const int devfn, const PCIBus *bus) { - Aml *dev, *notify_method = NULL, *method; - QObject *bsel; - PCIBus *sec; - int devfn; + const PCIDevice *pdev = bus->devices[devfn]; - bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); - if (bsel) { - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); + if (PCI_FUNC(devfn)) { + if (IS_PCI_BRIDGE(pdev)) { + /* + * Ignore only hotplugged PCI bridges on !0 functions, but + * allow describing cold plugged bridges on all functions + */ + if (DEVICE(pdev)->hotplugged) { + return true; + } + } else if (!get_dev_aml_func(DEVICE(pdev))) { + /* + * Ignore all other devices on !0 functions unless they + * have AML description (i.e have get_dev_aml_func() != 0) + */ + return true; + } + } + return false; +} - aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); - notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); +static bool is_devfn_ignored_hotplug(const int devfn, const PCIBus *bus) +{ + PCIDevice *pdev = bus->devices[devfn]; + if (pdev) { + return is_devfn_ignored_generic(devfn, bus) || + !DEVICE_GET_CLASS(pdev)->hotpluggable || + /* Cold plugged bridges aren't themselves hot-pluggable */ + (IS_PCI_BRIDGE(pdev) && !DEVICE(pdev)->hotplugged); + } else { /* non populated slots */ + /* + * hotplug is supported only for non-multifunction device + * so generate device description only for function 0 + */ + if (PCI_FUNC(devfn) || + (pci_bus_is_express(bus) && PCI_SLOT(devfn) > 0)) { + return true; + } } + return false; +} - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - DeviceClass *dc; - PCIDevice *pdev = bus->devices[devfn]; - int slot = PCI_SLOT(devfn); - int func = PCI_FUNC(devfn); - /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ - int adr = slot << 16 | func; - bool hotpluggbale_slot = false; - bool bridge_in_acpi = false; - bool cold_plugged_bridge = false; +static void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus, + QObject *bsel) +{ + int devfn; + Aml *dev, *notify_method = NULL, *method; + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - if (pdev) { - dc = DEVICE_GET_CLASS(pdev); + aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); + notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); - /* - * Cold plugged bridges aren't themselves hot-pluggable. - * Hotplugged bridges *are* hot-pluggable. - */ - cold_plugged_bridge = IS_PCI_BRIDGE(pdev) && - !DEVICE(pdev)->hotplugged; - bridge_in_acpi = cold_plugged_bridge && pcihp_bridge_en; + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + int slot = PCI_SLOT(devfn); + int adr = slot << 16 | PCI_FUNC(devfn); - hotpluggbale_slot = bsel && dc->hotpluggable && - !cold_plugged_bridge; + if (is_devfn_ignored_hotplug(devfn, bus)) { + continue; + } - /* - * allow describing coldplugged bridges in ACPI even if they are not - * on function 0, as they are not unpluggable, for all other devices - * generate description only for function 0 per slot, and for other - * functions if device on function provides its own AML - */ - if (func && !bridge_in_acpi && !get_dev_aml_func(DEVICE(pdev))) { - continue; - } + if (bus->devices[devfn]) { + dev = aml_scope("S%.02X", devfn); } else { - /* - * hotplug is supported only for non-multifunction device - * so generate device description only for function 0 - */ - if (bsel && !func) { - if (pci_bus_is_express(bus) && slot > 0) { - break; - } - /* mark it as empty hotpluggable slot */ - hotpluggbale_slot = true; - } else { - continue; - } + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); } - /* start to compose PCI device descriptor */ - dev = aml_device("S%.02X", devfn); - aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + /* + * Can't declare _SUN here for every device as it changes 'slot' + * enumeration order in linux kernel, so use another variable for it + */ + aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); + aml_append(dev, aml_pci_device_dsm()); - if (bsel) { - /* - * Can't declare _SUN here for every device as it changes 'slot' - * enumeration order in linux kernel, so use another variable for it - */ - aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); - aml_append(dev, aml_pci_device_dsm()); - } + aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); + /* add _EJ0 to make slot hotpluggable */ + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, + aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) + ); + aml_append(dev, method); - call_dev_aml_func(DEVICE(pdev), dev); + build_append_pcihp_notify_entry(notify_method, slot); - bridge_in_acpi = cold_plugged_bridge && pcihp_bridge_en; - if (bridge_in_acpi) { - /* - * device is coldplugged bridge, - * add child device descriptions into its scope - */ - PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); + /* device descriptor has been composed, add it into parent context */ + aml_append(parent_scope, dev); + } + aml_append(parent_scope, notify_method); +} - build_append_pci_bus_devices(dev, sec_bus, pcihp_bridge_en); - } +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) +{ + QObject *bsel; + int devfn; + Aml *dev; - if (hotpluggbale_slot) { - aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); - /* add _EJ0 to make slot hotpluggable */ - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); + bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); - build_append_pcihp_notify_entry(notify_method, slot); + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ + int adr = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn); + PCIDevice *pdev = bus->devices[devfn]; + + if (!pdev || is_devfn_ignored_generic(devfn, bus)) { + continue; } + /* start to compose PCI device descriptor */ + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + + call_dev_aml_func(DEVICE(bus->devices[devfn]), dev); + /* device descriptor has been composed, add it into parent context */ aml_append(parent_scope, dev); } if (bsel) { - aml_append(parent_scope, notify_method); + build_append_pcihp_slots(parent_scope, bus, bsel); } - /* Append PCNT method to notify about events on local and child buses. - * Add this method for root bus only when hotplug is enabled since DSDT - * expects it. - */ - if (bsel || pcihp_bridge_en) { - method = aml_method("PCNT", 0, AML_NOTSERIALIZED); - - /* If bus supports hotplug select it and notify about local events */ - if (bsel) { - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - - aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); - aml_append(method, aml_call2("DVNT", aml_name("PCIU"), - aml_int(1))); /* Device Check */ - aml_append(method, aml_call2("DVNT", aml_name("PCID"), - aml_int(3))); /* Eject Request */ + qobject_unref(bsel); +} + +static bool build_append_notfication_callback(Aml *parent_scope, + const PCIBus *bus) +{ + Aml *method; + PCIBus *sec; + QObject *bsel; + int nr_notifiers = 0; + + QLIST_FOREACH(sec, &bus->child, sibling) { + Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn); + if (pci_bus_is_root(sec) || + !object_property_find(OBJECT(sec), ACPI_PCIHP_PROP_BSEL)) { + continue; } + nr_notifiers = nr_notifiers + + build_append_notfication_callback(br_scope, sec); + aml_append(parent_scope, br_scope); + } - /* Notify about child bus events in any case */ - if (pcihp_bridge_en) { - QLIST_FOREACH(sec, &bus->child, sibling) { - if (pci_bus_is_root(sec)) { - continue; - } + /* + * Append PCNT method to notify about events on local and child buses. + * ps: hostbridge might not have hotplug (bsel) enabled but might have + * child bridges that do have bsel. + */ + method = aml_method("PCNT", 0, AML_NOTSERIALIZED); - aml_append(method, aml_name("^S%.02X.PCNT", - sec->parent_dev->devfn)); - } + /* If bus supports hotplug select it and notify about local events */ + bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); + if (bsel) { + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); + + aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); + aml_append(method, aml_call2("DVNT", aml_name("PCIU"), + aml_int(1))); /* Device Check */ + aml_append(method, aml_call2("DVNT", aml_name("PCID"), + aml_int(3))); /* Eject Request */ + nr_notifiers++; + } + + /* Notify about child bus events in any case */ + QLIST_FOREACH(sec, &bus->child, sibling) { + if (pci_bus_is_root(sec) || + !object_property_find(OBJECT(sec), ACPI_PCIHP_PROP_BSEL)) { + continue; } - aml_append(parent_scope, method); + aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn)); } + + aml_append(parent_scope, method); qobject_unref(bsel); + return !!nr_notifiers; } static Aml *aml_pci_pdsm(void) @@ -1678,7 +1710,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, PCIBus *bus = PCI_HOST_BRIDGE(pci_host)->bus; Aml *scope = aml_scope("PCI0"); /* Scan all PCI buses. Generate tables to support hotplug. */ - build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); + build_append_pci_bus_devices(scope, bus); aml_append(sb_scope, scope); } } @@ -1728,13 +1760,26 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en || pm->pcihp_root_en) { + bool has_pcnt; + + Object *pci_host = acpi_get_i386_pci_host(); + PCIBus *bus = PCI_HOST_BRIDGE(pci_host)->bus; + + scope = aml_scope("\\_SB.PCI0"); + has_pcnt = build_append_notfication_callback(scope, bus); + if (has_pcnt) { + aml_append(dsdt, scope); + } + scope = aml_scope("_GPE"); { method = aml_method("_E01", 0, AML_NOTSERIALIZED); - aml_append(method, - aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); - aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); - aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + if (has_pcnt) { + aml_append(method, + aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); + aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); + aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + } aml_append(scope, method); } aml_append(dsdt, scope); diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 4aaafbdd7b..52e5c1439a 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -33,9 +33,8 @@ #include "acpi-build.h" #include "acpi-common.h" -void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, - bool force_enabled) +void pc_madt_cpu_entry(int uid, const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled) { uint32_t apic_id = apic_ids->cpus[uid].arch_id; /* Flags – Local APIC Flags */ @@ -112,7 +111,7 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ for (i = 0; i < apic_ids->len; i++) { - adevc->madt_cpu(adev, i, apic_ids, table_data, false); + adevc->madt_cpu(i, apic_ids, table_data, false); if (apic_ids->cpus[i].arch_id > 254) { x2apic_mode = true; } diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index fb09185cbd..a075360d85 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -26,6 +26,7 @@ #include "exec/memory.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/acpi/generic_event_device.h" @@ -129,7 +130,7 @@ build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, sb_scope = aml_scope("_SB"); fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg); - isa_build_aml(ISA_BUS(isabus), sb_scope); + qbus_build_aml(BUS(isabus), sb_scope); build_ged_aml(sb_scope, GED_DEVICE, x86ms->acpi_dev, GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE); acpi_dsdt_add_power_button(sb_scope); diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 170a331e3f..29f30dd6d3 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -378,7 +378,8 @@ static void microvm_fix_kernel_cmdline(MachineState *machine) MicrovmMachineState *mms = MICROVM_MACHINE(machine); BusState *bus; BusChild *kid; - char *cmdline; + char *cmdline, *existing_cmdline; + size_t len; /* * Find MMIO transports with attached devices, and add them to the kernel @@ -387,7 +388,8 @@ static void microvm_fix_kernel_cmdline(MachineState *machine) * Yes, this is a hack, but one that heavily improves the UX without * introducing any significant issues. */ - cmdline = g_strdup(machine->kernel_cmdline); + existing_cmdline = fw_cfg_read_bytes_ptr(x86ms->fw_cfg, FW_CFG_CMDLINE_DATA); + cmdline = g_strdup(existing_cmdline); bus = sysbus_get_default(); QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; @@ -411,9 +413,12 @@ static void microvm_fix_kernel_cmdline(MachineState *machine) } } - fw_cfg_modify_i32(x86ms->fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(cmdline) + 1); - fw_cfg_modify_string(x86ms->fw_cfg, FW_CFG_CMDLINE_DATA, cmdline); - + len = strlen(cmdline); + if (len > VIRTIO_CMDLINE_TOTAL_MAX_LEN + strlen(existing_cmdline)) { + fprintf(stderr, "qemu: virtio mmio cmdline too large, skipping\n"); + } else { + memcpy(existing_cmdline, cmdline, len + 1); + } g_free(cmdline); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 83c57c6eb1..66cd718b70 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -257,8 +257,9 @@ static void pc_q35_init(MachineState *machine) NULL); if (!keep_pci_slot_hpc && acpi_pcihp) { - object_register_sugar_prop(TYPE_PCIE_SLOT, "x-native-hotplug", - "false", true); + object_register_sugar_prop(TYPE_PCIE_SLOT, + "x-do-not-expose-native-hotplug-cap", + "true", true); } /* irq lines */ diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 78cc131926..eaff4227bd 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -50,6 +50,7 @@ #include "hw/intc/i8259.h" #include "hw/rtc/mc146818rtc.h" #include "target/i386/sev.h" +#include "hw/i386/microvm.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/irq.h" @@ -813,12 +814,18 @@ void x86_load_linux(X86MachineState *x86ms, const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; const char *dtb_filename = machine->dtb; - const char *kernel_cmdline = machine->kernel_cmdline; + char *kernel_cmdline; SevKernelLoaderContext sev_load_ctx = {}; enum { RNG_SEED_LENGTH = 32 }; - /* Align to 16 bytes as a paranoia measure */ - cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; + /* + * Add the NUL terminator, some padding for the microvm cmdline fiddling + * hack, and then align to 16 bytes as a paranoia measure + */ + cmdline_size = (strlen(machine->kernel_cmdline) + 1 + + VIRTIO_CMDLINE_TOTAL_MAX_LEN + 16) & ~15; + /* Make a copy, since we might append arbitrary bytes to it later. */ + kernel_cmdline = g_strndup(machine->kernel_cmdline, cmdline_size); /* load the kernel header */ f = fopen(kernel_filename, "rb"); @@ -959,12 +966,6 @@ void x86_load_linux(X86MachineState *x86ms, initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; } - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - sev_load_ctx.cmdline_data = (char *)kernel_cmdline; - sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; - if (protocol >= 0x202) { stl_p(header + 0x228, cmdline_addr); } else { @@ -1091,27 +1092,24 @@ void x86_load_linux(X86MachineState *x86ms, exit(1); } - setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); - kernel_size = setup_data_offset + sizeof(SetupData) + dtb_size; - kernel = g_realloc(kernel, kernel_size); - - - setup_data = (SetupData *)(kernel + setup_data_offset); + setup_data_offset = cmdline_size; + cmdline_size += sizeof(SetupData) + dtb_size; + kernel_cmdline = g_realloc(kernel_cmdline, cmdline_size); + setup_data = (void *)kernel_cmdline + setup_data_offset; setup_data->next = cpu_to_le64(first_setup_data); - first_setup_data = prot_addr + setup_data_offset; + first_setup_data = cmdline_addr + setup_data_offset; setup_data->type = cpu_to_le32(SETUP_DTB); setup_data->len = cpu_to_le32(dtb_size); - load_image_size(dtb_filename, setup_data->data, dtb_size); } - if (!legacy_no_rng_seed) { - setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); - kernel_size = setup_data_offset + sizeof(SetupData) + RNG_SEED_LENGTH; - kernel = g_realloc(kernel, kernel_size); - setup_data = (SetupData *)(kernel + setup_data_offset); + if (!legacy_no_rng_seed && protocol >= 0x209) { + setup_data_offset = cmdline_size; + cmdline_size += sizeof(SetupData) + RNG_SEED_LENGTH; + kernel_cmdline = g_realloc(kernel_cmdline, cmdline_size); + setup_data = (void *)kernel_cmdline + setup_data_offset; setup_data->next = cpu_to_le64(first_setup_data); - first_setup_data = prot_addr + setup_data_offset; + first_setup_data = cmdline_addr + setup_data_offset; setup_data->type = cpu_to_le32(SETUP_RNG_SEED); setup_data->len = cpu_to_le32(RNG_SEED_LENGTH); qemu_guest_getrandom_nofail(setup_data->data, RNG_SEED_LENGTH); @@ -1122,6 +1120,12 @@ void x86_load_linux(X86MachineState *x86ms, fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); } + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, cmdline_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline, cmdline_size); + sev_load_ctx.cmdline_data = (char *)kernel_cmdline; + sev_load_ctx.cmdline_size = cmdline_size; + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); sev_load_ctx.kernel_data = (char *)kernel; @@ -1134,7 +1138,7 @@ void x86_load_linux(X86MachineState *x86ms, * kernel on the other side of the fw_cfg interface matches the hash of the * file the user passed in. */ - if (!sev_enabled()) { + if (!sev_enabled() && first_setup_data) { SetupDataFixup *fixup = g_malloc(sizeof(*fixup)); memcpy(setup, header, MIN(sizeof(header), setup_size)); diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index b17b29288c..d07b13eb27 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -21,6 +21,8 @@ #include "hw/irq.h" #include "cpu.h" #include "target/arm/cpregs.h" +#include "sysemu/tcg.h" +#include "sysemu/qtest.h" /* * Special case return value from hppvi_index(); must be larger than @@ -2376,6 +2378,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 6, .type = ARM_CP_IO | ARM_CP_NO_RAW, .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fgt = FGT_ICC_IGRPENN_EL1, .readfn = icc_igrpen_read, .writefn = icc_igrpen_write, }, @@ -2384,6 +2387,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, .access = PL1_RW, .accessfn = gicv3_irq_access, + .fgt = FGT_ICC_IGRPENN_EL1, .readfn = icc_igrpen_read, .writefn = icc_igrpen_write, }, @@ -2810,6 +2814,8 @@ void gicv3_init_cpuif(GICv3State *s) * which case we'd get the wrong value. * So instead we define the regs with no ri->opaque info, and * get back to the GICv3CPUState from the CPUARMState. + * + * These CP regs callbacks can be called from either TCG or HVF code. */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); @@ -2905,6 +2911,16 @@ void gicv3_init_cpuif(GICv3State *s) define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo); } } - arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); + if (tcg_enabled() || qtest_enabled()) { + /* + * We can only trap EL changes with TCG. However the GIC interrupt + * state only changes on EL changes involving EL2 or EL3, so for + * the non-TCG case this is OK, as EL2 and EL3 can't exist. + */ + arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); + } else { + assert(!arm_feature(&cpu->env, ARM_FEATURE_EL2)); + assert(!arm_feature(&cpu->env, ARM_FEATURE_EL3)); + } } } diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 1bee1a47f1..f155b80010 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -24,7 +24,6 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/isa/isa.h" -#include "hw/acpi/acpi_aml_interface.h" static ISABus *isabus; @@ -188,15 +187,6 @@ ISADevice *isa_vga_init(ISABus *bus) } } -void isa_build_aml(ISABus *bus, Aml *scope) -{ - BusChild *kid; - - QTAILQ_FOREACH(kid, &bus->parent_obj.children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } -} - static void isabus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 8d541e2b54..1fba3c210c 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -813,7 +813,6 @@ static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) { Aml *field; - BusChild *kid; ICH9LPCState *s = ICH9_LPC_DEVICE(adev); BusState *bus = BUS(s->isa_bus); Aml *sb_scope = aml_scope("\\_SB"); @@ -835,9 +834,7 @@ static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(sb_scope, field); aml_append(scope, sb_scope); - QTAILQ_FOREACH(kid, &bus->children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } + qbus_build_aml(bus, scope); } static void ich9_lpc_class_init(ObjectClass *klass, void *data) diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 283b971ec4..a9cb39bf21 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -306,7 +306,6 @@ static void pci_piix3_realize(PCIDevice *dev, Error **errp) static void build_pci_isa_aml(AcpiDevAmlIf *adev, Aml *scope) { Aml *field; - BusChild *kid; Aml *sb_scope = aml_scope("\\_SB"); BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0"); @@ -322,9 +321,7 @@ static void build_pci_isa_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(sb_scope, field); aml_append(scope, sb_scope); - QTAILQ_FOREACH(kid, &bus->children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } + qbus_build_aml(bus, scope); } static void pci_piix3_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index 6d7fdb040a..8965f5c22a 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -210,13 +210,6 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo) { - dinfo = drive_get(IF_NONE, 0, 0); - if (dinfo) { - warn_report("using \"-drive if=none\" for the OTP is deprecated, " - "use \"-drive if=pflash\" instead."); - } - } if (dinfo) { int ret; uint64_t perm; diff --git a/hw/net/meson.build b/hw/net/meson.build index ebac261542..4285145715 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -68,5 +68,6 @@ softmmu_ss.add(when: 'CONFIG_ROCKER', if_true: files( 'rocker/rocker_world.c', ), if_false: files('rocker/qmp-norocker.c')) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('rocker/qmp-norocker.c')) +softmmu_ss.add(files('rocker/rocker-hmp-cmds.c')) subdir('can') diff --git a/hw/net/rocker/rocker-hmp-cmds.c b/hw/net/rocker/rocker-hmp-cmds.c new file mode 100644 index 0000000000..197c6e28dc --- /dev/null +++ b/hw/net/rocker/rocker-hmp-cmds.c @@ -0,0 +1,316 @@ +/* + * Human Monitor Interface commands + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "net/eth.h" +#include "qapi/qapi-commands-rocker.h" +#include "qapi/qmp/qdict.h" + +void hmp_rocker(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + RockerSwitch *rocker; + Error *err = NULL; + + rocker = qmp_query_rocker(name, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "name: %s\n", rocker->name); + monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id); + monitor_printf(mon, "ports: %d\n", rocker->ports); + + qapi_free_RockerSwitch(rocker); +} + +void hmp_rocker_ports(Monitor *mon, const QDict *qdict) +{ + RockerPortList *list, *port; + const char *name = qdict_get_str(qdict, "name"); + Error *err = NULL; + + list = qmp_query_rocker_ports(name, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, " ena/ speed/ auto\n"); + monitor_printf(mon, " port link duplex neg?\n"); + + for (port = list; port; port = port->next) { + monitor_printf(mon, "%10s %-4s %-3s %2s %s\n", + port->value->name, + port->value->enabled ? port->value->link_up ? + "up" : "down" : "!ena", + port->value->speed == 10000 ? "10G" : "??", + port->value->duplex ? "FD" : "HD", + port->value->autoneg ? "Yes" : "No"); + } + + qapi_free_RockerPortList(list); +} + +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaFlowList *list, *info; + const char *name = qdict_get_str(qdict, "name"); + uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); + Error *err = NULL; + + list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "prio tbl hits key(mask) --> actions\n"); + + for (info = list; info; info = info->next) { + RockerOfDpaFlow *flow = info->value; + RockerOfDpaFlowKey *key = flow->key; + RockerOfDpaFlowMask *mask = flow->mask; + RockerOfDpaFlowAction *action = flow->action; + + if (flow->hits) { + monitor_printf(mon, "%-4d %-3d %-4" PRIu64, + key->priority, key->tbl_id, flow->hits); + } else { + monitor_printf(mon, "%-4d %-3d ", + key->priority, key->tbl_id); + } + + if (key->has_in_pport) { + monitor_printf(mon, " pport %d", key->in_pport); + if (mask->has_in_pport) { + monitor_printf(mon, "(0x%x)", mask->in_pport); + } + } + + if (key->has_vlan_id) { + monitor_printf(mon, " vlan %d", + key->vlan_id & VLAN_VID_MASK); + if (mask->has_vlan_id) { + monitor_printf(mon, "(0x%x)", mask->vlan_id); + } + } + + if (key->has_tunnel_id) { + monitor_printf(mon, " tunnel %d", key->tunnel_id); + if (mask->has_tunnel_id) { + monitor_printf(mon, "(0x%x)", mask->tunnel_id); + } + } + + if (key->has_eth_type) { + switch (key->eth_type) { + case 0x0806: + monitor_printf(mon, " ARP"); + break; + case 0x0800: + monitor_printf(mon, " IP"); + break; + case 0x86dd: + monitor_printf(mon, " IPv6"); + break; + case 0x8809: + monitor_printf(mon, " LACP"); + break; + case 0x88cc: + monitor_printf(mon, " LLDP"); + break; + default: + monitor_printf(mon, " eth type 0x%04x", key->eth_type); + break; + } + } + + if (key->eth_src) { + if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) && + mask->eth_src && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src <any mcast/bcast>"); + } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) && + mask->eth_src && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src <any ucast>"); + } else { + monitor_printf(mon, " src %s", key->eth_src); + if (mask->eth_src) { + monitor_printf(mon, "(%s)", mask->eth_src); + } + } + } + + if (key->eth_dst) { + if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) && + mask->eth_dst && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst <any mcast/bcast>"); + } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) && + mask->eth_dst && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst <any ucast>"); + } else { + monitor_printf(mon, " dst %s", key->eth_dst); + if (mask->eth_dst) { + monitor_printf(mon, "(%s)", mask->eth_dst); + } + } + } + + if (key->has_ip_proto) { + monitor_printf(mon, " proto %d", key->ip_proto); + if (mask->has_ip_proto) { + monitor_printf(mon, "(0x%x)", mask->ip_proto); + } + } + + if (key->has_ip_tos) { + monitor_printf(mon, " TOS %d", key->ip_tos); + if (mask->has_ip_tos) { + monitor_printf(mon, "(0x%x)", mask->ip_tos); + } + } + + if (key->ip_dst) { + monitor_printf(mon, " dst %s", key->ip_dst); + } + + if (action->has_goto_tbl || action->has_group_id || + action->has_new_vlan_id) { + monitor_printf(mon, " -->"); + } + + if (action->has_new_vlan_id) { + monitor_printf(mon, " apply new vlan %d", + ntohs(action->new_vlan_id)); + } + + if (action->has_group_id) { + monitor_printf(mon, " write group 0x%08x", action->group_id); + } + + if (action->has_goto_tbl) { + monitor_printf(mon, " goto tbl %d", action->goto_tbl); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaFlowList(list); +} + +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaGroupList *list, *g; + const char *name = qdict_get_str(qdict, "name"); + uint8_t type = qdict_get_try_int(qdict, "type", 9); + Error *err = NULL; + + list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "id (decode) --> buckets\n"); + + for (g = list; g; g = g->next) { + RockerOfDpaGroup *group = g->value; + bool set = false; + + monitor_printf(mon, "0x%08x", group->id); + + monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" : + group->type == 1 ? "L2 rewrite" : + group->type == 2 ? "L3 unicast" : + group->type == 3 ? "L2 multicast" : + group->type == 4 ? "L2 flood" : + group->type == 5 ? "L3 interface" : + group->type == 6 ? "L3 multicast" : + group->type == 7 ? "L3 ECMP" : + group->type == 8 ? "L2 overlay" : + "unknown"); + + if (group->has_vlan_id) { + monitor_printf(mon, " vlan %d", group->vlan_id); + } + + if (group->has_pport) { + monitor_printf(mon, " pport %d", group->pport); + } + + if (group->has_index) { + monitor_printf(mon, " index %d", group->index); + } + + monitor_printf(mon, ") -->"); + + if (group->has_set_vlan_id && group->set_vlan_id) { + set = true; + monitor_printf(mon, " set vlan %d", + group->set_vlan_id & VLAN_VID_MASK); + } + + if (group->set_eth_src) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " src %s", group->set_eth_src); + } + + if (group->set_eth_dst) { + if (!set) { + monitor_printf(mon, " set"); + } + monitor_printf(mon, " dst %s", group->set_eth_dst); + } + + if (group->has_ttl_check && group->ttl_check) { + monitor_printf(mon, " check TTL"); + } + + if (group->has_group_id && group->group_id) { + monitor_printf(mon, " group id 0x%08x", group->group_id); + } + + if (group->has_pop_vlan && group->pop_vlan) { + monitor_printf(mon, " pop vlan"); + } + + if (group->has_out_pport) { + monitor_printf(mon, " out pport %d", group->out_pport); + } + + if (group->has_group_ids) { + struct uint32List *id; + + monitor_printf(mon, " groups ["); + for (id = group->group_ids; id; id = id->next) { + monitor_printf(mon, "0x%08x", id->value); + if (id->next) { + monitor_printf(mon, ","); + } + } + monitor_printf(mon, "]"); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaGroupList(list); +} diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index a00881bc64..432754eda4 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -741,6 +741,15 @@ void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) fw_cfg_add_bytes_callback(s, key, NULL, NULL, NULL, data, len, true); } +void *fw_cfg_read_bytes_ptr(FWCfgState *s, uint16_t key) +{ + int arch = !!(key & FW_CFG_ARCH_LOCAL); + + key &= FW_CFG_ENTRY_MASK; + assert(key < fw_cfg_max_entry(s)); + return s->entries[arch][key].data; +} + void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) { size_t sz = strlen(value) + 1; diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 20099a8ae3..1ce4e7beba 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -87,7 +87,12 @@ static void gen_rp_realize(DeviceState *dev, Error **errp) return; } - if (grp->res_reserve.io == -1 && s->hotplug && !s->native_hotplug) { + /* + * reserving IO space led to worse issues in 6.1, when this hunk was + * introduced. (see commit: 211afe5c69b59). Keep this broken for 6.1 + * machine type ABI compatibility only + */ + if (s->hide_native_hotplug_cap && grp->res_reserve.io == -1 && s->hotplug) { grp->res_reserve.io = GEN_PCIE_ROOT_DEFAULT_IO_RANGE; } int rc = pci_bridge_qemu_reserve_cap_init(d, 0, diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 3435df8d73..4b2696ea7f 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -186,7 +186,6 @@ static Property pci_bridge_dev_properties[] = { res_reserve.mem_pref_32, -1), DEFINE_PROP_SIZE("pref64-reserve", PCIBridgeDev, res_reserve.mem_pref_64, -1), - DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 8cf318cb80..8e589ff2c9 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -91,7 +91,7 @@ static void grackle_init(Object *obj) static void grackle_pci_realize(PCIDevice *d, Error **errp) { - d->config[0x09] = 0x01; + d->config[PCI_CLASS_PROG] = 0x01; } static void grackle_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 5b00b4e462..cdfb62ac2e 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -330,9 +330,9 @@ static void raven_realize(PCIDevice *d, Error **errp) char *filename; int bios_size = -1; - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + d->config[PCI_CAPABILITY_LIST] = 0x00; memory_region_init_rom_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE, &error_fatal); diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index e3abe3c0f9..e4c1abd871 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -276,12 +276,9 @@ static void pci_unin_internal_init(Object *obj) static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache_line_size */ - d->config[0x0C] = 0x08; - /* latency_timer */ - d->config[0x0D] = 0x10; - /* capabilities_pointer */ - d->config[0x34] = 0x00; + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + d->config[PCI_CAPABILITY_LIST] = 0x00; /* * Set kMacRISCPCIAddressSelect (0x48) register to indicate PCI @@ -296,30 +293,22 @@ static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache_line_size */ - d->config[0x0C] = 0x08; - /* latency_timer */ - d->config[0x0D] = 0x10; - /* capabilities_pointer - d->config[0x34] = 0x80; */ + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + /* d->config[PCI_CAPABILITY_LIST] = 0x80; */ } static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache line size */ - d->config[0x0C] = 0x08; - /* latency timer */ - d->config[0x0D] = 0x10; + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; } static void unin_internal_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache_line_size */ - d->config[0x0C] = 0x08; - /* latency_timer */ - d->config[0x0D] = 0x10; - /* capabilities_pointer */ - d->config[0x34] = 0x00; + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + d->config[PCI_CAPABILITY_LIST] = 0x00; } static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 39a7bb32aa..208c16f450 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -483,7 +483,7 @@ static void pci_bus_uninit(PCIBus *bus) pci_host_bus_unregister(BUS(bus)->parent); } -bool pci_bus_is_express(PCIBus *bus) +bool pci_bus_is_express(const PCIBus *bus) { return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS); } diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index b2b180edd6..dd5af508f9 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -36,6 +36,8 @@ #include "qemu/module.h" #include "qemu/range.h" #include "qapi/error.h" +#include "hw/acpi/acpi_aml_interface.h" +#include "hw/acpi/pci.h" /* PCI bridge subsystem vendor ID helper functions */ #define PCI_SSVID_SIZEOF 8 @@ -467,11 +469,23 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, return 0; } +static void pci_bridge_class_init(ObjectClass *klass, void *data) +{ + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); + + adevc->build_dev_aml = build_pci_bridge_aml; +} + static const TypeInfo pci_bridge_type_info = { .name = TYPE_PCI_BRIDGE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBridge), + .class_init = pci_bridge_class_init, .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void pci_bridge_register_types(void) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 68a62da0b5..924fdabd15 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -611,11 +611,11 @@ void pcie_cap_slot_init(PCIDevice *dev, PCIESlot *s) PCI_EXP_SLTCAP_ABP); /* - * Enable native hot-plug on all hot-plugged bridges unless - * hot-plug is disabled on the slot. + * Expose native hot-plug on all bridges if hot-plug is enabled on the slot. + * (unless broken 6.1 ABI is enforced for compat reasons) */ if (s->hotplug && - (s->native_hotplug || DEVICE(dev)->hotplugged)) { + (!s->hide_native_hotplug_cap || DEVICE(dev)->hotplugged)) { pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, PCI_EXP_SLTCAP_HPS | PCI_EXP_SLTCAP_HPC); diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 687e4e763a..65a397ad23 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -173,7 +173,8 @@ static Property pcie_slot_props[] = { DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), DEFINE_PROP_BOOL("hotplug", PCIESlot, hotplug, true), - DEFINE_PROP_BOOL("x-native-hotplug", PCIESlot, native_hotplug, true), + DEFINE_PROP_BOOL("x-do-not-expose-native-hotplug-cap", PCIESlot, + hide_native_hotplug_cap, false), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index e71f3a7483..fca7f6691a 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -568,6 +568,13 @@ void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); + + if (led == SHPC_LED_BLINK) { + error_setg(errp, "Hot-unplug failed: " + "guest is busy (power indicator blinking)"); + return; + } + if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { shpc_free_devices_in_slot(shpc, slot); shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e493c28814..d4e360850f 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2332,10 +2332,15 @@ static void scsi_disk_reset(DeviceState *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); uint64_t nb_sectors; + AioContext *ctx; scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); + ctx = blk_get_aio_context(s->qdev.conf.blk); + aio_context_acquire(ctx); blk_get_geometry(s->qdev.conf.blk, &nb_sectors); + aio_context_release(ctx); + nb_sectors /= s->qdev.blocksize / BDRV_SECTOR_SIZE; if (nb_sectors) { nb_sectors--; diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index f93be2e137..bdec78bfc6 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -67,5 +67,6 @@ softmmu_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) softmmu_ss.add(when: 'CONFIG_VIRTIO', if_false: files('virtio-stub.c')) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('virtio-stub.c')) +softmmu_ss.add(files('virtio-hmp-cmds.c')) specific_ss.add_all(when: 'CONFIG_VIRTIO', if_true: specific_virtio_ss) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index d9ce0501b2..e68daa35d4 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -48,7 +48,7 @@ * hardware plaform. */ #if defined(TARGET_X86) || defined(TARGET_X86_64) || \ - defined(TARGET_ARM) || defined(TARGET_ARM_64) + defined(TARGET_ARM) || defined(TARGET_AARCH64) #include "hw/acpi/acpi.h" #define VHOST_USER_MAX_RAM_SLOTS ACPI_MAX_RAM_SLOTS @@ -305,19 +305,8 @@ static int vhost_user_read_header(struct vhost_dev *dev, VhostUserMsg *msg) return 0; } -struct vhost_user_read_cb_data { - struct vhost_dev *dev; - VhostUserMsg *msg; - GMainLoop *loop; - int ret; -}; - -static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, - gpointer opaque) +static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { - struct vhost_user_read_cb_data *data = opaque; - struct vhost_dev *dev = data->dev; - VhostUserMsg *msg = data->msg; struct vhost_user *u = dev->opaque; CharBackend *chr = u->user->chr; uint8_t *p = (uint8_t *) msg; @@ -325,8 +314,7 @@ static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, r = vhost_user_read_header(dev, msg); if (r < 0) { - data->ret = r; - goto end; + return r; } /* validate message size is sane */ @@ -334,8 +322,7 @@ static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", msg->hdr.size, VHOST_USER_PAYLOAD_SIZE); - data->ret = -EPROTO; - goto end; + return -EPROTO; } if (msg->hdr.size) { @@ -346,84 +333,11 @@ static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, int saved_errno = errno; error_report("Failed to read msg payload." " Read %d instead of %d.", r, msg->hdr.size); - data->ret = r < 0 ? -saved_errno : -EIO; - goto end; + return r < 0 ? -saved_errno : -EIO; } } -end: - g_main_loop_quit(data->loop); - return G_SOURCE_REMOVE; -} - -static gboolean slave_read(QIOChannel *ioc, GIOCondition condition, - gpointer opaque); - -/* - * This updates the read handler to use a new event loop context. - * Event sources are removed from the previous context : this ensures - * that events detected in the previous context are purged. They will - * be re-detected and processed in the new context. - */ -static void slave_update_read_handler(struct vhost_dev *dev, - GMainContext *ctxt) -{ - struct vhost_user *u = dev->opaque; - - if (!u->slave_ioc) { - return; - } - - if (u->slave_src) { - g_source_destroy(u->slave_src); - g_source_unref(u->slave_src); - } - - u->slave_src = qio_channel_add_watch_source(u->slave_ioc, - G_IO_IN | G_IO_HUP, - slave_read, dev, NULL, - ctxt); -} - -static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) -{ - struct vhost_user *u = dev->opaque; - CharBackend *chr = u->user->chr; - GMainContext *prev_ctxt = chr->chr->gcontext; - GMainContext *ctxt = g_main_context_new(); - GMainLoop *loop = g_main_loop_new(ctxt, FALSE); - struct vhost_user_read_cb_data data = { - .dev = dev, - .loop = loop, - .msg = msg, - .ret = 0 - }; - - /* - * We want to be able to monitor the slave channel fd while waiting - * for chr I/O. This requires an event loop, but we can't nest the - * one to which chr is currently attached : its fd handlers might not - * be prepared for re-entrancy. So we create a new one and switch chr - * to use it. - */ - slave_update_read_handler(dev, ctxt); - qemu_chr_be_update_read_handlers(chr->chr, ctxt); - qemu_chr_fe_add_watch(chr, G_IO_IN | G_IO_HUP, vhost_user_read_cb, &data); - - g_main_loop_run(loop); - - /* - * Restore the previous event loop context. This also destroys/recreates - * event sources : this guarantees that all pending events in the original - * context that have been processed by the nested loop are purged. - */ - qemu_chr_be_update_read_handlers(chr->chr, prev_ctxt); - slave_update_read_handler(dev, NULL); - - g_main_loop_unref(loop); - g_main_context_unref(ctxt); - - return data.ret; + return 0; } static int process_message_reply(struct vhost_dev *dev, @@ -459,6 +373,8 @@ static bool vhost_user_one_time_request(VhostUserRequest request) case VHOST_USER_SET_MEM_TABLE: case VHOST_USER_GET_QUEUE_NUM: case VHOST_USER_NET_SET_MTU: + case VHOST_USER_ADD_MEM_REG: + case VHOST_USER_REM_MEM_REG: return true; default: return false; @@ -1807,7 +1723,9 @@ static int vhost_setup_slave_channel(struct vhost_dev *dev) return -ECONNREFUSED; } u->slave_ioc = ioc; - slave_update_read_handler(dev, NULL); + u->slave_src = qio_channel_add_watch_source(u->slave_ioc, + G_IO_IN | G_IO_HUP, + slave_read, dev, NULL, NULL); if (reply_supported) { msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK; diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c new file mode 100644 index 0000000000..477c97dea2 --- /dev/null +++ b/hw/virtio/virtio-hmp-cmds.c @@ -0,0 +1,321 @@ +/* + * HMP commands related to virtio + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/qapi-commands-virtio.h" +#include "qapi/qmp/qdict.h" + + +static void hmp_virtio_dump_protocols(Monitor *mon, + VhostDeviceProtocols *pcol) +{ + strList *pcol_list = pcol->protocols; + while (pcol_list) { + monitor_printf(mon, "\t%s", pcol_list->value); + pcol_list = pcol_list->next; + if (pcol_list != NULL) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + if (pcol->has_unknown_protocols) { + monitor_printf(mon, " unknown-protocols(0x%016"PRIx64")\n", + pcol->unknown_protocols); + } +} + +static void hmp_virtio_dump_status(Monitor *mon, + VirtioDeviceStatus *status) +{ + strList *status_list = status->statuses; + while (status_list) { + monitor_printf(mon, "\t%s", status_list->value); + status_list = status_list->next; + if (status_list != NULL) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + if (status->has_unknown_statuses) { + monitor_printf(mon, " unknown-statuses(0x%016"PRIx32")\n", + status->unknown_statuses); + } +} + +static void hmp_virtio_dump_features(Monitor *mon, + VirtioDeviceFeatures *features) +{ + strList *transport_list = features->transports; + while (transport_list) { + monitor_printf(mon, "\t%s", transport_list->value); + transport_list = transport_list->next; + if (transport_list != NULL) { + monitor_printf(mon, ",\n"); + } + } + + monitor_printf(mon, "\n"); + strList *list = features->dev_features; + if (list) { + while (list) { + monitor_printf(mon, "\t%s", list->value); + list = list->next; + if (list != NULL) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + } + + if (features->has_unknown_dev_features) { + monitor_printf(mon, " unknown-features(0x%016"PRIx64")\n", + features->unknown_dev_features); + } +} + +void hmp_virtio_query(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + VirtioInfoList *list = qmp_x_query_virtio(&err); + VirtioInfoList *node; + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + if (list == NULL) { + monitor_printf(mon, "No VirtIO devices\n"); + return; + } + + node = list; + while (node) { + monitor_printf(mon, "%s [%s]\n", node->value->path, + node->value->name); + node = node->next; + } + qapi_free_VirtioInfoList(list); +} + +void hmp_virtio_status(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + VirtioStatus *s = qmp_x_query_virtio_status(path, &err); + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s %s\n", + s->name, s->vhost_dev ? "(vhost)" : ""); + monitor_printf(mon, " device_id: %d\n", s->device_id); + monitor_printf(mon, " vhost_started: %s\n", + s->vhost_started ? "true" : "false"); + monitor_printf(mon, " bus_name: %s\n", s->bus_name); + monitor_printf(mon, " broken: %s\n", + s->broken ? "true" : "false"); + monitor_printf(mon, " disabled: %s\n", + s->disabled ? "true" : "false"); + monitor_printf(mon, " disable_legacy_check: %s\n", + s->disable_legacy_check ? "true" : "false"); + monitor_printf(mon, " started: %s\n", + s->started ? "true" : "false"); + monitor_printf(mon, " use_started: %s\n", + s->use_started ? "true" : "false"); + monitor_printf(mon, " start_on_kick: %s\n", + s->start_on_kick ? "true" : "false"); + monitor_printf(mon, " use_guest_notifier_mask: %s\n", + s->use_guest_notifier_mask ? "true" : "false"); + monitor_printf(mon, " vm_running: %s\n", + s->vm_running ? "true" : "false"); + monitor_printf(mon, " num_vqs: %"PRId64"\n", s->num_vqs); + monitor_printf(mon, " queue_sel: %d\n", + s->queue_sel); + monitor_printf(mon, " isr: %d\n", s->isr); + monitor_printf(mon, " endianness: %s\n", + s->device_endian); + monitor_printf(mon, " status:\n"); + hmp_virtio_dump_status(mon, s->status); + monitor_printf(mon, " Guest features:\n"); + hmp_virtio_dump_features(mon, s->guest_features); + monitor_printf(mon, " Host features:\n"); + hmp_virtio_dump_features(mon, s->host_features); + monitor_printf(mon, " Backend features:\n"); + hmp_virtio_dump_features(mon, s->backend_features); + + if (s->vhost_dev) { + monitor_printf(mon, " VHost:\n"); + monitor_printf(mon, " nvqs: %d\n", + s->vhost_dev->nvqs); + monitor_printf(mon, " vq_index: %"PRId64"\n", + s->vhost_dev->vq_index); + monitor_printf(mon, " max_queues: %"PRId64"\n", + s->vhost_dev->max_queues); + monitor_printf(mon, " n_mem_sections: %"PRId64"\n", + s->vhost_dev->n_mem_sections); + monitor_printf(mon, " n_tmp_sections: %"PRId64"\n", + s->vhost_dev->n_tmp_sections); + monitor_printf(mon, " backend_cap: %"PRId64"\n", + s->vhost_dev->backend_cap); + monitor_printf(mon, " log_enabled: %s\n", + s->vhost_dev->log_enabled ? "true" : "false"); + monitor_printf(mon, " log_size: %"PRId64"\n", + s->vhost_dev->log_size); + monitor_printf(mon, " Features:\n"); + hmp_virtio_dump_features(mon, s->vhost_dev->features); + monitor_printf(mon, " Acked features:\n"); + hmp_virtio_dump_features(mon, s->vhost_dev->acked_features); + monitor_printf(mon, " Backend features:\n"); + hmp_virtio_dump_features(mon, s->vhost_dev->backend_features); + monitor_printf(mon, " Protocol features:\n"); + hmp_virtio_dump_protocols(mon, s->vhost_dev->protocol_features); + } + + qapi_free_VirtioStatus(s); +} + +void hmp_vhost_queue_status(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + int queue = qdict_get_int(qdict, "queue"); + VirtVhostQueueStatus *s = + qmp_x_query_virtio_vhost_queue_status(path, queue, &err); + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s (vhost)\n", + s->name); + monitor_printf(mon, " kick: %"PRId64"\n", s->kick); + monitor_printf(mon, " call: %"PRId64"\n", s->call); + monitor_printf(mon, " VRing:\n"); + monitor_printf(mon, " num: %"PRId64"\n", s->num); + monitor_printf(mon, " desc: 0x%016"PRIx64"\n", s->desc); + monitor_printf(mon, " desc_phys: 0x%016"PRIx64"\n", + s->desc_phys); + monitor_printf(mon, " desc_size: %"PRId32"\n", s->desc_size); + monitor_printf(mon, " avail: 0x%016"PRIx64"\n", s->avail); + monitor_printf(mon, " avail_phys: 0x%016"PRIx64"\n", + s->avail_phys); + monitor_printf(mon, " avail_size: %"PRId32"\n", s->avail_size); + monitor_printf(mon, " used: 0x%016"PRIx64"\n", s->used); + monitor_printf(mon, " used_phys: 0x%016"PRIx64"\n", + s->used_phys); + monitor_printf(mon, " used_size: %"PRId32"\n", s->used_size); + + qapi_free_VirtVhostQueueStatus(s); +} + +void hmp_virtio_queue_status(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + int queue = qdict_get_int(qdict, "queue"); + VirtQueueStatus *s = qmp_x_query_virtio_queue_status(path, queue, &err); + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s\n", s->name); + monitor_printf(mon, " queue_index: %d\n", s->queue_index); + monitor_printf(mon, " inuse: %d\n", s->inuse); + monitor_printf(mon, " used_idx: %d\n", s->used_idx); + monitor_printf(mon, " signalled_used: %d\n", + s->signalled_used); + monitor_printf(mon, " signalled_used_valid: %s\n", + s->signalled_used_valid ? "true" : "false"); + if (s->has_last_avail_idx) { + monitor_printf(mon, " last_avail_idx: %d\n", + s->last_avail_idx); + } + if (s->has_shadow_avail_idx) { + monitor_printf(mon, " shadow_avail_idx: %d\n", + s->shadow_avail_idx); + } + monitor_printf(mon, " VRing:\n"); + monitor_printf(mon, " num: %"PRId32"\n", s->vring_num); + monitor_printf(mon, " num_default: %"PRId32"\n", + s->vring_num_default); + monitor_printf(mon, " align: %"PRId32"\n", + s->vring_align); + monitor_printf(mon, " desc: 0x%016"PRIx64"\n", + s->vring_desc); + monitor_printf(mon, " avail: 0x%016"PRIx64"\n", + s->vring_avail); + monitor_printf(mon, " used: 0x%016"PRIx64"\n", + s->vring_used); + + qapi_free_VirtQueueStatus(s); +} + +void hmp_virtio_queue_element(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + int queue = qdict_get_int(qdict, "queue"); + int index = qdict_get_try_int(qdict, "index", -1); + VirtioQueueElement *e; + VirtioRingDescList *list; + + e = qmp_x_query_virtio_queue_element(path, queue, index != -1, + index, &err); + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s\n", e->name); + monitor_printf(mon, " index: %d\n", e->index); + monitor_printf(mon, " desc:\n"); + monitor_printf(mon, " descs:\n"); + + list = e->descs; + while (list) { + monitor_printf(mon, " addr 0x%"PRIx64" len %d", + list->value->addr, list->value->len); + if (list->value->flags) { + strList *flag = list->value->flags; + monitor_printf(mon, " ("); + while (flag) { + monitor_printf(mon, "%s", flag->value); + flag = flag->next; + if (flag) { + monitor_printf(mon, ", "); + } + } + monitor_printf(mon, ")"); + } + list = list->next; + if (list) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + monitor_printf(mon, " avail:\n"); + monitor_printf(mon, " flags: %d\n", e->avail->flags); + monitor_printf(mon, " idx: %d\n", e->avail->idx); + monitor_printf(mon, " ring: %d\n", e->avail->ring); + monitor_printf(mon, " used:\n"); + monitor_printf(mon, " flags: %d\n", e->used->flags); + monitor_printf(mon, " idx: %d\n", e->used->idx); + + qapi_free_VirtioQueueElement(e); +} diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 23c470977e..1cd258135d 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1366,7 +1366,8 @@ static const VMStateDescription vmstate_virtio_iommu = { }; static Property virtio_iommu_properties[] = { - DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, "PCI", PCIBus *), + DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, + TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_BOOL("boot-bypass", VirtIOIOMMU, boot_bypass, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/block/block-common.h b/include/block/block-common.h index 41686810de..469300fe8d 100644 --- a/include/block/block-common.h +++ b/include/block/block-common.h @@ -45,11 +45,14 @@ * - co_wrapper_mixed_bdrv_rdlock are co_wrapper_mixed functions but * automatically take and release the graph rdlock when creating a new * coroutine. + * + * These functions should not be called from a coroutine_fn; instead, + * call the wrapped function directly. */ -#define co_wrapper -#define co_wrapper_mixed -#define co_wrapper_bdrv_rdlock -#define co_wrapper_mixed_bdrv_rdlock +#define co_wrapper no_coroutine_fn +#define co_wrapper_mixed no_coroutine_fn coroutine_mixed_fn +#define co_wrapper_bdrv_rdlock no_coroutine_fn +#define co_wrapper_mixed_bdrv_rdlock no_coroutine_fn coroutine_mixed_fn #include "block/blockjob.h" diff --git a/include/block/block-io.h b/include/block/block-io.h index 3398351596..614cbd7eda 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -76,9 +76,15 @@ 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); +int64_t coroutine_fn bdrv_co_nb_sectors(BlockDriverState *bs); +int64_t co_wrapper_mixed bdrv_nb_sectors(BlockDriverState *bs); + +int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs); +int64_t co_wrapper_mixed bdrv_getlength(BlockDriverState *bs); + +int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs); +int64_t co_wrapper 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); @@ -136,16 +142,23 @@ bool bdrv_is_read_only(BlockDriverState *bs); bool bdrv_is_writable(BlockDriverState *bs); bool bdrv_is_sg(BlockDriverState *bs); int bdrv_get_flags(BlockDriverState *bs); -bool bdrv_is_inserted(BlockDriverState *bs); -void bdrv_lock_medium(BlockDriverState *bs, bool locked); -void bdrv_eject(BlockDriverState *bs, bool eject_flag); + +bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs); +bool co_wrapper bdrv_is_inserted(BlockDriverState *bs); + +void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked); +void coroutine_fn bdrv_co_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); + +int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); +int co_wrapper_mixed bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); + ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, Error **errp); BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); @@ -178,7 +191,10 @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); void bdrv_enable_copy_on_read(BlockDriverState *bs); void bdrv_disable_copy_on_read(BlockDriverState *bs); -void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); +void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs, + BlkdebugEvent event); +void co_wrapper_mixed bdrv_debug_event(BlockDriverState *bs, + BlkdebugEvent event); #define BLKDBG_EVENT(child, evt) \ do { \ @@ -213,15 +229,10 @@ AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs); */ 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); +void coroutine_fn bdrv_co_io_plug(BlockDriverState *bs); +void coroutine_fn bdrv_co_io_unplug(BlockDriverState *bs); bool coroutine_fn bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 887ace7dbd..ba2e0fce25 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -680,8 +680,10 @@ struct BlockDriver { 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); + int64_t coroutine_fn (*bdrv_co_getlength)(BlockDriverState *bs); + int64_t coroutine_fn (*bdrv_co_get_allocated_file_size)( + BlockDriverState *bs); + BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs, Error **errp); @@ -691,22 +693,23 @@ struct BlockDriver { int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); - int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); + int coroutine_fn (*bdrv_co_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 GRAPH_RDLOCK_PTR (*bdrv_save_vmstate)( + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_save_vmstate)( BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); - int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_load_vmstate)( + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_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); + bool coroutine_fn (*bdrv_co_is_inserted)(BlockDriverState *bs); + void coroutine_fn (*bdrv_co_eject)(BlockDriverState *bs, bool eject_flag); + void coroutine_fn (*bdrv_co_lock_medium)(BlockDriverState *bs, bool locked); /* to control generic scsi devices */ BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, @@ -722,11 +725,12 @@ struct BlockDriver { int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_check)( BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix); - void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); + void coroutine_fn (*bdrv_co_debug_event)(BlockDriverState *bs, + BlkdebugEvent event); /* io queue for linux-aio */ - void (*bdrv_io_plug)(BlockDriverState *bs); - void (*bdrv_io_unplug)(BlockDriverState *bs); + void coroutine_fn (*bdrv_co_io_plug)(BlockDriverState *bs); + void coroutine_fn (*bdrv_co_io_unplug)(BlockDriverState *bs); /** * bdrv_drain_begin is called if implemented in the beginning of a diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 44367219f4..4430bf4c4a 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -122,7 +122,10 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); -int refresh_total_sectors(BlockDriverState *bs, int64_t hint); +int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs, + int64_t hint); +int co_wrapper_mixed +bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint); BdrvChild *bdrv_cow_child(BlockDriverState *bs); BdrvChild *bdrv_filter_child(BlockDriverState *bs); diff --git a/include/block/nbd.h b/include/block/nbd.h index 4ede3b2bd0..a4c98169c3 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -24,6 +24,7 @@ #include "io/channel-socket.h" #include "crypto/tlscreds.h" #include "qapi/error.h" +#include "qemu/bswap.h" extern const BlockExportDriver blk_exp_nbd; diff --git a/include/block/qapi.h b/include/block/qapi.h index 865fb974d4..8773b9b191 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -35,11 +35,21 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, int bdrv_query_snapshot_info_list(BlockDriverState *bs, SnapshotInfoList **p_list, Error **errp); +void bdrv_query_block_node_info(BlockDriverState *bs, + BlockNodeInfo **p_info, + Error **errp); void bdrv_query_image_info(BlockDriverState *bs, ImageInfo **p_info, + bool flat, + bool skip_implicit_filters, Error **errp); +void bdrv_query_block_graph_info(BlockDriverState *bs, + BlockGraphInfo **p_info, + Error **errp); void bdrv_snapshot_dump(QEMUSnapshotInfo *sn); -void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec); -void bdrv_image_info_dump(ImageInfo *info); +void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, + const char *prefix, + int indentation); +void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol); #endif diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h index c4b1bda632..7a3f04b58c 100644 --- a/include/exec/helper-proto.h +++ b/include/exec/helper-proto.h @@ -6,34 +6,49 @@ #include "exec/helper-head.h" +/* + * Work around an issue with --enable-lto, in which GCC's ipa-split pass + * decides to split out the noreturn code paths that raise an exception, + * taking the __builtin_return_address() along into the new function, + * where it no longer computes a value that returns to TCG generated code. + * Despite the name, the noinline attribute affects splitter, so this + * prevents the optimization in question. Given that helpers should not + * otherwise be called directly, this should have any other visible effect. + * + * See https://gitlab.com/qemu-project/qemu/-/issues/1454 + */ +#define DEF_HELPER_ATTR __attribute__((noinline)) + #define DEF_HELPER_FLAGS_0(name, flags, ret) \ -dh_ctype(ret) HELPER(name) (void); +dh_ctype(ret) HELPER(name) (void) DEF_HELPER_ATTR; #define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1)); +dh_ctype(ret) HELPER(name) (dh_ctype(t1)) DEF_HELPER_ATTR; #define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2)); +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2)) DEF_HELPER_ATTR; #define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3)); +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), \ + dh_ctype(t3)) DEF_HELPER_ATTR; #define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4)); + dh_ctype(t4)) DEF_HELPER_ATTR; #define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4), dh_ctype(t5)); + dh_ctype(t4), dh_ctype(t5)) DEF_HELPER_ATTR; #define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4), dh_ctype(t5), dh_ctype(t6)); + dh_ctype(t4), dh_ctype(t5), \ + dh_ctype(t6)) DEF_HELPER_ATTR; #define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7) \ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ dh_ctype(t4), dh_ctype(t5), dh_ctype(t6), \ - dh_ctype(t7)); + dh_ctype(t7)) DEF_HELPER_ATTR; #define IN_HELPER_PROTO @@ -51,5 +66,6 @@ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ #undef DEF_HELPER_FLAGS_5 #undef DEF_HELPER_FLAGS_6 #undef DEF_HELPER_FLAGS_7 +#undef DEF_HELPER_ATTR #endif /* HELPER_PROTO_H */ diff --git a/include/exec/memory.h b/include/exec/memory.h index c37ffdbcd1..2e602a2fad 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -129,6 +129,32 @@ struct IOMMUTLBEntry { /* * Bitmap for different IOMMUNotifier capabilities. Each notifier can * register with one or multiple IOMMU Notifier capability bit(s). + * + * Normally there're two use cases for the notifiers: + * + * (1) When the device needs accurate synchronizations of the vIOMMU page + * tables, it needs to register with both MAP|UNMAP notifies (which + * is defined as IOMMU_NOTIFIER_IOTLB_EVENTS below). + * + * Regarding to accurate synchronization, it's when the notified + * device maintains a shadow page table and must be notified on each + * guest MAP (page table entry creation) and UNMAP (invalidation) + * events (e.g. VFIO). Both notifications must be accurate so that + * the shadow page table is fully in sync with the guest view. + * + * (2) When the device doesn't need accurate synchronizations of the + * vIOMMU page tables, it needs to register only with UNMAP or + * DEVIOTLB_UNMAP notifies. + * + * It's when the device maintains a cache of IOMMU translations + * (IOTLB) and is able to fill that cache by requesting translations + * from the vIOMMU through a protocol similar to ATS (Address + * Translation Service). + * + * Note that in this mode the vIOMMU will not maintain a shadowed + * page table for the address space, and the UNMAP messages can cover + * more than the pages that used to get mapped. The IOMMU notifiee + * should be able to take care of over-sized invalidations. */ typedef enum { IOMMU_NOTIFIER_NONE = 0, diff --git a/include/hw/acpi/acpi_aml_interface.h b/include/hw/acpi/acpi_aml_interface.h index 436da069d6..11748a8866 100644 --- a/include/hw/acpi/acpi_aml_interface.h +++ b/include/hw/acpi/acpi_aml_interface.h @@ -3,6 +3,7 @@ #include "qom/object.h" #include "hw/acpi/aml-build.h" +#include "hw/qdev-core.h" #define TYPE_ACPI_DEV_AML_IF "acpi-dev-aml-interface" typedef struct AcpiDevAmlIfClass AcpiDevAmlIfClass; @@ -46,4 +47,6 @@ static inline void call_dev_aml_func(DeviceState *dev, Aml *scope) } } +void qbus_build_aml(BusState *bus, Aml *scope); + #endif diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index ea6056ab92..a1648220ff 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -52,8 +52,7 @@ struct AcpiDeviceIfClass { /* <public> */ void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); void (*send_event)(AcpiDeviceIf *adev, AcpiEventStatusBits ev); - void (*madt_cpu)(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, + void (*madt_cpu)(int uid, const CPUArchIdList *apic_ids, GArray *entry, bool force_enabled); }; #endif diff --git a/include/hw/acpi/pci.h b/include/hw/acpi/pci.h index b5deee0a9d..467a99461c 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -27,6 +27,7 @@ #define HW_ACPI_PCI_H #include "hw/acpi/bios-linker-loader.h" +#include "hw/acpi/acpi_aml_interface.h" typedef struct AcpiMcfgInfo { uint64_t base; @@ -36,4 +37,7 @@ typedef struct AcpiMcfgInfo { void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, const char *oem_id, const char *oem_table_id); Aml *aml_pci_device_dsm(void); + +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus); +void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope); #endif diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index c7dd59d7f1..e1ddbea96b 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -109,14 +109,19 @@ typedef enum VirtMSIControllerType { } VirtMSIControllerType; typedef enum VirtGICType { - VIRT_GIC_VERSION_MAX, - VIRT_GIC_VERSION_HOST, - VIRT_GIC_VERSION_2, - VIRT_GIC_VERSION_3, - VIRT_GIC_VERSION_4, + VIRT_GIC_VERSION_MAX = 0, + VIRT_GIC_VERSION_HOST = 1, + /* The concrete GIC values have to match the GIC version number */ + VIRT_GIC_VERSION_2 = 2, + VIRT_GIC_VERSION_3 = 3, + VIRT_GIC_VERSION_4 = 4, VIRT_GIC_VERSION_NOSEL, } VirtGICType; +#define VIRT_GIC_VERSION_2_MASK BIT(VIRT_GIC_VERSION_2) +#define VIRT_GIC_VERSION_3_MASK BIT(VIRT_GIC_VERSION_3) +#define VIRT_GIC_VERSION_4_MASK BIT(VIRT_GIC_VERSION_4) + struct VirtMachineClass { MachineClass parent; bool disallow_affinity_adjustment; diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index dc2c90eedc..926322e242 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -27,6 +27,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(PL011State, PL011) /* This shares the same struct (and cast macro) as the base pl011 device */ #define TYPE_PL011_LUMINARY "pl011_luminary" +/* Depth of UART FIFO in bytes, when FIFO mode is enabled (else depth == 1) */ +#define PL011_FIFO_DEPTH 16 + struct PL011State { SysBusDevice parent_obj; @@ -39,7 +42,7 @@ struct PL011State { uint32_t dmacr; uint32_t int_enabled; uint32_t int_level; - uint32_t read_fifo[16]; + uint32_t read_fifo[PL011_FIFO_DEPTH]; uint32_t ilpr; uint32_t ibrd; uint32_t fbrd; diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 46d973e629..89dcbc5e1e 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -109,7 +109,43 @@ struct VTDAddressSpace { QLIST_ENTRY(VTDAddressSpace) next; /* Superset of notifier flags that this address space has */ IOMMUNotifierFlag notifier_flags; - IOVATree *iova_tree; /* Traces mapped IOVA ranges */ + /* + * @iova_tree traces mapped IOVA ranges. + * + * The tree is not needed if no MAP notifier is registered with current + * VTD address space, because all guest invalidate commands can be + * directly passed to the IOMMU UNMAP notifiers without any further + * reshuffling. + * + * The tree OTOH is required for MAP typed iommu notifiers for a few + * reasons. + * + * Firstly, there's no way to identify whether an PSI (Page Selective + * Invalidations) or DSI (Domain Selective Invalidations) event is an + * MAP or UNMAP event within the message itself. Without having prior + * knowledge of existing state vIOMMU doesn't know whether it should + * notify MAP or UNMAP for a PSI message it received when caching mode + * is enabled (for MAP notifiers). + * + * Secondly, PSI messages received from guest driver can be enlarged in + * range, covers but not limited to what the guest driver wanted to + * invalidate. When the range to invalidates gets bigger than the + * limit of a PSI message, it can even become a DSI which will + * invalidate the whole domain. If the vIOMMU directly notifies the + * registered device with the unmodified range, it may confuse the + * registered drivers (e.g. vfio-pci) on either: + * + * (1) Trying to map the same region more than once (for + * VFIO_IOMMU_MAP_DMA, -EEXIST will trigger), or, + * + * (2) Trying to UNMAP a range that is still partially mapped. + * + * That accuracy is not required for UNMAP-only notifiers, but it is a + * must-to-have for notifiers registered with MAP events, because the + * vIOMMU needs to make sure the shadow page table is always in sync + * with the guest IOMMU pgtables for a device. + */ + IOVATree *iova_tree; }; struct VTDIOTLBEntry { diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index fad97a891d..e8af61f194 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -50,8 +50,9 @@ */ /* Platform virtio definitions */ -#define VIRTIO_MMIO_BASE 0xfeb00000 -#define VIRTIO_CMDLINE_MAXLEN 64 +#define VIRTIO_MMIO_BASE 0xfeb00000 +#define VIRTIO_CMDLINE_MAXLEN 64 +#define VIRTIO_CMDLINE_TOTAL_MAX_LEN ((VIRTIO_CMDLINE_MAXLEN + 1) * 16) #define GED_MMIO_BASE 0xfea00000 #define GED_MMIO_BASE_MEMHP (GED_MMIO_BASE + 0x100) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 88a120bc23..66e3d059ef 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -9,7 +9,6 @@ #include "hw/block/flash.h" #include "hw/i386/x86.h" -#include "hw/acpi/acpi_dev_interface.h" #include "hw/hotplug.h" #include "qom/object.h" #include "hw/i386/sgx-epc.h" @@ -193,9 +192,8 @@ bool pc_system_ovmf_table_find(const char *entry, uint8_t **data, void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* hw/i386/acpi-common.c */ -void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, - bool force_enabled); +void pc_madt_cpu_entry(int uid, const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 6c8a8a92cb..25acd5c34c 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -86,7 +86,6 @@ bool isa_realize_and_unref(ISADevice *dev, ISABus *bus, Error **errp); ISADevice *isa_create_simple(ISABus *bus, const char *name); ISADevice *isa_vga_init(ISABus *bus); -void isa_build_aml(ISABus *bus, Aml *scope); /** * isa_register_ioport: Install an I/O port region on the ISA bus. diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 2e503904dc..990dcdbb2e 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -140,6 +140,15 @@ void fw_cfg_add_bytes_callback(FWCfgState *s, uint16_t key, bool read_only); /** + * fw_cfg_read_bytes_ptr: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * + * Reads an existing fw_cfg data pointer. + */ +void *fw_cfg_read_bytes_ptr(FWCfgState *s, uint16_t key); + +/** * fw_cfg_add_string: * @s: fw_cfg device being modified * @key: selector key value for new fw_cfg item diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 85ee458cd2..d5a40cd058 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -270,7 +270,7 @@ typedef void (*pci_bus_dev_fn)(PCIBus *b, PCIDevice *d, void *opaque); typedef void (*pci_bus_fn)(PCIBus *b, void *opaque); typedef void *(*pci_bus_ret_fn)(PCIBus *b, void *opaque); -bool pci_bus_is_express(PCIBus *bus); +bool pci_bus_is_express(const PCIBus *bus); void pci_root_bus_init(PCIBus *bus, size_t bus_size, DeviceState *parent, const char *name, diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index fd484afb30..6c40e3733f 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -63,7 +63,8 @@ struct PCIESlot { /* Indicates whether any type of hot-plug is allowed on the slot */ bool hotplug; - bool native_hotplug; + /* broken ACPI hotplug compat knob to preserve 6.1 ABI intact */ + bool hide_native_hotplug_cap; QLIST_ENTRY(PCIESlot) next; }; diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h index 1891a19b21..d78e979f05 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -51,5 +51,11 @@ 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); +void hmp_memory_dump(Monitor *mon, const QDict *qdict); +void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict); +void hmp_info_registers(Monitor *mon, const QDict *qdict); +void hmp_gva2gpa(Monitor *mon, const QDict *qdict); +void hmp_gpa2hva(Monitor *mon, const QDict *qdict); +void hmp_gpa2hpa(Monitor *mon, const QDict *qdict); #endif /* MONITOR_HMP_TARGET_H */ diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 1b3bdcb446..2220f14fc9 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -18,6 +18,8 @@ #include "qapi/qapi-types-common.h" bool hmp_handle_error(Monitor *mon, Error *err); +void hmp_help_cmd(Monitor *mon, const char *name); +strList *hmp_split_at_comma(const char *str); void hmp_info_name(Monitor *mon, const QDict *qdict); void hmp_info_version(Monitor *mon, const QDict *qdict); @@ -54,6 +56,7 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict); void hmp_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_nmi(Monitor *mon, const QDict *qdict); +void hmp_info_network(Monitor *mon, const QDict *qdict); void hmp_set_link(Monitor *mon, const QDict *qdict); void hmp_balloon(Monitor *mon, const QDict *qdict); void hmp_loadvm(Monitor *mon, const QDict *qdict); @@ -77,6 +80,9 @@ void hmp_change_vnc(Monitor *mon, const char *device, const char *target, const char *arg, const char *read_only, bool force, Error **errp); #endif +void hmp_change_medium(Monitor *mon, const char *device, const char *target, + const char *arg, const char *read_only, bool force, + Error **errp); void hmp_migrate(Monitor *mon, const QDict *qdict); void hmp_device_add(Monitor *mon, const QDict *qdict); void hmp_device_del(Monitor *mon, const QDict *qdict); @@ -150,6 +156,27 @@ void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); void hmp_human_readable_text_helper(Monitor *mon, HumanReadableText *(*qmp_handler)(Error **)); void hmp_info_stats(Monitor *mon, const QDict *qdict); +void hmp_singlestep(Monitor *mon, const QDict *qdict); +void hmp_watchdog_action(Monitor *mon, const QDict *qdict); void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); +void hmp_info_capture(Monitor *mon, const QDict *qdict); +void hmp_stopcapture(Monitor *mon, const QDict *qdict); +void hmp_wavcapture(Monitor *mon, const QDict *qdict); +void hmp_trace_event(Monitor *mon, const QDict *qdict); +void hmp_trace_file(Monitor *mon, const QDict *qdict); +void hmp_info_trace_events(Monitor *mon, const QDict *qdict); +void hmp_help(Monitor *mon, const QDict *qdict); +void hmp_info_help(Monitor *mon, const QDict *qdict); +void hmp_info_sync_profile(Monitor *mon, const QDict *qdict); +void hmp_info_history(Monitor *mon, const QDict *qdict); +void hmp_logfile(Monitor *mon, const QDict *qdict); +void hmp_log(Monitor *mon, const QDict *qdict); +void hmp_gdbserver(Monitor *mon, const QDict *qdict); +void hmp_print(Monitor *mon, const QDict *qdict); +void hmp_sum(Monitor *mon, const QDict *qdict); +void hmp_ioport_read(Monitor *mon, const QDict *qdict); +void hmp_ioport_write(Monitor *mon, const QDict *qdict); +void hmp_boot_set(Monitor *mon, const QDict *qdict); +void hmp_info_mtree(Monitor *mon, const QDict *qdict); #endif diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 1e6f4c9bd7..033390f699 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -35,6 +35,7 @@ int monitor_puts(Monitor *mon, const char *str); int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); int monitor_printf(Monitor *mon, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +void monitor_printc(Monitor *mon, int ch); void monitor_flush(Monitor *mon); int monitor_set_cpu(Monitor *mon, int cpu_index); int monitor_get_cpu_index(Monitor *mon); diff --git a/include/monitor/qmp-helpers.h b/include/monitor/qmp-helpers.h index 4718c63c73..318c3357a2 100644 --- a/include/monitor/qmp-helpers.h +++ b/include/monitor/qmp-helpers.h @@ -22,5 +22,8 @@ bool qmp_add_client_vnc(int fd, bool has_skipauth, bool skipauth, bool qmp_add_client_dbus_display(int fd, bool has_skipauth, bool skipauth, bool has_tls, bool tls, Error **errp); #endif +bool qmp_add_client_char(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, const char *protocol, + Error **errp); #endif diff --git a/include/net/net.h b/include/net/net.h index dc20b31e9f..fad589cc1d 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -115,6 +115,8 @@ struct NetClientState { QTAILQ_HEAD(, NetFilterState) filters; }; +typedef QTAILQ_HEAD(NetClientStateList, NetClientState) NetClientStateList; + typedef struct NICState { NetClientState *ncs; NICConf *conf; @@ -196,7 +198,6 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models, const char *default_model); void print_net_client(Monitor *mon, NetClientState *nc); -void hmp_info_network(Monitor *mon, const QDict *qdict); void net_socket_rs_init(SocketReadState *rs, SocketReadStateFinalize *finalize, bool vnet_hdr); @@ -222,6 +223,7 @@ extern NICInfo nd_table[MAX_NICS]; extern const char *host_net_devices[]; /* from net.c */ +extern NetClientStateList net_clients; bool netdev_is_modern(const char *optarg); void netdev_parse_modern(const char *optarg); void net_client_parse(QemuOptsList *opts_list, const char *str); diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 346d05f2aa..3cbe52246b 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -1,97 +1,44 @@ #ifndef BSWAP_H #define BSWAP_H -#ifdef CONFIG_MACHINE_BSWAP_H -# include <sys/endian.h> -# include <machine/bswap.h> -#elif defined(__FreeBSD__) -# include <sys/endian.h> -#elif defined(__HAIKU__) -# include <endian.h> -#elif defined(CONFIG_BYTESWAP_H) -# include <byteswap.h> -#define BSWAP_FROM_BYTESWAP -# else -#define BSWAP_FROM_FALLBACKS -#endif /* ! CONFIG_MACHINE_BSWAP_H */ - #ifdef __cplusplus extern "C" { #endif -#ifdef BSWAP_FROM_BYTESWAP -static inline uint16_t bswap16(uint16_t x) -{ - return bswap_16(x); -} - -static inline uint32_t bswap32(uint32_t x) -{ - return bswap_32(x); -} - -static inline uint64_t bswap64(uint64_t x) -{ - return bswap_64(x); -} -#endif - -#ifdef BSWAP_FROM_FALLBACKS -static inline uint16_t bswap16(uint16_t x) -{ - return (((x & 0x00ff) << 8) | - ((x & 0xff00) >> 8)); -} - -static inline uint32_t bswap32(uint32_t x) -{ - return (((x & 0x000000ffU) << 24) | - ((x & 0x0000ff00U) << 8) | - ((x & 0x00ff0000U) >> 8) | - ((x & 0xff000000U) >> 24)); -} - -static inline uint64_t bswap64(uint64_t x) -{ - return (((x & 0x00000000000000ffULL) << 56) | - ((x & 0x000000000000ff00ULL) << 40) | - ((x & 0x0000000000ff0000ULL) << 24) | - ((x & 0x00000000ff000000ULL) << 8) | - ((x & 0x000000ff00000000ULL) >> 8) | - ((x & 0x0000ff0000000000ULL) >> 24) | - ((x & 0x00ff000000000000ULL) >> 40) | - ((x & 0xff00000000000000ULL) >> 56)); -} -#endif - -#undef BSWAP_FROM_BYTESWAP -#undef BSWAP_FROM_FALLBACKS +#undef bswap16 +#define bswap16(_x) __builtin_bswap16(_x) +#undef bswap32 +#define bswap32(_x) __builtin_bswap32(_x) +#undef bswap64 +#define bswap64(_x) __builtin_bswap64(_x) static inline void bswap16s(uint16_t *s) { - *s = bswap16(*s); + *s = __builtin_bswap16(*s); } static inline void bswap32s(uint32_t *s) { - *s = bswap32(*s); + *s = __builtin_bswap32(*s); } static inline void bswap64s(uint64_t *s) { - *s = bswap64(*s); + *s = __builtin_bswap64(*s); } #if HOST_BIG_ENDIAN #define be_bswap(v, size) (v) -#define le_bswap(v, size) glue(bswap, size)(v) +#define le_bswap(v, size) glue(__builtin_bswap, size)(v) #define be_bswaps(v, size) -#define le_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0) +#define le_bswaps(p, size) \ + do { *p = glue(__builtin_bswap, size)(*p); } while (0) #else #define le_bswap(v, size) (v) -#define be_bswap(v, size) glue(bswap, size)(v) +#define be_bswap(v, size) glue(__builtin_bswap, size)(v) #define le_bswaps(v, size) -#define be_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0) +#define be_bswaps(p, size) \ + do { *p = glue(__builtin_bswap, size)(*p); } while (0) #endif /** diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index c850001408..88c9facbf2 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -171,7 +171,51 @@ extern "C" { * .... * } */ +#ifdef __clang__ +#define coroutine_fn __attribute__((__annotate__("coroutine_fn"))) +#else #define coroutine_fn +#endif + +/** + * Mark a function that can suspend when executed in coroutine context, + * but can handle running in non-coroutine context too. + */ +#ifdef __clang__ +#define coroutine_mixed_fn __attribute__((__annotate__("coroutine_mixed_fn"))) +#else +#define coroutine_mixed_fn +#endif + +/** + * Mark a function that should not be called from a coroutine context. + * Usually there will be an analogous, coroutine_fn function that should + * be used instead. + * + * When the function is also marked as coroutine_mixed_fn, the function should + * only be called if the caller does not know whether it is in coroutine + * context. + * + * Functions that are only no_coroutine_fn, on the other hand, should not + * be called from within coroutines at all. This for example includes + * functions that block. + * + * In the future it would be nice to enable compiler or static checker + * support for catching such errors. This annotation is the first step + * towards this, and in the meantime it serves as documentation. + * + * For example: + * + * static void no_coroutine_fn foo(void) { + * .... + * } + */ +#ifdef __clang__ +#define no_coroutine_fn __attribute__((__annotate__("no_coroutine_fn"))) +#else +#define no_coroutine_fn +#endif + /* * For mingw, as of v6.0.0, the function implementing the assert macro is diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index a772e14193..fb338ba576 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -59,6 +59,8 @@ get_plugin_meminfo_rw(qemu_plugin_meminfo_t i) #ifdef CONFIG_PLUGIN extern QemuOptsList qemu_plugin_opts; +#define QEMU_PLUGIN_ASSERT(cond) g_assert(cond) + static inline void qemu_plugin_add_opts(void) { qemu_add_opts(&qemu_plugin_opts); @@ -118,7 +120,10 @@ struct qemu_plugin_insn { void *haddr; GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES]; bool calls_helpers; + + /* if set, the instruction calls helpers that might access guest memory */ bool mem_helper; + bool mem_only; }; @@ -158,6 +163,10 @@ struct qemu_plugin_tb { void *haddr1; void *haddr2; bool mem_only; + + /* if set, the TB calls helpers that might access guest memory */ + bool mem_helper; + GArray *cbs[PLUGIN_N_CB_SUBTYPES]; }; @@ -243,6 +252,8 @@ void qemu_plugin_user_postfork(bool is_child); #else /* !CONFIG_PLUGIN */ +#define QEMU_PLUGIN_ASSERT(cond) + static inline void qemu_plugin_add_opts(void) { } diff --git a/include/qemu/readline.h b/include/qemu/readline.h index 622aa4564f..b05e4782da 100644 --- a/include/qemu/readline.h +++ b/include/qemu/readline.h @@ -44,6 +44,8 @@ typedef struct ReadLineState { } ReadLineState; void readline_add_completion(ReadLineState *rs, const char *str); +void readline_add_completion_of(ReadLineState *rs, + const char *pfx, const char *str); void readline_set_completion_index(ReadLineState *rs, int completion_index); const char *readline_get_history(ReadLineState *rs, unsigned int index); diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 7c6703bce3..7841084199 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -237,11 +237,10 @@ static inline void qemu_spin_init(QemuSpin *spin) #endif } -/* const parameter because the only purpose here is the TSAN annotation */ -static inline void qemu_spin_destroy(const QemuSpin *spin) +static inline void qemu_spin_destroy(QemuSpin *spin) { #ifdef CONFIG_TSAN - __tsan_mutex_destroy((void *)spin, __tsan_mutex_not_static); + __tsan_mutex_destroy(spin, __tsan_mutex_not_static); #endif } diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h index 031a27ba10..b1196ab93c 100644 --- a/include/sysemu/block-backend-io.h +++ b/include/sysemu/block-backend-io.h @@ -54,13 +54,26 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, void blk_inc_in_flight(BlockBackend *blk); void blk_dec_in_flight(BlockBackend *blk); -bool blk_is_inserted(BlockBackend *blk); + +bool coroutine_fn blk_co_is_inserted(BlockBackend *blk); +bool co_wrapper_mixed 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 coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked); +void co_wrapper blk_lock_medium(BlockBackend *blk, bool locked); + +void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag); +void co_wrapper blk_eject(BlockBackend *blk, bool eject_flag); + +int64_t coroutine_fn blk_co_getlength(BlockBackend *blk); +int64_t co_wrapper_mixed blk_getlength(BlockBackend *blk); + void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); -int64_t blk_nb_sectors(BlockBackend *blk); + +int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk); +int64_t co_wrapper_mixed 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); @@ -74,8 +87,12 @@ 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_io_plug(BlockBackend *blk); -void blk_io_unplug(BlockBackend *blk); +void coroutine_fn blk_co_io_plug(BlockBackend *blk); +void co_wrapper blk_io_plug(BlockBackend *blk); + +void coroutine_fn blk_co_io_unplug(BlockBackend *blk); +void co_wrapper 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, diff --git a/include/monitor/stats.h b/include/sysemu/stats.h index fcf0983154..fcf0983154 100644 --- a/include/monitor/stats.h +++ b/include/sysemu/stats.h diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 6f497172f8..c5112da0ef 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -155,13 +155,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_rem_i64 0 #endif -/* For 32-bit targets, some sort of unsigned widening multiply is required. */ -#if TCG_TARGET_REG_BITS == 32 \ - && !(defined(TCG_TARGET_HAS_mulu2_i32) \ - || defined(TCG_TARGET_HAS_muluh_i32)) -# error "Missing unsigned widening multiply" -#endif - #if !defined(TCG_TARGET_HAS_v64) \ && !defined(TCG_TARGET_HAS_v128) \ && !defined(TCG_TARGET_HAS_v256) @@ -405,6 +398,8 @@ typedef TCGv_ptr TCGv_env; #define TCG_CALL_NO_SIDE_EFFECTS 0x0004 /* Helper is G_NORETURN. */ #define TCG_CALL_NO_RETURN 0x0008 +/* Helper is part of Plugins. */ +#define TCG_CALL_PLUGIN 0x0010 /* convenience version of most used call flags */ #define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS diff --git a/linux-user/include/host/ppc/host-signal.h b/linux-user/include/host/ppc/host-signal.h new file mode 100644 index 0000000000..de25c803f5 --- /dev/null +++ b/linux-user/include/host/ppc/host-signal.h @@ -0,0 +1,39 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2022 Linaro Ltd. + * + * 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 + +#include <asm/ptrace.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/meson.build b/meson.build index cccd19f864..4ba3bf3431 100644 --- a/meson.build +++ b/meson.build @@ -2013,8 +2013,6 @@ if rdma.found() endif # has_header_symbol -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_FALLOCATE_PUNCH_HOLE', @@ -2032,10 +2030,6 @@ config_host_data.set('CONFIG_INOTIFY', cc.has_header_symbol('sys/inotify.h', 'inotify_init')) config_host_data.set('CONFIG_INOTIFY1', cc.has_header_symbol('sys/inotify.h', 'inotify_init1')) -config_host_data.set('CONFIG_MACHINE_BSWAP_H', - cc.has_header_symbol('machine/bswap.h', 'bswap32', - prefix: '''#include <sys/endian.h> - #include <sys/types.h>''')) config_host_data.set('CONFIG_PRCTL_PR_SET_TIMERSLACK', cc.has_header_symbol('sys/prctl.h', 'PR_SET_TIMERSLACK')) config_host_data.set('CONFIG_RTNETLINK', @@ -2777,7 +2771,7 @@ config_host_data.set('CONFIG_SLIRP', slirp.found()) genh += configure_file(output: 'config-host.h', configuration: config_host_data) hxtool = find_program('scripts/hxtool') -shaderinclude = find_program('scripts/shaderinclude.pl') +shaderinclude = find_program('scripts/shaderinclude.py') qapi_gen = find_program('scripts/qapi-gen.py') qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', meson.current_source_dir() / 'scripts/qapi/commands.py', @@ -3130,6 +3124,7 @@ subdir('monitor') subdir('net') subdir('replay') subdir('semihosting') +subdir('stats') subdir('tcg') subdir('fpu') subdir('accel') diff --git a/migration/meson.build b/migration/meson.build index 690487cf1a..a9e7e18793 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -18,6 +18,7 @@ softmmu_ss.add(files( 'exec.c', 'fd.c', 'global_state.c', + 'migration-hmp-cmds.c', 'migration.c', 'multifd.c', 'multifd-zlib.c', diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c new file mode 100644 index 0000000000..ef25bc8929 --- /dev/null +++ b/migration/migration-hmp-cmds.c @@ -0,0 +1,807 @@ +/* + * HMP commands related to migration + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "block/qapi.h" +#include "migration/misc.h" +#include "migration/snapshot.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-migration.h" +#include "qapi/qapi-visit-migration.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qerror.h" +#include "qapi/string-input-visitor.h" +#include "qapi/string-output-visitor.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" +#include "sysemu/runstate.h" +#include "ui/qemu-spice.h" + +void hmp_info_migrate(Monitor *mon, const QDict *qdict) +{ + MigrationInfo *info; + + info = qmp_query_migrate(NULL); + + migration_global_dump(mon); + + if (info->blocked_reasons) { + strList *reasons = info->blocked_reasons; + monitor_printf(mon, "Outgoing migration blocked:\n"); + while (reasons) { + monitor_printf(mon, " %s\n", reasons->value); + reasons = reasons->next; + } + } + + if (info->has_status) { + monitor_printf(mon, "Migration status: %s", + MigrationStatus_str(info->status)); + if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { + monitor_printf(mon, " (%s)\n", info->error_desc); + } else { + monitor_printf(mon, "\n"); + } + + monitor_printf(mon, "total time: %" PRIu64 " ms\n", + info->total_time); + if (info->has_expected_downtime) { + monitor_printf(mon, "expected downtime: %" PRIu64 " ms\n", + info->expected_downtime); + } + if (info->has_downtime) { + monitor_printf(mon, "downtime: %" PRIu64 " ms\n", + info->downtime); + } + if (info->has_setup_time) { + monitor_printf(mon, "setup: %" PRIu64 " ms\n", + info->setup_time); + } + } + + if (info->ram) { + monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n", + info->ram->transferred >> 10); + monitor_printf(mon, "throughput: %0.2f mbps\n", + info->ram->mbps); + monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n", + info->ram->remaining >> 10); + monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n", + info->ram->total >> 10); + monitor_printf(mon, "duplicate: %" PRIu64 " pages\n", + info->ram->duplicate); + monitor_printf(mon, "skipped: %" PRIu64 " pages\n", + info->ram->skipped); + monitor_printf(mon, "normal: %" PRIu64 " pages\n", + info->ram->normal); + monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n", + info->ram->normal_bytes >> 10); + monitor_printf(mon, "dirty sync count: %" PRIu64 "\n", + info->ram->dirty_sync_count); + monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", + info->ram->page_size >> 10); + monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", + info->ram->multifd_bytes >> 10); + monitor_printf(mon, "pages-per-second: %" PRIu64 "\n", + info->ram->pages_per_second); + + if (info->ram->dirty_pages_rate) { + monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", + info->ram->dirty_pages_rate); + } + if (info->ram->postcopy_requests) { + monitor_printf(mon, "postcopy request count: %" PRIu64 "\n", + info->ram->postcopy_requests); + } + if (info->ram->precopy_bytes) { + monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n", + info->ram->precopy_bytes >> 10); + } + if (info->ram->downtime_bytes) { + monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n", + info->ram->downtime_bytes >> 10); + } + if (info->ram->postcopy_bytes) { + monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n", + info->ram->postcopy_bytes >> 10); + } + if (info->ram->dirty_sync_missed_zero_copy) { + monitor_printf(mon, + "Zero-copy-send fallbacks happened: %" PRIu64 " times\n", + info->ram->dirty_sync_missed_zero_copy); + } + } + + if (info->disk) { + monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n", + info->disk->transferred >> 10); + monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n", + info->disk->remaining >> 10); + monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n", + info->disk->total >> 10); + } + + if (info->xbzrle_cache) { + monitor_printf(mon, "cache size: %" PRIu64 " bytes\n", + info->xbzrle_cache->cache_size); + monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n", + info->xbzrle_cache->bytes >> 10); + monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n", + info->xbzrle_cache->pages); + monitor_printf(mon, "xbzrle cache miss: %" PRIu64 " pages\n", + info->xbzrle_cache->cache_miss); + monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n", + info->xbzrle_cache->cache_miss_rate); + monitor_printf(mon, "xbzrle encoding rate: %0.2f\n", + info->xbzrle_cache->encoding_rate); + monitor_printf(mon, "xbzrle overflow: %" PRIu64 "\n", + info->xbzrle_cache->overflow); + } + + if (info->compression) { + monitor_printf(mon, "compression pages: %" PRIu64 " pages\n", + info->compression->pages); + monitor_printf(mon, "compression busy: %" PRIu64 "\n", + info->compression->busy); + monitor_printf(mon, "compression busy rate: %0.2f\n", + info->compression->busy_rate); + monitor_printf(mon, "compressed size: %" PRIu64 " kbytes\n", + info->compression->compressed_size >> 10); + monitor_printf(mon, "compression rate: %0.2f\n", + info->compression->compression_rate); + } + + if (info->has_cpu_throttle_percentage) { + monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", + info->cpu_throttle_percentage); + } + + if (info->has_postcopy_blocktime) { + monitor_printf(mon, "postcopy blocktime: %u\n", + info->postcopy_blocktime); + } + + if (info->has_postcopy_vcpu_blocktime) { + Visitor *v; + char *str; + v = string_output_visitor_new(false, &str); + visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, + &error_abort); + visit_complete(v, &str); + monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str); + g_free(str); + visit_free(v); + } + if (info->has_socket_address) { + SocketAddressList *addr; + + monitor_printf(mon, "socket address: [\n"); + + for (addr = info->socket_address; addr; addr = addr->next) { + char *s = socket_uri(addr->value); + monitor_printf(mon, "\t%s\n", s); + g_free(s); + } + monitor_printf(mon, "]\n"); + } + + if (info->vfio) { + monitor_printf(mon, "vfio device transferred: %" PRIu64 " kbytes\n", + info->vfio->transferred >> 10); + } + + qapi_free_MigrationInfo(info); +} + +void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) +{ + MigrationCapabilityStatusList *caps, *cap; + + caps = qmp_query_migrate_capabilities(NULL); + + if (caps) { + for (cap = caps; cap; cap = cap->next) { + monitor_printf(mon, "%s: %s\n", + MigrationCapability_str(cap->value->capability), + cap->value->state ? "on" : "off"); + } + } + + qapi_free_MigrationCapabilityStatusList(caps); +} + +void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) +{ + MigrationParameters *params; + + params = qmp_query_migrate_parameters(NULL); + + if (params) { + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL), + params->announce_initial); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX), + params->announce_max); + monitor_printf(mon, "%s: %" PRIu64 "\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS), + params->announce_rounds); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP), + params->announce_step); + assert(params->has_compress_level); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL), + params->compress_level); + assert(params->has_compress_threads); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS), + params->compress_threads); + assert(params->has_compress_wait_thread); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD), + params->compress_wait_thread ? "on" : "off"); + assert(params->has_decompress_threads); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), + params->decompress_threads); + assert(params->has_throttle_trigger_threshold); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), + params->throttle_trigger_threshold); + assert(params->has_cpu_throttle_initial); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL), + params->cpu_throttle_initial); + assert(params->has_cpu_throttle_increment); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT), + params->cpu_throttle_increment); + assert(params->has_cpu_throttle_tailslow); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW), + params->cpu_throttle_tailslow ? "on" : "off"); + assert(params->has_max_cpu_throttle); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE), + params->max_cpu_throttle); + assert(params->tls_creds); + monitor_printf(mon, "%s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS), + params->tls_creds); + assert(params->tls_hostname); + monitor_printf(mon, "%s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME), + params->tls_hostname); + assert(params->has_max_bandwidth); + monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH), + params->max_bandwidth); + assert(params->has_downtime_limit); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT), + params->downtime_limit); + assert(params->has_x_checkpoint_delay); + monitor_printf(mon, "%s: %u ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY), + params->x_checkpoint_delay); + assert(params->has_block_incremental); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL), + params->block_incremental ? "on" : "off"); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), + params->multifd_channels); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION), + MultiFDCompression_str(params->multifd_compression)); + monitor_printf(mon, "%s: %" PRIu64 " bytes\n", + MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), + params->xbzrle_cache_size); + monitor_printf(mon, "%s: %" PRIu64 "\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), + params->max_postcopy_bandwidth); + monitor_printf(mon, "%s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), + params->tls_authz); + + if (params->has_block_bitmap_mapping) { + const BitmapMigrationNodeAliasList *bmnal; + + monitor_printf(mon, "%s:\n", + MigrationParameter_str( + MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING)); + + for (bmnal = params->block_bitmap_mapping; + bmnal; + bmnal = bmnal->next) + { + const BitmapMigrationNodeAlias *bmna = bmnal->value; + const BitmapMigrationBitmapAliasList *bmbal; + + monitor_printf(mon, " '%s' -> '%s'\n", + bmna->node_name, bmna->alias); + + for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) { + const BitmapMigrationBitmapAlias *bmba = bmbal->value; + + monitor_printf(mon, " '%s' -> '%s'\n", + bmba->name, bmba->alias); + } + } + } + } + + qapi_free_MigrationParameters(params); +} + +void hmp_loadvm(Monitor *mon, const QDict *qdict) +{ + int saved_vm_running = runstate_is_running(); + const char *name = qdict_get_str(qdict, "name"); + Error *err = NULL; + + vm_stop(RUN_STATE_RESTORE_VM); + + if (load_snapshot(name, NULL, false, NULL, &err) && saved_vm_running) { + vm_start(); + } + hmp_handle_error(mon, err); +} + +void hmp_savevm(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + save_snapshot(qdict_get_try_str(qdict, "name"), + true, NULL, false, NULL, &err); + hmp_handle_error(mon, err); +} + +void hmp_delvm(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *name = qdict_get_str(qdict, "name"); + + delete_snapshot(name, false, NULL, &err); + hmp_handle_error(mon, err); +} + +void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) +{ + qmp_migrate_cancel(NULL); +} + +void hmp_migrate_continue(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *state = qdict_get_str(qdict, "state"); + int val = qapi_enum_parse(&MigrationStatus_lookup, state, -1, &err); + + if (val >= 0) { + qmp_migrate_continue(val, &err); + } + + hmp_handle_error(mon, err); +} + +void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *uri = qdict_get_str(qdict, "uri"); + + qmp_migrate_incoming(uri, &err); + + hmp_handle_error(mon, err); +} + +void hmp_migrate_recover(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *uri = qdict_get_str(qdict, "uri"); + + qmp_migrate_recover(uri, &err); + + hmp_handle_error(mon, err); +} + +void hmp_migrate_pause(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_migrate_pause(&err); + + hmp_handle_error(mon, err); +} + + +void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) +{ + const char *cap = qdict_get_str(qdict, "capability"); + bool state = qdict_get_bool(qdict, "state"); + Error *err = NULL; + MigrationCapabilityStatusList *caps = NULL; + MigrationCapabilityStatus *value; + int val; + + val = qapi_enum_parse(&MigrationCapability_lookup, cap, -1, &err); + if (val < 0) { + goto end; + } + + value = g_malloc0(sizeof(*value)); + value->capability = val; + value->state = state; + QAPI_LIST_PREPEND(caps, value); + qmp_migrate_set_capabilities(caps, &err); + qapi_free_MigrationCapabilityStatusList(caps); + +end: + hmp_handle_error(mon, err); +} + +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) +{ + const char *param = qdict_get_str(qdict, "parameter"); + const char *valuestr = qdict_get_str(qdict, "value"); + Visitor *v = string_input_visitor_new(valuestr); + MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); + uint64_t valuebw = 0; + uint64_t cache_size; + Error *err = NULL; + int val, ret; + + val = qapi_enum_parse(&MigrationParameter_lookup, param, -1, &err); + if (val < 0) { + goto cleanup; + } + + switch (val) { + case MIGRATION_PARAMETER_COMPRESS_LEVEL: + p->has_compress_level = true; + visit_type_uint8(v, param, &p->compress_level, &err); + break; + case MIGRATION_PARAMETER_COMPRESS_THREADS: + p->has_compress_threads = true; + visit_type_uint8(v, param, &p->compress_threads, &err); + break; + case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD: + p->has_compress_wait_thread = true; + visit_type_bool(v, param, &p->compress_wait_thread, &err); + break; + case MIGRATION_PARAMETER_DECOMPRESS_THREADS: + p->has_decompress_threads = true; + visit_type_uint8(v, param, &p->decompress_threads, &err); + break; + case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: + p->has_throttle_trigger_threshold = true; + visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err); + break; + case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: + p->has_cpu_throttle_initial = true; + visit_type_uint8(v, param, &p->cpu_throttle_initial, &err); + break; + case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT: + p->has_cpu_throttle_increment = true; + visit_type_uint8(v, param, &p->cpu_throttle_increment, &err); + break; + case MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW: + p->has_cpu_throttle_tailslow = true; + visit_type_bool(v, param, &p->cpu_throttle_tailslow, &err); + break; + case MIGRATION_PARAMETER_MAX_CPU_THROTTLE: + p->has_max_cpu_throttle = true; + visit_type_uint8(v, param, &p->max_cpu_throttle, &err); + break; + case MIGRATION_PARAMETER_TLS_CREDS: + p->tls_creds = g_new0(StrOrNull, 1); + p->tls_creds->type = QTYPE_QSTRING; + visit_type_str(v, param, &p->tls_creds->u.s, &err); + break; + case MIGRATION_PARAMETER_TLS_HOSTNAME: + p->tls_hostname = g_new0(StrOrNull, 1); + p->tls_hostname->type = QTYPE_QSTRING; + visit_type_str(v, param, &p->tls_hostname->u.s, &err); + break; + case MIGRATION_PARAMETER_TLS_AUTHZ: + p->tls_authz = g_new0(StrOrNull, 1); + p->tls_authz->type = QTYPE_QSTRING; + visit_type_str(v, param, &p->tls_authz->u.s, &err); + break; + case MIGRATION_PARAMETER_MAX_BANDWIDTH: + p->has_max_bandwidth = true; + /* + * Can't use visit_type_size() here, because it + * defaults to Bytes rather than Mebibytes. + */ + ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw); + if (ret < 0 || valuebw > INT64_MAX + || (size_t)valuebw != valuebw) { + error_setg(&err, "Invalid size %s", valuestr); + break; + } + p->max_bandwidth = valuebw; + break; + case MIGRATION_PARAMETER_DOWNTIME_LIMIT: + p->has_downtime_limit = true; + visit_type_size(v, param, &p->downtime_limit, &err); + break; + case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY: + p->has_x_checkpoint_delay = true; + visit_type_uint32(v, param, &p->x_checkpoint_delay, &err); + break; + case MIGRATION_PARAMETER_BLOCK_INCREMENTAL: + p->has_block_incremental = true; + visit_type_bool(v, param, &p->block_incremental, &err); + break; + case MIGRATION_PARAMETER_MULTIFD_CHANNELS: + p->has_multifd_channels = true; + visit_type_uint8(v, param, &p->multifd_channels, &err); + break; + case MIGRATION_PARAMETER_MULTIFD_COMPRESSION: + p->has_multifd_compression = true; + visit_type_MultiFDCompression(v, param, &p->multifd_compression, + &err); + break; + case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL: + p->has_multifd_zlib_level = true; + visit_type_uint8(v, param, &p->multifd_zlib_level, &err); + break; + case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL: + p->has_multifd_zstd_level = true; + visit_type_uint8(v, param, &p->multifd_zstd_level, &err); + break; + case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE: + p->has_xbzrle_cache_size = true; + if (!visit_type_size(v, param, &cache_size, &err)) { + break; + } + if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) { + error_setg(&err, "Invalid size %s", valuestr); + break; + } + p->xbzrle_cache_size = cache_size; + break; + case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH: + p->has_max_postcopy_bandwidth = true; + visit_type_size(v, param, &p->max_postcopy_bandwidth, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_INITIAL: + p->has_announce_initial = true; + visit_type_size(v, param, &p->announce_initial, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_MAX: + p->has_announce_max = true; + visit_type_size(v, param, &p->announce_max, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS: + p->has_announce_rounds = true; + visit_type_size(v, param, &p->announce_rounds, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_STEP: + p->has_announce_step = true; + visit_type_size(v, param, &p->announce_step, &err); + break; + case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING: + error_setg(&err, "The block-bitmap-mapping parameter can only be set " + "through QMP"); + break; + default: + assert(0); + } + + if (err) { + goto cleanup; + } + + qmp_migrate_set_parameters(p, &err); + + cleanup: + qapi_free_MigrateSetParameters(p); + visit_free(v); + hmp_handle_error(mon, err); +} + +void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *hostname = qdict_get_str(qdict, "hostname"); + bool has_port = qdict_haskey(qdict, "port"); + int port = qdict_get_try_int(qdict, "port", -1); + bool has_tls_port = qdict_haskey(qdict, "tls-port"); + int tls_port = qdict_get_try_int(qdict, "tls-port", -1); + const char *cert_subject = qdict_get_try_str(qdict, "cert-subject"); + + qmp_client_migrate_info(protocol, hostname, + has_port, port, has_tls_port, tls_port, + cert_subject, &err); + hmp_handle_error(mon, err); +} + +void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + qmp_migrate_start_postcopy(&err); + hmp_handle_error(mon, err); +} + +void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_x_colo_lost_heartbeat(&err); + hmp_handle_error(mon, err); +} + +typedef struct HMPMigrationStatus { + QEMUTimer *timer; + Monitor *mon; + bool is_block_migration; +} HMPMigrationStatus; + +static void hmp_migrate_status_cb(void *opaque) +{ + HMPMigrationStatus *status = opaque; + MigrationInfo *info; + + info = qmp_query_migrate(NULL); + if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE || + info->status == MIGRATION_STATUS_SETUP) { + if (info->disk) { + int progress; + + if (info->disk->remaining) { + progress = info->disk->transferred * 100 / info->disk->total; + } else { + progress = 100; + } + + monitor_printf(status->mon, "Completed %d %%\r", progress); + monitor_flush(status->mon); + } + + timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); + } else { + if (status->is_block_migration) { + monitor_printf(status->mon, "\n"); + } + if (info->error_desc) { + error_report("%s", info->error_desc); + } + monitor_resume(status->mon); + timer_free(status->timer); + g_free(status); + } + + qapi_free_MigrationInfo(info); +} + +void hmp_migrate(Monitor *mon, const QDict *qdict) +{ + bool detach = qdict_get_try_bool(qdict, "detach", false); + bool blk = qdict_get_try_bool(qdict, "blk", false); + bool inc = qdict_get_try_bool(qdict, "inc", false); + bool resume = qdict_get_try_bool(qdict, "resume", false); + const char *uri = qdict_get_str(qdict, "uri"); + Error *err = NULL; + + qmp_migrate(uri, !!blk, blk, !!inc, inc, + false, false, true, resume, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + if (!detach) { + HMPMigrationStatus *status; + + if (monitor_suspend(mon) < 0) { + monitor_printf(mon, "terminal does not allow synchronous " + "migration, continuing detached\n"); + return; + } + + status = g_malloc0(sizeof(*status)); + status->mon = mon; + status->is_block_migration = blk || inc; + status->timer = timer_new_ms(QEMU_CLOCK_REALTIME, hmp_migrate_status_cb, + status); + timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + } +} + +void migrate_set_capability_completion(ReadLineState *rs, int nb_args, + const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + int i; + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + readline_add_completion_of(rs, str, MigrationCapability_str(i)); + } + } else if (nb_args == 3) { + readline_add_completion_of(rs, str, "on"); + readline_add_completion_of(rs, str, "off"); + } +} + +void migrate_set_parameter_completion(ReadLineState *rs, int nb_args, + const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + int i; + for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { + readline_add_completion_of(rs, str, MigrationParameter_str(i)); + } + } +} + +static void vm_completion(ReadLineState *rs, const char *str) +{ + size_t len; + BlockDriverState *bs; + BdrvNextIterator it; + + len = strlen(str); + readline_set_completion_index(rs, len); + + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + SnapshotInfoList *snapshots, *snapshot; + AioContext *ctx = bdrv_get_aio_context(bs); + bool ok = false; + + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs)) { + ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0; + } + aio_context_release(ctx); + if (!ok) { + continue; + } + + snapshot = snapshots; + while (snapshot) { + readline_add_completion_of(rs, str, snapshot->value->name); + readline_add_completion_of(rs, str, snapshot->value->id); + snapshot = snapshot->next; + } + qapi_free_SnapshotInfoList(snapshots); + } + +} + +void delvm_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args == 2) { + vm_completion(rs, str); + } +} + +void loadvm_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args == 2) { + vm_completion(rs, str); + } +} diff --git a/migration/migration.c b/migration/migration.c index 52b5d39244..56859d5869 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -61,6 +61,7 @@ #include "sysemu/cpus.h" #include "yank_functions.h" #include "sysemu/qtest.h" +#include "ui/qemu-spice.h" #define MAX_THROTTLE (128 << 20) /* Migration transfer speed throttling */ @@ -963,6 +964,35 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) return params; } +void qmp_client_migrate_info(const char *protocol, const char *hostname, + bool has_port, int64_t port, + bool has_tls_port, int64_t tls_port, + const char *cert_subject, + Error **errp) +{ + if (strcmp(protocol, "spice") == 0) { + if (!qemu_using_spice(errp)) { + return; + } + + if (!has_port && !has_tls_port) { + error_setg(errp, QERR_MISSING_PARAMETER, "port/tls-port"); + return; + } + + if (qemu_spice.migrate_info(hostname, + has_port ? port : -1, + has_tls_port ? tls_port : -1, + cert_subject)) { + error_setg(errp, "Could not set up display for migration"); + return; + } + return; + } + + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'"); +} + AnnounceParameters *migrate_announce_params(void) { static AnnounceParameters ap; diff --git a/monitor/fds.c b/monitor/fds.c new file mode 100644 index 0000000000..26b39a0ce6 --- /dev/null +++ b/monitor/fds.c @@ -0,0 +1,468 @@ +/* + * QEMU monitor file descriptor passing + * + * Copyright (c) 2003-2004 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. + */ + +#include "qemu/osdep.h" +#include "monitor-internal.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qmp/qerror.h" +#include "qemu/ctype.h" +#include "qemu/cutils.h" +#include "sysemu/runstate.h" + +/* file descriptors passed via SCM_RIGHTS */ +typedef struct mon_fd_t mon_fd_t; +struct mon_fd_t { + char *name; + int fd; + QLIST_ENTRY(mon_fd_t) next; +}; + +/* file descriptor associated with a file descriptor set */ +typedef struct MonFdsetFd MonFdsetFd; +struct MonFdsetFd { + int fd; + bool removed; + char *opaque; + QLIST_ENTRY(MonFdsetFd) next; +}; + +/* file descriptor set containing fds passed via SCM_RIGHTS */ +typedef struct MonFdset MonFdset; +struct MonFdset { + int64_t id; + QLIST_HEAD(, MonFdsetFd) fds; + QLIST_HEAD(, MonFdsetFd) dup_fds; + QLIST_ENTRY(MonFdset) next; +}; + +/* Protects mon_fdsets */ +static QemuMutex mon_fdsets_lock; +static QLIST_HEAD(, MonFdset) mon_fdsets; + +void qmp_getfd(const char *fdname, Error **errp) +{ + Monitor *cur_mon = monitor_cur(); + mon_fd_t *monfd; + int fd, tmp_fd; + + fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); + if (fd == -1) { + error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); + return; + } + + if (qemu_isdigit(fdname[0])) { + close(fd); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", + "a name not starting with a digit"); + return; + } + + QEMU_LOCK_GUARD(&cur_mon->mon_lock); + QLIST_FOREACH(monfd, &cur_mon->fds, next) { + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + tmp_fd = monfd->fd; + monfd->fd = fd; + /* Make sure close() is outside critical section */ + close(tmp_fd); + return; + } + + monfd = g_new0(mon_fd_t, 1); + monfd->name = g_strdup(fdname); + monfd->fd = fd; + + QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); +} + +void qmp_closefd(const char *fdname, Error **errp) +{ + Monitor *cur_mon = monitor_cur(); + mon_fd_t *monfd; + int tmp_fd; + + qemu_mutex_lock(&cur_mon->mon_lock); + QLIST_FOREACH(monfd, &cur_mon->fds, next) { + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + QLIST_REMOVE(monfd, next); + tmp_fd = monfd->fd; + g_free(monfd->name); + g_free(monfd); + qemu_mutex_unlock(&cur_mon->mon_lock); + /* Make sure close() is outside critical section */ + close(tmp_fd); + return; + } + + qemu_mutex_unlock(&cur_mon->mon_lock); + error_setg(errp, "File descriptor named '%s' not found", fdname); +} + +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) +{ + mon_fd_t *monfd; + + QEMU_LOCK_GUARD(&mon->mon_lock); + QLIST_FOREACH(monfd, &mon->fds, next) { + int fd; + + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + fd = monfd->fd; + assert(fd >= 0); + + /* caller takes ownership of fd */ + QLIST_REMOVE(monfd, next); + g_free(monfd->name); + g_free(monfd); + + return fd; + } + + error_setg(errp, "File descriptor named '%s' has not been found", fdname); + return -1; +} + +static void monitor_fdset_cleanup(MonFdset *mon_fdset) +{ + MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd_next; + + QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) { + if ((mon_fdset_fd->removed || + (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) && + runstate_is_running()) { + close(mon_fdset_fd->fd); + g_free(mon_fdset_fd->opaque); + QLIST_REMOVE(mon_fdset_fd, next); + g_free(mon_fdset_fd); + } + } + + if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { + QLIST_REMOVE(mon_fdset, next); + g_free(mon_fdset); + } +} + +void monitor_fdsets_cleanup(void) +{ + MonFdset *mon_fdset; + MonFdset *mon_fdset_next; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { + monitor_fdset_cleanup(mon_fdset); + } +} + +AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, + const char *opaque, Error **errp) +{ + int fd; + Monitor *mon = monitor_cur(); + AddfdInfo *fdinfo; + + fd = qemu_chr_fe_get_msgfd(&mon->chr); + if (fd == -1) { + error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); + goto error; + } + + fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, opaque, errp); + if (fdinfo) { + return fdinfo; + } + +error: + if (fd != -1) { + close(fd); + } + return NULL; +} + +void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd; + char fd_str[60]; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + if (mon_fdset->id != fdset_id) { + continue; + } + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + if (has_fd) { + if (mon_fdset_fd->fd != fd) { + continue; + } + mon_fdset_fd->removed = true; + break; + } else { + mon_fdset_fd->removed = true; + } + } + if (has_fd && !mon_fdset_fd) { + goto error; + } + monitor_fdset_cleanup(mon_fdset); + return; + } + +error: + if (has_fd) { + snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64, + fdset_id, fd); + } else { + snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id); + } + error_setg(errp, "File descriptor named '%s' not found", fd_str); +} + +FdsetInfoList *qmp_query_fdsets(Error **errp) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd; + FdsetInfoList *fdset_list = NULL; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info)); + + fdset_info->fdset_id = mon_fdset->id; + + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + FdsetFdInfo *fdsetfd_info; + + fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info)); + fdsetfd_info->fd = mon_fdset_fd->fd; + fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque); + + QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info); + } + + QAPI_LIST_PREPEND(fdset_list, fdset_info); + } + + return fdset_list; +} + +AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, + const char *opaque, Error **errp) +{ + MonFdset *mon_fdset = NULL; + MonFdsetFd *mon_fdset_fd; + AddfdInfo *fdinfo; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + if (has_fdset_id) { + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + /* Break if match found or match impossible due to ordering by ID */ + if (fdset_id <= mon_fdset->id) { + if (fdset_id < mon_fdset->id) { + mon_fdset = NULL; + } + break; + } + } + } + + if (mon_fdset == NULL) { + int64_t fdset_id_prev = -1; + MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets); + + if (has_fdset_id) { + if (fdset_id < 0) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id", + "a non-negative value"); + return NULL; + } + /* Use specified fdset ID */ + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + mon_fdset_cur = mon_fdset; + if (fdset_id < mon_fdset_cur->id) { + break; + } + } + } else { + /* Use first available fdset ID */ + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + mon_fdset_cur = mon_fdset; + if (fdset_id_prev == mon_fdset_cur->id - 1) { + fdset_id_prev = mon_fdset_cur->id; + continue; + } + break; + } + } + + mon_fdset = g_malloc0(sizeof(*mon_fdset)); + if (has_fdset_id) { + mon_fdset->id = fdset_id; + } else { + mon_fdset->id = fdset_id_prev + 1; + } + + /* The fdset list is ordered by fdset ID */ + if (!mon_fdset_cur) { + QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next); + } else if (mon_fdset->id < mon_fdset_cur->id) { + QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next); + } else { + QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next); + } + } + + mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd)); + mon_fdset_fd->fd = fd; + mon_fdset_fd->removed = false; + mon_fdset_fd->opaque = g_strdup(opaque); + QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next); + + fdinfo = g_malloc0(sizeof(*fdinfo)); + fdinfo->fdset_id = mon_fdset->id; + fdinfo->fd = mon_fdset_fd->fd; + + return fdinfo; +} + +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) +{ +#ifdef _WIN32 + return -ENOENT; +#else + MonFdset *mon_fdset; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd_dup; + int fd = -1; + int dup_fd; + int mon_fd_flags; + + if (mon_fdset->id != fdset_id) { + continue; + } + + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); + if (mon_fd_flags == -1) { + return -1; + } + + if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { + fd = mon_fdset_fd->fd; + break; + } + } + + if (fd == -1) { + errno = EACCES; + return -1; + } + + dup_fd = qemu_dup_flags(fd, flags); + if (dup_fd == -1) { + return -1; + } + + mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup)); + mon_fdset_fd_dup->fd = dup_fd; + QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next); + return dup_fd; + } + + errno = ENOENT; + return -1; +#endif +} + +static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd_dup; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { + if (mon_fdset_fd_dup->fd == dup_fd) { + if (remove) { + QLIST_REMOVE(mon_fdset_fd_dup, next); + g_free(mon_fdset_fd_dup); + if (QLIST_EMPTY(&mon_fdset->dup_fds)) { + monitor_fdset_cleanup(mon_fdset); + } + return -1; + } else { + return mon_fdset->id; + } + } + } + } + + return -1; +} + +int64_t monitor_fdset_dup_fd_find(int dup_fd) +{ + return monitor_fdset_dup_fd_find_remove(dup_fd, false); +} + +void monitor_fdset_dup_fd_remove(int dup_fd) +{ + monitor_fdset_dup_fd_find_remove(dup_fd, true); +} + +int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) +{ + int fd; + + if (!qemu_isdigit(fdname[0]) && mon) { + fd = monitor_get_fd(mon, fdname, errp); + } else { + fd = qemu_parse_fd(fdname); + if (fd < 0) { + error_setg(errp, "Invalid file descriptor number '%s'", + fdname); + } + } + + return fd; +} + +static void __attribute__((__constructor__)) monitor_fds_init(void) +{ + qemu_mutex_init(&mon_fdsets_lock); +} diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c new file mode 100644 index 0000000000..0d3e84d960 --- /dev/null +++ b/monitor/hmp-cmds-target.c @@ -0,0 +1,380 @@ +/* + * Miscellaneous target-dependent HMP commands + * + * Copyright (c) 2003-2004 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. + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "exec/address-spaces.h" +#include "monitor/hmp-target.h" +#include "monitor/monitor-internal.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "sysemu/hw_accel.h" + +/* Set the current CPU defined by the user. Callers must hold BQL. */ +int monitor_set_cpu(Monitor *mon, int cpu_index) +{ + CPUState *cpu; + + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + return -1; + } + g_free(mon->mon_cpu_path); + mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); + return 0; +} + +/* Callers must hold BQL. */ +static CPUState *mon_get_cpu_sync(Monitor *mon, bool synchronize) +{ + CPUState *cpu = NULL; + + if (mon->mon_cpu_path) { + cpu = (CPUState *) object_resolve_path_type(mon->mon_cpu_path, + TYPE_CPU, NULL); + if (!cpu) { + g_free(mon->mon_cpu_path); + mon->mon_cpu_path = NULL; + } + } + if (!mon->mon_cpu_path) { + if (!first_cpu) { + return NULL; + } + monitor_set_cpu(mon, first_cpu->cpu_index); + cpu = first_cpu; + } + assert(cpu != NULL); + if (synchronize) { + cpu_synchronize_state(cpu); + } + return cpu; +} + +CPUState *mon_get_cpu(Monitor *mon) +{ + return mon_get_cpu_sync(mon, true); +} + +CPUArchState *mon_get_cpu_env(Monitor *mon) +{ + CPUState *cs = mon_get_cpu(mon); + + return cs ? cs->env_ptr : NULL; +} + +int monitor_get_cpu_index(Monitor *mon) +{ + CPUState *cs = mon_get_cpu_sync(mon, false); + + return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; +} + +void hmp_info_registers(Monitor *mon, const QDict *qdict) +{ + bool all_cpus = qdict_get_try_bool(qdict, "cpustate_all", false); + int vcpu = qdict_get_try_int(qdict, "vcpu", -1); + CPUState *cs; + + if (all_cpus) { + CPU_FOREACH(cs) { + monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + } + } else { + cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : mon_get_cpu(mon); + + if (!cs) { + if (vcpu >= 0) { + monitor_printf(mon, "CPU#%d not available\n", vcpu); + } else { + monitor_printf(mon, "No CPU available\n"); + } + return; + } + + monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + } +} + +static void memory_dump(Monitor *mon, int count, int format, int wsize, + hwaddr addr, int is_physical) +{ + int l, line_size, i, max_digits, len; + uint8_t buf[16]; + uint64_t v; + CPUState *cs = mon_get_cpu(mon); + + if (!cs && (format == 'i' || !is_physical)) { + monitor_printf(mon, "Can not dump without CPU\n"); + return; + } + + if (format == 'i') { + monitor_disas(mon, cs, addr, count, is_physical); + return; + } + + len = wsize * count; + if (wsize == 1) { + line_size = 8; + } else { + line_size = 16; + } + max_digits = 0; + + switch(format) { + case 'o': + max_digits = DIV_ROUND_UP(wsize * 8, 3); + break; + default: + case 'x': + max_digits = (wsize * 8) / 4; + break; + case 'u': + case 'd': + max_digits = DIV_ROUND_UP(wsize * 8 * 10, 33); + break; + case 'c': + wsize = 1; + break; + } + + while (len > 0) { + if (is_physical) { + monitor_printf(mon, HWADDR_FMT_plx ":", addr); + } else { + monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr); + } + l = len; + if (l > line_size) + l = line_size; + if (is_physical) { + AddressSpace *as = cs ? cs->as : &address_space_memory; + MemTxResult r = address_space_read(as, addr, + MEMTXATTRS_UNSPECIFIED, buf, l); + if (r != MEMTX_OK) { + monitor_printf(mon, " Cannot access memory\n"); + break; + } + } else { + if (cpu_memory_rw_debug(cs, addr, buf, l, 0) < 0) { + monitor_printf(mon, " Cannot access memory\n"); + break; + } + } + i = 0; + while (i < l) { + switch(wsize) { + default: + case 1: + v = ldub_p(buf + i); + break; + case 2: + v = lduw_p(buf + i); + break; + case 4: + v = (uint32_t)ldl_p(buf + i); + break; + case 8: + v = ldq_p(buf + i); + break; + } + monitor_printf(mon, " "); + switch(format) { + case 'o': + monitor_printf(mon, "%#*" PRIo64, max_digits, v); + break; + case 'x': + monitor_printf(mon, "0x%0*" PRIx64, max_digits, v); + break; + case 'u': + monitor_printf(mon, "%*" PRIu64, max_digits, v); + break; + case 'd': + monitor_printf(mon, "%*" PRId64, max_digits, v); + break; + case 'c': + monitor_printc(mon, v); + break; + } + i += wsize; + } + monitor_printf(mon, "\n"); + addr += l; + len -= l; + } +} + +void hmp_memory_dump(Monitor *mon, const QDict *qdict) +{ + int count = qdict_get_int(qdict, "count"); + int format = qdict_get_int(qdict, "format"); + int size = qdict_get_int(qdict, "size"); + target_long addr = qdict_get_int(qdict, "addr"); + + memory_dump(mon, count, format, size, addr, 0); +} + +void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict) +{ + int count = qdict_get_int(qdict, "count"); + int format = qdict_get_int(qdict, "format"); + int size = qdict_get_int(qdict, "size"); + hwaddr addr = qdict_get_int(qdict, "addr"); + + memory_dump(mon, count, format, size, addr, 1); +} + +void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp) +{ + Int128 gpa_region_size; + MemoryRegionSection mrs = memory_region_find(get_system_memory(), + addr, size); + + if (!mrs.mr) { + error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr); + return NULL; + } + + if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) { + error_setg(errp, "Memory at address 0x%" HWADDR_PRIx "is not RAM", addr); + memory_region_unref(mrs.mr); + return NULL; + } + + gpa_region_size = int128_make64(size); + if (int128_lt(mrs.size, gpa_region_size)) { + error_setg(errp, "Size of memory region at 0x%" HWADDR_PRIx + " exceeded.", addr); + memory_region_unref(mrs.mr); + return NULL; + } + + *p_mr = mrs.mr; + return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); +} + +void hmp_gpa2hva(Monitor *mon, const QDict *qdict) +{ + hwaddr addr = qdict_get_int(qdict, "addr"); + Error *local_err = NULL; + MemoryRegion *mr = NULL; + void *ptr; + + ptr = gpa2hva(&mr, addr, 1, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + monitor_printf(mon, "Host virtual address for 0x%" HWADDR_PRIx + " (%s) is %p\n", + addr, mr->name, ptr); + + memory_region_unref(mr); +} + +void hmp_gva2gpa(Monitor *mon, const QDict *qdict) +{ + target_ulong addr = qdict_get_int(qdict, "addr"); + MemTxAttrs attrs; + CPUState *cs = mon_get_cpu(mon); + hwaddr gpa; + + if (!cs) { + monitor_printf(mon, "No cpu\n"); + return; + } + + gpa = cpu_get_phys_page_attrs_debug(cs, addr & TARGET_PAGE_MASK, &attrs); + if (gpa == -1) { + monitor_printf(mon, "Unmapped\n"); + } else { + monitor_printf(mon, "gpa: %#" HWADDR_PRIx "\n", + gpa + (addr & ~TARGET_PAGE_MASK)); + } +} + +#ifdef CONFIG_LINUX +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(); + off_t offset = addr / pagesize * sizeof(pinfo); + int fd; + + fd = open("/proc/self/pagemap", O_RDONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "Cannot open /proc/self/pagemap"); + return -1; + } + + /* Force copy-on-write if necessary. */ + qatomic_add((uint8_t *)ptr, 0); + + if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) { + error_setg_errno(errp, errno, "Cannot read pagemap"); + goto out; + } + if ((pinfo & (1ull << 63)) == 0) { + error_setg(errp, "Page not present"); + goto out; + } + ret = ((pinfo & 0x007fffffffffffffull) * pagesize) | (addr & (pagesize - 1)); + +out: + close(fd); + return ret; +} + +void hmp_gpa2hpa(Monitor *mon, const QDict *qdict) +{ + hwaddr addr = qdict_get_int(qdict, "addr"); + Error *local_err = NULL; + MemoryRegion *mr = NULL; + void *ptr; + uint64_t physaddr; + + ptr = gpa2hva(&mr, addr, 1, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + physaddr = vtop(ptr, &local_err); + if (local_err) { + error_report_err(local_err); + } else { + monitor_printf(mon, "Host physical address for 0x%" HWADDR_PRIx + " (%s) is 0x%" PRIx64 "\n", + addr, mr->name, (uint64_t) physaddr); + } + + memory_region_unref(mr); +} +#endif diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 1dba973092..34bd8c67d7 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -14,48 +14,21 @@ */ #include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "exec/gdbstub.h" +#include "exec/ioport.h" #include "monitor/hmp.h" -#include "net/net.h" -#include "net/eth.h" -#include "chardev/char.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" -#include "qemu/config-file.h" -#include "qemu/option.h" -#include "qemu/timer.h" -#include "qemu/sockets.h" #include "qemu/help_option.h" -#include "monitor/monitor.h" +#include "monitor/monitor-internal.h" #include "qapi/error.h" -#include "qapi/clone-visitor.h" -#include "qapi/opts-visitor.h" -#include "qapi/qapi-builtin-visit.h" -#include "qapi/qapi-commands-block.h" -#include "qapi/qapi-commands-char.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-commands-migration.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-commands-net.h" -#include "qapi/qapi-commands-rocker.h" -#include "qapi/qapi-commands-run-state.h" -#include "qapi/qapi-commands-stats.h" -#include "qapi/qapi-commands-tpm.h" -#include "qapi/qapi-commands-virtio.h" -#include "qapi/qapi-visit-virtio.h" -#include "qapi/qapi-visit-net.h" -#include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/string-input-visitor.h" -#include "qapi/string-output-visitor.h" -#include "qom/object_interfaces.h" #include "qemu/cutils.h" -#include "qemu/error-report.h" -#include "hw/core/cpu.h" #include "hw/intc/intc.h" -#include "migration/snapshot.h" -#include "migration/misc.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" bool hmp_handle_error(Monitor *mon, Error *err) { @@ -67,28 +40,21 @@ bool hmp_handle_error(Monitor *mon, Error *err) } /* - * Produce a strList from a comma separated list. - * A NULL or empty input string return NULL. + * Split @str at comma. + * A null @str defaults to "". */ -static strList *strList_from_comma_list(const char *in) +strList *hmp_split_at_comma(const char *str) { + char **split = g_strsplit(str ?: "", ",", -1); strList *res = NULL; strList **tail = &res; + int i; - while (in && in[0]) { - char *comma = strchr(in, ','); - char *value; - - if (comma) { - value = g_strndup(in, comma - in); - in = comma + 1; /* skip the , */ - } else { - value = g_strdup(in); - in = NULL; - } - QAPI_LIST_APPEND(tail, value); + for (i = 0; split[i]; i++) { + QAPI_LIST_APPEND(tail, split[i]); } + g_free(split); return res; } @@ -116,395 +82,6 @@ void hmp_info_version(Monitor *mon, const QDict *qdict) qapi_free_VersionInfo(info); } -void hmp_info_kvm(Monitor *mon, const QDict *qdict) -{ - KvmInfo *info; - - info = qmp_query_kvm(NULL); - monitor_printf(mon, "kvm support: "); - if (info->present) { - monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled"); - } else { - monitor_printf(mon, "not compiled\n"); - } - - qapi_free_KvmInfo(info); -} - -void hmp_info_status(Monitor *mon, const QDict *qdict) -{ - StatusInfo *info; - - info = qmp_query_status(NULL); - - monitor_printf(mon, "VM status: %s%s", - info->running ? "running" : "paused", - info->singlestep ? " (single step mode)" : ""); - - if (!info->running && info->status != RUN_STATE_PAUSED) { - monitor_printf(mon, " (%s)", RunState_str(info->status)); - } - - monitor_printf(mon, "\n"); - - qapi_free_StatusInfo(info); -} - -void hmp_info_uuid(Monitor *mon, const QDict *qdict) -{ - UuidInfo *info; - - info = qmp_query_uuid(NULL); - monitor_printf(mon, "%s\n", info->UUID); - qapi_free_UuidInfo(info); -} - -void hmp_info_chardev(Monitor *mon, const QDict *qdict) -{ - ChardevInfoList *char_info, *info; - - char_info = qmp_query_chardev(NULL); - for (info = char_info; info; info = info->next) { - monitor_printf(mon, "%s: filename=%s\n", info->value->label, - info->value->filename); - } - - qapi_free_ChardevInfoList(char_info); -} - -void hmp_info_migrate(Monitor *mon, const QDict *qdict) -{ - MigrationInfo *info; - - info = qmp_query_migrate(NULL); - - migration_global_dump(mon); - - if (info->blocked_reasons) { - strList *reasons = info->blocked_reasons; - monitor_printf(mon, "Outgoing migration blocked:\n"); - while (reasons) { - monitor_printf(mon, " %s\n", reasons->value); - reasons = reasons->next; - } - } - - if (info->has_status) { - monitor_printf(mon, "Migration status: %s", - MigrationStatus_str(info->status)); - if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { - monitor_printf(mon, " (%s)\n", info->error_desc); - } else { - monitor_printf(mon, "\n"); - } - - monitor_printf(mon, "total time: %" PRIu64 " ms\n", - info->total_time); - if (info->has_expected_downtime) { - monitor_printf(mon, "expected downtime: %" PRIu64 " ms\n", - info->expected_downtime); - } - if (info->has_downtime) { - monitor_printf(mon, "downtime: %" PRIu64 " ms\n", - info->downtime); - } - if (info->has_setup_time) { - monitor_printf(mon, "setup: %" PRIu64 " ms\n", - info->setup_time); - } - } - - if (info->ram) { - monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n", - info->ram->transferred >> 10); - monitor_printf(mon, "throughput: %0.2f mbps\n", - info->ram->mbps); - monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n", - info->ram->remaining >> 10); - monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n", - info->ram->total >> 10); - monitor_printf(mon, "duplicate: %" PRIu64 " pages\n", - info->ram->duplicate); - monitor_printf(mon, "skipped: %" PRIu64 " pages\n", - info->ram->skipped); - monitor_printf(mon, "normal: %" PRIu64 " pages\n", - info->ram->normal); - monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n", - info->ram->normal_bytes >> 10); - monitor_printf(mon, "dirty sync count: %" PRIu64 "\n", - info->ram->dirty_sync_count); - monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", - info->ram->page_size >> 10); - monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", - info->ram->multifd_bytes >> 10); - monitor_printf(mon, "pages-per-second: %" PRIu64 "\n", - info->ram->pages_per_second); - - if (info->ram->dirty_pages_rate) { - monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", - info->ram->dirty_pages_rate); - } - if (info->ram->postcopy_requests) { - monitor_printf(mon, "postcopy request count: %" PRIu64 "\n", - info->ram->postcopy_requests); - } - if (info->ram->precopy_bytes) { - monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n", - info->ram->precopy_bytes >> 10); - } - if (info->ram->downtime_bytes) { - monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n", - info->ram->downtime_bytes >> 10); - } - if (info->ram->postcopy_bytes) { - monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n", - info->ram->postcopy_bytes >> 10); - } - if (info->ram->dirty_sync_missed_zero_copy) { - monitor_printf(mon, - "Zero-copy-send fallbacks happened: %" PRIu64 " times\n", - info->ram->dirty_sync_missed_zero_copy); - } - } - - if (info->disk) { - monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n", - info->disk->transferred >> 10); - monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n", - info->disk->remaining >> 10); - monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n", - info->disk->total >> 10); - } - - if (info->xbzrle_cache) { - monitor_printf(mon, "cache size: %" PRIu64 " bytes\n", - info->xbzrle_cache->cache_size); - monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n", - info->xbzrle_cache->bytes >> 10); - monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n", - info->xbzrle_cache->pages); - monitor_printf(mon, "xbzrle cache miss: %" PRIu64 " pages\n", - info->xbzrle_cache->cache_miss); - monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n", - info->xbzrle_cache->cache_miss_rate); - monitor_printf(mon, "xbzrle encoding rate: %0.2f\n", - info->xbzrle_cache->encoding_rate); - monitor_printf(mon, "xbzrle overflow: %" PRIu64 "\n", - info->xbzrle_cache->overflow); - } - - if (info->compression) { - monitor_printf(mon, "compression pages: %" PRIu64 " pages\n", - info->compression->pages); - monitor_printf(mon, "compression busy: %" PRIu64 "\n", - info->compression->busy); - monitor_printf(mon, "compression busy rate: %0.2f\n", - info->compression->busy_rate); - monitor_printf(mon, "compressed size: %" PRIu64 " kbytes\n", - info->compression->compressed_size >> 10); - monitor_printf(mon, "compression rate: %0.2f\n", - info->compression->compression_rate); - } - - if (info->has_cpu_throttle_percentage) { - monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", - info->cpu_throttle_percentage); - } - - if (info->has_postcopy_blocktime) { - monitor_printf(mon, "postcopy blocktime: %u\n", - info->postcopy_blocktime); - } - - if (info->has_postcopy_vcpu_blocktime) { - Visitor *v; - char *str; - v = string_output_visitor_new(false, &str); - visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, - &error_abort); - visit_complete(v, &str); - monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str); - g_free(str); - visit_free(v); - } - if (info->has_socket_address) { - SocketAddressList *addr; - - monitor_printf(mon, "socket address: [\n"); - - for (addr = info->socket_address; addr; addr = addr->next) { - char *s = socket_uri(addr->value); - monitor_printf(mon, "\t%s\n", s); - g_free(s); - } - monitor_printf(mon, "]\n"); - } - - if (info->vfio) { - monitor_printf(mon, "vfio device transferred: %" PRIu64 " kbytes\n", - info->vfio->transferred >> 10); - } - - qapi_free_MigrationInfo(info); -} - -void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) -{ - MigrationCapabilityStatusList *caps, *cap; - - caps = qmp_query_migrate_capabilities(NULL); - - if (caps) { - for (cap = caps; cap; cap = cap->next) { - monitor_printf(mon, "%s: %s\n", - MigrationCapability_str(cap->value->capability), - cap->value->state ? "on" : "off"); - } - } - - qapi_free_MigrationCapabilityStatusList(caps); -} - -void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) -{ - MigrationParameters *params; - - params = qmp_query_migrate_parameters(NULL); - - if (params) { - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL), - params->announce_initial); - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX), - params->announce_max); - monitor_printf(mon, "%s: %" PRIu64 "\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS), - params->announce_rounds); - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP), - params->announce_step); - assert(params->has_compress_level); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL), - params->compress_level); - assert(params->has_compress_threads); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS), - params->compress_threads); - assert(params->has_compress_wait_thread); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD), - params->compress_wait_thread ? "on" : "off"); - assert(params->has_decompress_threads); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), - params->decompress_threads); - assert(params->has_throttle_trigger_threshold); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), - params->throttle_trigger_threshold); - assert(params->has_cpu_throttle_initial); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL), - params->cpu_throttle_initial); - assert(params->has_cpu_throttle_increment); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT), - params->cpu_throttle_increment); - assert(params->has_cpu_throttle_tailslow); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW), - params->cpu_throttle_tailslow ? "on" : "off"); - assert(params->has_max_cpu_throttle); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE), - params->max_cpu_throttle); - assert(params->tls_creds); - monitor_printf(mon, "%s: '%s'\n", - MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS), - params->tls_creds); - assert(params->tls_hostname); - monitor_printf(mon, "%s: '%s'\n", - MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME), - params->tls_hostname); - assert(params->has_max_bandwidth); - monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", - MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH), - params->max_bandwidth); - assert(params->has_downtime_limit); - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT), - params->downtime_limit); - assert(params->has_x_checkpoint_delay); - monitor_printf(mon, "%s: %u ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY), - params->x_checkpoint_delay); - assert(params->has_block_incremental); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL), - params->block_incremental ? "on" : "off"); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), - params->multifd_channels); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION), - MultiFDCompression_str(params->multifd_compression)); - monitor_printf(mon, "%s: %" PRIu64 " bytes\n", - MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), - params->xbzrle_cache_size); - monitor_printf(mon, "%s: %" PRIu64 "\n", - MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), - params->max_postcopy_bandwidth); - monitor_printf(mon, "%s: '%s'\n", - MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), - params->tls_authz); - - if (params->has_block_bitmap_mapping) { - const BitmapMigrationNodeAliasList *bmnal; - - monitor_printf(mon, "%s:\n", - MigrationParameter_str( - MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING)); - - for (bmnal = params->block_bitmap_mapping; - bmnal; - bmnal = bmnal->next) - { - const BitmapMigrationNodeAlias *bmna = bmnal->value; - const BitmapMigrationBitmapAliasList *bmbal; - - monitor_printf(mon, " '%s' -> '%s'\n", - bmna->node_name, bmna->alias); - - for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) { - const BitmapMigrationBitmapAlias *bmba = bmbal->value; - - monitor_printf(mon, " '%s' -> '%s'\n", - bmba->name, bmba->alias); - } - } - } - } - - qapi_free_MigrationParameters(params); -} - -void hmp_info_balloon(Monitor *mon, const QDict *qdict) -{ - BalloonInfo *info; - Error *err = NULL; - - info = qmp_query_balloon(&err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); - - qapi_free_BalloonInfo(info); -} - static int hmp_info_pic_foreach(Object *obj, void *opaque) { InterruptStatsProvider *intc; @@ -531,59 +108,6 @@ void hmp_info_pic(Monitor *mon, const QDict *qdict) hmp_info_pic_foreach, mon); } -void hmp_info_tpm(Monitor *mon, const QDict *qdict) -{ -#ifdef CONFIG_TPM - TPMInfoList *info_list, *info; - Error *err = NULL; - unsigned int c = 0; - TPMPassthroughOptions *tpo; - TPMEmulatorOptions *teo; - - info_list = qmp_query_tpm(&err); - if (err) { - monitor_printf(mon, "TPM device not supported\n"); - error_free(err); - return; - } - - if (info_list) { - monitor_printf(mon, "TPM device:\n"); - } - - for (info = info_list; info; info = info->next) { - TPMInfo *ti = info->value; - monitor_printf(mon, " tpm%d: model=%s\n", - c, TpmModel_str(ti->model)); - - monitor_printf(mon, " \\ %s: type=%s", - ti->id, TpmType_str(ti->options->type)); - - switch (ti->options->type) { - case TPM_TYPE_PASSTHROUGH: - tpo = ti->options->u.passthrough.data; - monitor_printf(mon, "%s%s%s%s", - tpo->path ? ",path=" : "", - tpo->path ?: "", - tpo->cancel_path ? ",cancel-path=" : "", - tpo->cancel_path ?: ""); - break; - case TPM_TYPE_EMULATOR: - teo = ti->options->u.emulator.data; - monitor_printf(mon, ",chardev=%s", teo->chardev); - break; - case TPM_TYPE__MAX: - break; - } - monitor_printf(mon, "\n"); - c++; - } - qapi_free_TPMInfoList(info_list); -#else - monitor_printf(mon, "TPM device not supported\n"); -#endif /* CONFIG_TPM */ -} - void hmp_quit(Monitor *mon, const QDict *qdict) { monitor_suspend(mon); @@ -619,16 +143,6 @@ void hmp_sync_profile(Monitor *mon, const QDict *qdict) } } -void hmp_system_reset(Monitor *mon, const QDict *qdict) -{ - qmp_system_reset(NULL); -} - -void hmp_system_powerdown(Monitor *mon, const QDict *qdict) -{ - qmp_system_powerdown(NULL); -} - void hmp_exit_preconfig(Monitor *mon, const QDict *qdict) { Error *err = NULL; @@ -649,74 +163,6 @@ void hmp_cpu(Monitor *mon, const QDict *qdict) } } -void hmp_memsave(Monitor *mon, const QDict *qdict) -{ - uint32_t size = qdict_get_int(qdict, "size"); - const char *filename = qdict_get_str(qdict, "filename"); - uint64_t addr = qdict_get_int(qdict, "val"); - Error *err = NULL; - int cpu_index = monitor_get_cpu_index(mon); - - if (cpu_index < 0) { - monitor_printf(mon, "No CPU available\n"); - return; - } - - qmp_memsave(addr, size, filename, true, cpu_index, &err); - hmp_handle_error(mon, err); -} - -void hmp_pmemsave(Monitor *mon, const QDict *qdict) -{ - uint32_t size = qdict_get_int(qdict, "size"); - const char *filename = qdict_get_str(qdict, "filename"); - uint64_t addr = qdict_get_int(qdict, "val"); - Error *err = NULL; - - qmp_pmemsave(addr, size, filename, &err); - hmp_handle_error(mon, err); -} - -void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) -{ - const char *chardev = qdict_get_str(qdict, "device"); - const char *data = qdict_get_str(qdict, "data"); - Error *err = NULL; - - qmp_ringbuf_write(chardev, data, false, 0, &err); - - hmp_handle_error(mon, err); -} - -void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) -{ - uint32_t size = qdict_get_int(qdict, "size"); - const char *chardev = qdict_get_str(qdict, "device"); - char *data; - Error *err = NULL; - int i; - - data = qmp_ringbuf_read(chardev, size, false, 0, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - for (i = 0; data[i]; i++) { - unsigned char ch = data[i]; - - if (ch == '\\') { - monitor_printf(mon, "\\\\"); - } else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) { - monitor_printf(mon, "\\u%04X", ch); - } else { - monitor_printf(mon, "%c", ch); - } - - } - monitor_printf(mon, "\n"); - g_free(data); -} - void hmp_cont(Monitor *mon, const QDict *qdict) { Error *err = NULL; @@ -725,355 +171,6 @@ void hmp_cont(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_system_wakeup(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_system_wakeup(&err); - hmp_handle_error(mon, err); -} - -void hmp_nmi(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_inject_nmi(&err); - hmp_handle_error(mon, err); -} - -void hmp_set_link(Monitor *mon, const QDict *qdict) -{ - const char *name = qdict_get_str(qdict, "name"); - bool up = qdict_get_bool(qdict, "up"); - Error *err = NULL; - - qmp_set_link(name, up, &err); - hmp_handle_error(mon, err); -} - -void hmp_balloon(Monitor *mon, const QDict *qdict) -{ - int64_t value = qdict_get_int(qdict, "value"); - Error *err = NULL; - - qmp_balloon(value, &err); - hmp_handle_error(mon, err); -} - -void hmp_loadvm(Monitor *mon, const QDict *qdict) -{ - int saved_vm_running = runstate_is_running(); - const char *name = qdict_get_str(qdict, "name"); - Error *err = NULL; - - vm_stop(RUN_STATE_RESTORE_VM); - - if (load_snapshot(name, NULL, false, NULL, &err) && saved_vm_running) { - vm_start(); - } - hmp_handle_error(mon, err); -} - -void hmp_savevm(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - save_snapshot(qdict_get_try_str(qdict, "name"), - true, NULL, false, NULL, &err); - hmp_handle_error(mon, err); -} - -void hmp_delvm(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *name = qdict_get_str(qdict, "name"); - - delete_snapshot(name, false, NULL, &err); - hmp_handle_error(mon, err); -} - -void hmp_announce_self(Monitor *mon, const QDict *qdict) -{ - const char *interfaces_str = qdict_get_try_str(qdict, "interfaces"); - const char *id = qdict_get_try_str(qdict, "id"); - AnnounceParameters *params = QAPI_CLONE(AnnounceParameters, - migrate_announce_params()); - - qapi_free_strList(params->interfaces); - params->interfaces = strList_from_comma_list(interfaces_str); - params->has_interfaces = params->interfaces != NULL; - params->id = g_strdup(id); - qmp_announce_self(params, NULL); - qapi_free_AnnounceParameters(params); -} - -void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) -{ - qmp_migrate_cancel(NULL); -} - -void hmp_migrate_continue(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *state = qdict_get_str(qdict, "state"); - int val = qapi_enum_parse(&MigrationStatus_lookup, state, -1, &err); - - if (val >= 0) { - qmp_migrate_continue(val, &err); - } - - hmp_handle_error(mon, err); -} - -void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *uri = qdict_get_str(qdict, "uri"); - - qmp_migrate_incoming(uri, &err); - - hmp_handle_error(mon, err); -} - -void hmp_migrate_recover(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *uri = qdict_get_str(qdict, "uri"); - - qmp_migrate_recover(uri, &err); - - hmp_handle_error(mon, err); -} - -void hmp_migrate_pause(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_migrate_pause(&err); - - hmp_handle_error(mon, err); -} - - -void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) -{ - const char *cap = qdict_get_str(qdict, "capability"); - bool state = qdict_get_bool(qdict, "state"); - Error *err = NULL; - MigrationCapabilityStatusList *caps = NULL; - MigrationCapabilityStatus *value; - int val; - - val = qapi_enum_parse(&MigrationCapability_lookup, cap, -1, &err); - if (val < 0) { - goto end; - } - - value = g_malloc0(sizeof(*value)); - value->capability = val; - value->state = state; - QAPI_LIST_PREPEND(caps, value); - qmp_migrate_set_capabilities(caps, &err); - qapi_free_MigrationCapabilityStatusList(caps); - -end: - hmp_handle_error(mon, err); -} - -void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) -{ - const char *param = qdict_get_str(qdict, "parameter"); - const char *valuestr = qdict_get_str(qdict, "value"); - Visitor *v = string_input_visitor_new(valuestr); - MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); - uint64_t valuebw = 0; - uint64_t cache_size; - Error *err = NULL; - int val, ret; - - val = qapi_enum_parse(&MigrationParameter_lookup, param, -1, &err); - if (val < 0) { - goto cleanup; - } - - switch (val) { - case MIGRATION_PARAMETER_COMPRESS_LEVEL: - p->has_compress_level = true; - visit_type_uint8(v, param, &p->compress_level, &err); - break; - case MIGRATION_PARAMETER_COMPRESS_THREADS: - p->has_compress_threads = true; - visit_type_uint8(v, param, &p->compress_threads, &err); - break; - case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD: - p->has_compress_wait_thread = true; - visit_type_bool(v, param, &p->compress_wait_thread, &err); - break; - case MIGRATION_PARAMETER_DECOMPRESS_THREADS: - p->has_decompress_threads = true; - visit_type_uint8(v, param, &p->decompress_threads, &err); - break; - case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: - p->has_throttle_trigger_threshold = true; - visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err); - break; - case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: - p->has_cpu_throttle_initial = true; - visit_type_uint8(v, param, &p->cpu_throttle_initial, &err); - break; - case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT: - p->has_cpu_throttle_increment = true; - visit_type_uint8(v, param, &p->cpu_throttle_increment, &err); - break; - case MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW: - p->has_cpu_throttle_tailslow = true; - visit_type_bool(v, param, &p->cpu_throttle_tailslow, &err); - break; - case MIGRATION_PARAMETER_MAX_CPU_THROTTLE: - p->has_max_cpu_throttle = true; - visit_type_uint8(v, param, &p->max_cpu_throttle, &err); - break; - case MIGRATION_PARAMETER_TLS_CREDS: - p->tls_creds = g_new0(StrOrNull, 1); - p->tls_creds->type = QTYPE_QSTRING; - visit_type_str(v, param, &p->tls_creds->u.s, &err); - break; - case MIGRATION_PARAMETER_TLS_HOSTNAME: - p->tls_hostname = g_new0(StrOrNull, 1); - p->tls_hostname->type = QTYPE_QSTRING; - visit_type_str(v, param, &p->tls_hostname->u.s, &err); - break; - case MIGRATION_PARAMETER_TLS_AUTHZ: - p->tls_authz = g_new0(StrOrNull, 1); - p->tls_authz->type = QTYPE_QSTRING; - visit_type_str(v, param, &p->tls_authz->u.s, &err); - break; - case MIGRATION_PARAMETER_MAX_BANDWIDTH: - p->has_max_bandwidth = true; - /* - * Can't use visit_type_size() here, because it - * defaults to Bytes rather than Mebibytes. - */ - ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw); - if (ret < 0 || valuebw > INT64_MAX - || (size_t)valuebw != valuebw) { - error_setg(&err, "Invalid size %s", valuestr); - break; - } - p->max_bandwidth = valuebw; - break; - case MIGRATION_PARAMETER_DOWNTIME_LIMIT: - p->has_downtime_limit = true; - visit_type_size(v, param, &p->downtime_limit, &err); - break; - case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY: - p->has_x_checkpoint_delay = true; - visit_type_uint32(v, param, &p->x_checkpoint_delay, &err); - break; - case MIGRATION_PARAMETER_BLOCK_INCREMENTAL: - p->has_block_incremental = true; - visit_type_bool(v, param, &p->block_incremental, &err); - break; - case MIGRATION_PARAMETER_MULTIFD_CHANNELS: - p->has_multifd_channels = true; - visit_type_uint8(v, param, &p->multifd_channels, &err); - break; - case MIGRATION_PARAMETER_MULTIFD_COMPRESSION: - p->has_multifd_compression = true; - visit_type_MultiFDCompression(v, param, &p->multifd_compression, - &err); - break; - case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL: - p->has_multifd_zlib_level = true; - visit_type_uint8(v, param, &p->multifd_zlib_level, &err); - break; - case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL: - p->has_multifd_zstd_level = true; - visit_type_uint8(v, param, &p->multifd_zstd_level, &err); - break; - case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE: - p->has_xbzrle_cache_size = true; - if (!visit_type_size(v, param, &cache_size, &err)) { - break; - } - if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) { - error_setg(&err, "Invalid size %s", valuestr); - break; - } - p->xbzrle_cache_size = cache_size; - break; - case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH: - p->has_max_postcopy_bandwidth = true; - visit_type_size(v, param, &p->max_postcopy_bandwidth, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_INITIAL: - p->has_announce_initial = true; - visit_type_size(v, param, &p->announce_initial, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_MAX: - p->has_announce_max = true; - visit_type_size(v, param, &p->announce_max, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS: - p->has_announce_rounds = true; - visit_type_size(v, param, &p->announce_rounds, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_STEP: - p->has_announce_step = true; - visit_type_size(v, param, &p->announce_step, &err); - break; - case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING: - error_setg(&err, "The block-bitmap-mapping parameter can only be set " - "through QMP"); - break; - default: - assert(0); - } - - if (err) { - goto cleanup; - } - - qmp_migrate_set_parameters(p, &err); - - cleanup: - qapi_free_MigrateSetParameters(p); - visit_free(v); - hmp_handle_error(mon, err); -} - -void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *protocol = qdict_get_str(qdict, "protocol"); - const char *hostname = qdict_get_str(qdict, "hostname"); - bool has_port = qdict_haskey(qdict, "port"); - int port = qdict_get_try_int(qdict, "port", -1); - bool has_tls_port = qdict_haskey(qdict, "tls-port"); - int tls_port = qdict_get_try_int(qdict, "tls-port", -1); - const char *cert_subject = qdict_get_try_str(qdict, "cert-subject"); - - qmp_client_migrate_info(protocol, hostname, - has_port, port, has_tls_port, tls_port, - cert_subject, &err); - hmp_handle_error(mon, err); -} - -void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - qmp_migrate_start_postcopy(&err); - hmp_handle_error(mon, err); -} - -void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_x_colo_lost_heartbeat(&err); - hmp_handle_error(mon, err); -} - void hmp_change(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); @@ -1081,7 +178,6 @@ void hmp_change(Monitor *mon, const QDict *qdict) 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; #ifdef CONFIG_VNC @@ -1090,140 +186,9 @@ void hmp_change(Monitor *mon, const QDict *qdict) } else #endif { - if (read_only) { - read_only_mode = - qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup, - read_only, - BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err); - if (err) { - goto end; - } - } - - qmp_blockdev_change_medium(device, NULL, target, arg, true, force, - !!read_only, read_only_mode, - &err); - } - -end: - hmp_handle_error(mon, err); -} - -typedef struct HMPMigrationStatus { - QEMUTimer *timer; - Monitor *mon; - bool is_block_migration; -} HMPMigrationStatus; - -static void hmp_migrate_status_cb(void *opaque) -{ - HMPMigrationStatus *status = opaque; - MigrationInfo *info; - - info = qmp_query_migrate(NULL); - if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE || - info->status == MIGRATION_STATUS_SETUP) { - if (info->disk) { - int progress; - - if (info->disk->remaining) { - progress = info->disk->transferred * 100 / info->disk->total; - } else { - progress = 100; - } - - monitor_printf(status->mon, "Completed %d %%\r", progress); - monitor_flush(status->mon); - } - - timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); - } else { - if (status->is_block_migration) { - monitor_printf(status->mon, "\n"); - } - if (info->error_desc) { - error_report("%s", info->error_desc); - } - monitor_resume(status->mon); - timer_free(status->timer); - g_free(status); - } - - qapi_free_MigrationInfo(info); -} - -void hmp_migrate(Monitor *mon, const QDict *qdict) -{ - bool detach = qdict_get_try_bool(qdict, "detach", false); - bool blk = qdict_get_try_bool(qdict, "blk", false); - bool inc = qdict_get_try_bool(qdict, "inc", false); - bool resume = qdict_get_try_bool(qdict, "resume", false); - const char *uri = qdict_get_str(qdict, "uri"); - Error *err = NULL; - - qmp_migrate(uri, !!blk, blk, !!inc, inc, - false, false, true, resume, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - if (!detach) { - HMPMigrationStatus *status; - - if (monitor_suspend(mon) < 0) { - monitor_printf(mon, "terminal does not allow synchronous " - "migration, continuing detached\n"); - return; - } - - status = g_malloc0(sizeof(*status)); - status->mon = mon; - status->is_block_migration = blk || inc; - status->timer = timer_new_ms(QEMU_CLOCK_REALTIME, hmp_migrate_status_cb, - status); - timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - } -} - -void hmp_netdev_add(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - QemuOpts *opts; - const char *type = qdict_get_try_str(qdict, "type"); - - if (type && is_help_option(type)) { - show_netdevs(); - return; - } - opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err); - if (err) { - goto out; + hmp_change_medium(mon, device, target, arg, read_only, force, &err); } - netdev_add(opts, &err); - if (err) { - qemu_opts_del(opts); - } - -out: - hmp_handle_error(mon, err); -} - -void hmp_netdev_del(Monitor *mon, const QDict *qdict) -{ - const char *id = qdict_get_str(qdict, "id"); - Error *err = NULL; - - qmp_netdev_del(id, &err); - hmp_handle_error(mon, err); -} - -void hmp_object_add(Monitor *mon, const QDict *qdict) -{ - const char *options = qdict_get_str(qdict, "object"); - Error *err = NULL; - - user_creatable_add_from_str(options, &err); hmp_handle_error(mon, err); } @@ -1245,158 +210,6 @@ void hmp_closefd(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_chardev_add(Monitor *mon, const QDict *qdict) -{ - const char *args = qdict_get_str(qdict, "args"); - Error *err = NULL; - QemuOpts *opts; - - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, true); - if (opts == NULL) { - error_setg(&err, "Parsing chardev args failed"); - } else { - qemu_chr_new_from_opts(opts, NULL, &err); - qemu_opts_del(opts); - } - hmp_handle_error(mon, err); -} - -void hmp_chardev_change(Monitor *mon, const QDict *qdict) -{ - const char *args = qdict_get_str(qdict, "args"); - const char *id; - Error *err = NULL; - ChardevBackend *backend = NULL; - ChardevReturn *ret = NULL; - QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, - true); - if (!opts) { - error_setg(&err, "Parsing chardev args failed"); - goto end; - } - - id = qdict_get_str(qdict, "id"); - if (qemu_opts_id(opts)) { - error_setg(&err, "Unexpected 'id' parameter"); - goto end; - } - - backend = qemu_chr_parse_opts(opts, &err); - if (!backend) { - goto end; - } - - ret = qmp_chardev_change(id, backend, &err); - -end: - qapi_free_ChardevReturn(ret); - qapi_free_ChardevBackend(backend); - qemu_opts_del(opts); - hmp_handle_error(mon, err); -} - -void hmp_chardev_remove(Monitor *mon, const QDict *qdict) -{ - Error *local_err = NULL; - - qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); - hmp_handle_error(mon, local_err); -} - -void hmp_chardev_send_break(Monitor *mon, const QDict *qdict) -{ - Error *local_err = NULL; - - qmp_chardev_send_break(qdict_get_str(qdict, "id"), &local_err); - hmp_handle_error(mon, local_err); -} - -void hmp_object_del(Monitor *mon, const QDict *qdict) -{ - const char *id = qdict_get_str(qdict, "id"); - Error *err = NULL; - - user_creatable_del(id, &err); - hmp_handle_error(mon, err); -} - -void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - MemoryDeviceInfoList *info_list = qmp_query_memory_devices(&err); - MemoryDeviceInfoList *info; - VirtioPMEMDeviceInfo *vpi; - VirtioMEMDeviceInfo *vmi; - MemoryDeviceInfo *value; - PCDIMMDeviceInfo *di; - SgxEPCDeviceInfo *se; - - for (info = info_list; info; info = info->next) { - value = info->value; - - if (value) { - switch (value->type) { - case MEMORY_DEVICE_INFO_KIND_DIMM: - case MEMORY_DEVICE_INFO_KIND_NVDIMM: - di = value->type == MEMORY_DEVICE_INFO_KIND_DIMM ? - value->u.dimm.data : value->u.nvdimm.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - di->id ? di->id : ""); - monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr); - monitor_printf(mon, " slot: %" PRId64 "\n", di->slot); - monitor_printf(mon, " node: %" PRId64 "\n", di->node); - monitor_printf(mon, " size: %" PRIu64 "\n", di->size); - monitor_printf(mon, " memdev: %s\n", di->memdev); - monitor_printf(mon, " hotplugged: %s\n", - di->hotplugged ? "true" : "false"); - monitor_printf(mon, " hotpluggable: %s\n", - di->hotpluggable ? "true" : "false"); - break; - case MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM: - vpi = value->u.virtio_pmem.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - vpi->id ? vpi->id : ""); - monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vpi->memaddr); - monitor_printf(mon, " size: %" PRIu64 "\n", vpi->size); - monitor_printf(mon, " memdev: %s\n", vpi->memdev); - break; - case MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM: - vmi = value->u.virtio_mem.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - vmi->id ? vmi->id : ""); - monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vmi->memaddr); - monitor_printf(mon, " node: %" PRId64 "\n", vmi->node); - monitor_printf(mon, " requested-size: %" PRIu64 "\n", - vmi->requested_size); - monitor_printf(mon, " size: %" PRIu64 "\n", vmi->size); - monitor_printf(mon, " max-size: %" PRIu64 "\n", vmi->max_size); - monitor_printf(mon, " block-size: %" PRIu64 "\n", - vmi->block_size); - monitor_printf(mon, " memdev: %s\n", vmi->memdev); - break; - case MEMORY_DEVICE_INFO_KIND_SGX_EPC: - se = value->u.sgx_epc.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - se->id ? se->id : ""); - monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", se->memaddr); - monitor_printf(mon, " size: %" PRIu64 "\n", se->size); - monitor_printf(mon, " node: %" PRId64 "\n", se->node); - monitor_printf(mon, " memdev: %s\n", se->memdev); - break; - default: - g_assert_not_reached(); - } - } - } - - qapi_free_MemoryDeviceInfoList(info_list); - hmp_handle_error(mon, err); -} - void hmp_info_iothreads(Monitor *mon, const QDict *qdict) { IOThreadInfoList *info_list = qmp_query_iothreads(NULL); @@ -1417,866 +230,214 @@ void hmp_info_iothreads(Monitor *mon, const QDict *qdict) qapi_free_IOThreadInfoList(info_list); } -void hmp_rocker(Monitor *mon, const QDict *qdict) +void hmp_help(Monitor *mon, const QDict *qdict) { - const char *name = qdict_get_str(qdict, "name"); - RockerSwitch *rocker; - Error *err = NULL; - - rocker = qmp_query_rocker(name, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, "name: %s\n", rocker->name); - monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id); - monitor_printf(mon, "ports: %d\n", rocker->ports); - - qapi_free_RockerSwitch(rocker); + hmp_help_cmd(mon, qdict_get_try_str(qdict, "name")); } -void hmp_rocker_ports(Monitor *mon, const QDict *qdict) +void hmp_info_help(Monitor *mon, const QDict *qdict) { - RockerPortList *list, *port; - const char *name = qdict_get_str(qdict, "name"); - Error *err = NULL; - - list = qmp_query_rocker_ports(name, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, " ena/ speed/ auto\n"); - monitor_printf(mon, " port link duplex neg?\n"); - - for (port = list; port; port = port->next) { - monitor_printf(mon, "%10s %-4s %-3s %2s %s\n", - port->value->name, - port->value->enabled ? port->value->link_up ? - "up" : "down" : "!ena", - port->value->speed == 10000 ? "10G" : "??", - port->value->duplex ? "FD" : "HD", - port->value->autoneg ? "Yes" : "No"); - } - - qapi_free_RockerPortList(list); + hmp_help_cmd(mon, "info"); } -void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) +void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) { - RockerOfDpaFlowList *list, *info; - const char *name = qdict_get_str(qdict, "name"); - uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); - Error *err = NULL; + int64_t max = qdict_get_try_int(qdict, "max", 10); + bool mean = qdict_get_try_bool(qdict, "mean", false); + bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false); + enum QSPSortBy sort_by; - list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, "prio tbl hits key(mask) --> actions\n"); - - for (info = list; info; info = info->next) { - RockerOfDpaFlow *flow = info->value; - RockerOfDpaFlowKey *key = flow->key; - RockerOfDpaFlowMask *mask = flow->mask; - RockerOfDpaFlowAction *action = flow->action; - - if (flow->hits) { - monitor_printf(mon, "%-4d %-3d %-4" PRIu64, - key->priority, key->tbl_id, flow->hits); - } else { - monitor_printf(mon, "%-4d %-3d ", - key->priority, key->tbl_id); - } - - if (key->has_in_pport) { - monitor_printf(mon, " pport %d", key->in_pport); - if (mask->has_in_pport) { - monitor_printf(mon, "(0x%x)", mask->in_pport); - } - } - - if (key->has_vlan_id) { - monitor_printf(mon, " vlan %d", - key->vlan_id & VLAN_VID_MASK); - if (mask->has_vlan_id) { - monitor_printf(mon, "(0x%x)", mask->vlan_id); - } - } - - if (key->has_tunnel_id) { - monitor_printf(mon, " tunnel %d", key->tunnel_id); - if (mask->has_tunnel_id) { - monitor_printf(mon, "(0x%x)", mask->tunnel_id); - } - } - - if (key->has_eth_type) { - switch (key->eth_type) { - case 0x0806: - monitor_printf(mon, " ARP"); - break; - case 0x0800: - monitor_printf(mon, " IP"); - break; - case 0x86dd: - monitor_printf(mon, " IPv6"); - break; - case 0x8809: - monitor_printf(mon, " LACP"); - break; - case 0x88cc: - monitor_printf(mon, " LLDP"); - break; - default: - monitor_printf(mon, " eth type 0x%04x", key->eth_type); - break; - } - } - - if (key->eth_src) { - if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) && - mask->eth_src && - (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " src <any mcast/bcast>"); - } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) && - mask->eth_src && - (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " src <any ucast>"); - } else { - monitor_printf(mon, " src %s", key->eth_src); - if (mask->eth_src) { - monitor_printf(mon, "(%s)", mask->eth_src); - } - } - } - - if (key->eth_dst) { - if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) && - mask->eth_dst && - (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " dst <any mcast/bcast>"); - } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) && - mask->eth_dst && - (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " dst <any ucast>"); - } else { - monitor_printf(mon, " dst %s", key->eth_dst); - if (mask->eth_dst) { - monitor_printf(mon, "(%s)", mask->eth_dst); - } - } - } - - if (key->has_ip_proto) { - monitor_printf(mon, " proto %d", key->ip_proto); - if (mask->has_ip_proto) { - monitor_printf(mon, "(0x%x)", mask->ip_proto); - } - } - - if (key->has_ip_tos) { - monitor_printf(mon, " TOS %d", key->ip_tos); - if (mask->has_ip_tos) { - monitor_printf(mon, "(0x%x)", mask->ip_tos); - } - } - - if (key->ip_dst) { - monitor_printf(mon, " dst %s", key->ip_dst); - } - - if (action->has_goto_tbl || action->has_group_id || - action->has_new_vlan_id) { - monitor_printf(mon, " -->"); - } - - if (action->has_new_vlan_id) { - monitor_printf(mon, " apply new vlan %d", - ntohs(action->new_vlan_id)); - } - - if (action->has_group_id) { - monitor_printf(mon, " write group 0x%08x", action->group_id); - } - - if (action->has_goto_tbl) { - monitor_printf(mon, " goto tbl %d", action->goto_tbl); - } - - monitor_printf(mon, "\n"); - } - - qapi_free_RockerOfDpaFlowList(list); + sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME; + qsp_report(max, sort_by, coalesce); } -void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) +void hmp_info_history(Monitor *mon, const QDict *qdict) { - RockerOfDpaGroupList *list, *g; - const char *name = qdict_get_str(qdict, "name"); - uint8_t type = qdict_get_try_int(qdict, "type", 9); - Error *err = NULL; + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); + int i; + const char *str; - list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err); - if (hmp_handle_error(mon, err)) { + if (!hmp_mon->rs) { return; } - - monitor_printf(mon, "id (decode) --> buckets\n"); - - for (g = list; g; g = g->next) { - RockerOfDpaGroup *group = g->value; - bool set = false; - - monitor_printf(mon, "0x%08x", group->id); - - monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" : - group->type == 1 ? "L2 rewrite" : - group->type == 2 ? "L3 unicast" : - group->type == 3 ? "L2 multicast" : - group->type == 4 ? "L2 flood" : - group->type == 5 ? "L3 interface" : - group->type == 6 ? "L3 multicast" : - group->type == 7 ? "L3 ECMP" : - group->type == 8 ? "L2 overlay" : - "unknown"); - - if (group->has_vlan_id) { - monitor_printf(mon, " vlan %d", group->vlan_id); - } - - if (group->has_pport) { - monitor_printf(mon, " pport %d", group->pport); - } - - if (group->has_index) { - monitor_printf(mon, " index %d", group->index); - } - - monitor_printf(mon, ") -->"); - - if (group->has_set_vlan_id && group->set_vlan_id) { - set = true; - monitor_printf(mon, " set vlan %d", - group->set_vlan_id & VLAN_VID_MASK); - } - - if (group->set_eth_src) { - if (!set) { - set = true; - monitor_printf(mon, " set"); - } - monitor_printf(mon, " src %s", group->set_eth_src); - } - - if (group->set_eth_dst) { - if (!set) { - monitor_printf(mon, " set"); - } - monitor_printf(mon, " dst %s", group->set_eth_dst); - } - - if (group->has_ttl_check && group->ttl_check) { - monitor_printf(mon, " check TTL"); - } - - if (group->has_group_id && group->group_id) { - monitor_printf(mon, " group id 0x%08x", group->group_id); - } - - if (group->has_pop_vlan && group->pop_vlan) { - monitor_printf(mon, " pop vlan"); - } - - if (group->has_out_pport) { - monitor_printf(mon, " out pport %d", group->out_pport); - } - - if (group->has_group_ids) { - struct uint32List *id; - - monitor_printf(mon, " groups ["); - for (id = group->group_ids; id; id = id->next) { - monitor_printf(mon, "0x%08x", id->value); - if (id->next) { - monitor_printf(mon, ","); - } - } - monitor_printf(mon, "]"); + i = 0; + for(;;) { + str = readline_get_history(hmp_mon->rs, i); + if (!str) { + break; } - - monitor_printf(mon, "\n"); + monitor_printf(mon, "%d: '%s'\n", i, str); + i++; } - - qapi_free_RockerOfDpaGroupList(list); } -void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict) +void hmp_logfile(Monitor *mon, const QDict *qdict) { Error *err = NULL; - GuidInfo *info = qmp_query_vm_generation_id(&err); - if (info) { - monitor_printf(mon, "%s\n", info->guid); + + if (!qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err)) { + error_report_err(err); } - hmp_handle_error(mon, err); - qapi_free_GuidInfo(info); } -void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict) +void hmp_log(Monitor *mon, const QDict *qdict) { + int mask; + const char *items = qdict_get_str(qdict, "items"); Error *err = NULL; - MemoryInfo *info = qmp_query_memory_size_summary(&err); - if (info) { - monitor_printf(mon, "base memory: %" PRIu64 "\n", - info->base_memory); - - if (info->has_plugged_memory) { - monitor_printf(mon, "plugged memory: %" PRIu64 "\n", - info->plugged_memory); - } - qapi_free_MemoryInfo(info); - } - hmp_handle_error(mon, err); -} - -static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) -{ - const char *unit = NULL; - monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type), - value->has_unit || value->exponent ? ", " : ""); - - if (value->has_unit) { - if (value->unit == STATS_UNIT_SECONDS) { - unit = "s"; - } else if (value->unit == STATS_UNIT_BYTES) { - unit = "B"; + if (!strcmp(items, "none")) { + mask = 0; + } else { + mask = qemu_str_to_log_mask(items); + if (!mask) { + hmp_help_cmd(mon, "log"); + return; } } - if (unit && value->base == 10 && - value->exponent >= -18 && value->exponent <= 18 && - value->exponent % 3 == 0) { - monitor_puts(mon, si_prefix(value->exponent)); - } else if (unit && value->base == 2 && - value->exponent >= 0 && value->exponent <= 60 && - value->exponent % 10 == 0) { - - monitor_puts(mon, iec_binary_prefix(value->exponent)); - } else if (value->exponent) { - /* Use exponential notation and write the unit's English name */ - monitor_printf(mon, "* %d^%d%s", - value->base, value->exponent, - value->has_unit ? " " : ""); - unit = NULL; + if (!qemu_set_log(mask, &err)) { + error_report_err(err); } - - if (value->has_unit) { - monitor_puts(mon, unit ? unit : StatsUnit_str(value->unit)); - } - - /* Print bucket size for linear histograms */ - if (value->type == STATS_TYPE_LINEAR_HISTOGRAM && value->has_bucket_size) { - monitor_printf(mon, ", bucket size=%d", value->bucket_size); - } - monitor_printf(mon, ")"); } -static StatsSchemaValueList *find_schema_value_list( - StatsSchemaList *list, StatsProvider provider, - StatsTarget target) +void hmp_gdbserver(Monitor *mon, const QDict *qdict) { - StatsSchemaList *node; - - for (node = list; node; node = node->next) { - if (node->value->provider == provider && - node->value->target == target) { - return node->value->stats; - } + const char *device = qdict_get_try_str(qdict, "device"); + if (!device) { + device = "tcp::" DEFAULT_GDBSTUB_PORT; } - return NULL; -} -static void print_stats_results(Monitor *mon, StatsTarget target, - bool show_provider, - StatsResult *result, - StatsSchemaList *schema) -{ - /* Find provider schema */ - StatsSchemaValueList *schema_value_list = - find_schema_value_list(schema, result->provider, target); - StatsList *stats_list; - - if (!schema_value_list) { - monitor_printf(mon, "failed to find schema list for %s\n", - StatsProvider_str(result->provider)); - return; - } - - if (show_provider) { - monitor_printf(mon, "provider: %s\n", - StatsProvider_str(result->provider)); - } - - for (stats_list = result->stats; stats_list; - stats_list = stats_list->next, - schema_value_list = schema_value_list->next) { - - Stats *stats = stats_list->value; - StatsValue *stats_value = stats->value; - StatsSchemaValue *schema_value = schema_value_list->value; - - /* Find schema entry */ - while (!g_str_equal(stats->name, schema_value->name)) { - if (!schema_value_list->next) { - monitor_printf(mon, "failed to find schema entry for %s\n", - stats->name); - return; - } - schema_value_list = schema_value_list->next; - schema_value = schema_value_list->value; - } - - print_stats_schema_value(mon, schema_value); - - if (stats_value->type == QTYPE_QNUM) { - monitor_printf(mon, ": %" PRId64 "\n", stats_value->u.scalar); - } else if (stats_value->type == QTYPE_QBOOL) { - monitor_printf(mon, ": %s\n", stats_value->u.boolean ? "yes" : "no"); - } else if (stats_value->type == QTYPE_QLIST) { - uint64List *list; - int i; - - monitor_printf(mon, ": "); - for (list = stats_value->u.list, i = 1; - list; - list = list->next, i++) { - monitor_printf(mon, "[%d]=%" PRId64 " ", i, list->value); - } - monitor_printf(mon, "\n"); - } + if (gdbserver_start(device) < 0) { + monitor_printf(mon, "Could not open gdbserver on device '%s'\n", + device); + } else if (strcmp(device, "none") == 0) { + monitor_printf(mon, "Disabled gdbserver\n"); + } else { + monitor_printf(mon, "Waiting for gdb connection on device '%s'\n", + device); } } -/* Create the StatsFilter that is needed for an "info stats" invocation. */ -static StatsFilter *stats_filter(StatsTarget target, const char *names, - int cpu_index, StatsProvider provider) +void hmp_print(Monitor *mon, const QDict *qdict) { - StatsFilter *filter = g_malloc0(sizeof(*filter)); - StatsProvider provider_idx; - StatsRequestList *request_list = NULL; + int format = qdict_get_int(qdict, "format"); + hwaddr val = qdict_get_int(qdict, "val"); - filter->target = target; - switch (target) { - case STATS_TARGET_VM: + switch(format) { + case 'o': + monitor_printf(mon, "%#" HWADDR_PRIo, val); break; - case STATS_TARGET_VCPU: - { - strList *vcpu_list = NULL; - CPUState *cpu = qemu_get_cpu(cpu_index); - char *canonical_path = object_get_canonical_path(OBJECT(cpu)); - - QAPI_LIST_PREPEND(vcpu_list, canonical_path); - filter->u.vcpu.has_vcpus = true; - filter->u.vcpu.vcpus = vcpu_list; + case 'x': + monitor_printf(mon, "%#" HWADDR_PRIx, val); break; - } - default: + case 'u': + monitor_printf(mon, "%" HWADDR_PRIu, val); break; - } - - if (!names && provider == STATS_PROVIDER__MAX) { - return filter; - } - - /* - * "info stats" can only query either one or all the providers. Querying - * by name, but not by provider, requires the creation of one filter per - * provider. - */ - for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) { - if (provider == STATS_PROVIDER__MAX || provider == provider_idx) { - StatsRequest *request = g_new0(StatsRequest, 1); - request->provider = provider_idx; - if (names && !g_str_equal(names, "*")) { - request->has_names = true; - request->names = strList_from_comma_list(names); - } - QAPI_LIST_PREPEND(request_list, request); - } - } - - filter->has_providers = true; - filter->providers = request_list; - return filter; -} - -void hmp_info_stats(Monitor *mon, const QDict *qdict) -{ - const char *target_str = qdict_get_str(qdict, "target"); - const char *provider_str = qdict_get_try_str(qdict, "provider"); - const char *names = qdict_get_try_str(qdict, "names"); - - StatsProvider provider = STATS_PROVIDER__MAX; - StatsTarget target; - Error *err = NULL; - g_autoptr(StatsSchemaList) schema = NULL; - g_autoptr(StatsResultList) stats = NULL; - g_autoptr(StatsFilter) filter = NULL; - StatsResultList *entry; - - target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err); - if (err) { - monitor_printf(mon, "invalid stats target %s\n", target_str); - goto exit_no_print; - } - if (provider_str) { - provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err); - if (err) { - monitor_printf(mon, "invalid stats provider %s\n", provider_str); - goto exit_no_print; - } - } - - schema = qmp_query_stats_schemas(provider_str ? true : false, - provider, &err); - if (err) { - goto exit; - } - - switch (target) { - case STATS_TARGET_VM: - filter = stats_filter(target, names, -1, provider); + default: + case 'd': + monitor_printf(mon, "%" HWADDR_PRId, val); break; - case STATS_TARGET_VCPU: {} - int cpu_index = monitor_get_cpu_index(mon); - filter = stats_filter(target, names, cpu_index, provider); + case 'c': + monitor_printc(mon, val); break; - default: - abort(); - } - - stats = qmp_query_stats(filter, &err); - if (err) { - goto exit; - } - for (entry = stats; entry; entry = entry->next) { - print_stats_results(mon, target, provider_str == NULL, entry->value, schema); - } - -exit: - if (err) { - monitor_printf(mon, "%s\n", error_get_pretty(err)); - } -exit_no_print: - error_free(err); -} - -static void hmp_virtio_dump_protocols(Monitor *mon, - VhostDeviceProtocols *pcol) -{ - strList *pcol_list = pcol->protocols; - while (pcol_list) { - monitor_printf(mon, "\t%s", pcol_list->value); - pcol_list = pcol_list->next; - if (pcol_list != NULL) { - monitor_printf(mon, ",\n"); - } } monitor_printf(mon, "\n"); - if (pcol->has_unknown_protocols) { - monitor_printf(mon, " unknown-protocols(0x%016"PRIx64")\n", - pcol->unknown_protocols); - } } -static void hmp_virtio_dump_status(Monitor *mon, - VirtioDeviceStatus *status) +void hmp_sum(Monitor *mon, const QDict *qdict) { - strList *status_list = status->statuses; - while (status_list) { - monitor_printf(mon, "\t%s", status_list->value); - status_list = status_list->next; - if (status_list != NULL) { - monitor_printf(mon, ",\n"); - } - } - monitor_printf(mon, "\n"); - if (status->has_unknown_statuses) { - monitor_printf(mon, " unknown-statuses(0x%016"PRIx32")\n", - status->unknown_statuses); - } -} - -static void hmp_virtio_dump_features(Monitor *mon, - VirtioDeviceFeatures *features) -{ - strList *transport_list = features->transports; - while (transport_list) { - monitor_printf(mon, "\t%s", transport_list->value); - transport_list = transport_list->next; - if (transport_list != NULL) { - monitor_printf(mon, ",\n"); - } - } - - monitor_printf(mon, "\n"); - strList *list = features->dev_features; - if (list) { - while (list) { - monitor_printf(mon, "\t%s", list->value); - list = list->next; - if (list != NULL) { - monitor_printf(mon, ",\n"); - } - } - monitor_printf(mon, "\n"); - } + uint32_t addr; + uint16_t sum; + uint32_t start = qdict_get_int(qdict, "start"); + uint32_t size = qdict_get_int(qdict, "size"); - if (features->has_unknown_dev_features) { - monitor_printf(mon, " unknown-features(0x%016"PRIx64")\n", - features->unknown_dev_features); + sum = 0; + for(addr = start; addr < (start + size); addr++) { + uint8_t val = address_space_ldub(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + /* BSD sum algorithm ('sum' Unix command) */ + sum = (sum >> 1) | (sum << 15); + sum += val; } + monitor_printf(mon, "%05d\n", sum); } -void hmp_virtio_query(Monitor *mon, const QDict *qdict) +void hmp_ioport_read(Monitor *mon, const QDict *qdict) { - Error *err = NULL; - VirtioInfoList *list = qmp_x_query_virtio(&err); - VirtioInfoList *node; + int size = qdict_get_int(qdict, "size"); + int addr = qdict_get_int(qdict, "addr"); + int has_index = qdict_haskey(qdict, "index"); + uint32_t val; + int suffix; - if (err != NULL) { - hmp_handle_error(mon, err); - return; - } - - if (list == NULL) { - monitor_printf(mon, "No VirtIO devices\n"); - return; + if (has_index) { + int index = qdict_get_int(qdict, "index"); + cpu_outb(addr & IOPORTS_MASK, index & 0xff); + addr++; } + addr &= 0xffff; - node = list; - while (node) { - monitor_printf(mon, "%s [%s]\n", node->value->path, - node->value->name); - node = node->next; + switch(size) { + default: + case 1: + val = cpu_inb(addr); + suffix = 'b'; + break; + case 2: + val = cpu_inw(addr); + suffix = 'w'; + break; + case 4: + val = cpu_inl(addr); + suffix = 'l'; + break; } - qapi_free_VirtioInfoList(list); + monitor_printf(mon, "port%c[0x%04x] = 0x%0*x\n", + suffix, addr, size * 2, val); } -void hmp_virtio_status(Monitor *mon, const QDict *qdict) +void hmp_ioport_write(Monitor *mon, const QDict *qdict) { - Error *err = NULL; - const char *path = qdict_get_try_str(qdict, "path"); - VirtioStatus *s = qmp_x_query_virtio_status(path, &err); + int size = qdict_get_int(qdict, "size"); + int addr = qdict_get_int(qdict, "addr"); + int val = qdict_get_int(qdict, "val"); - if (err != NULL) { - hmp_handle_error(mon, err); - return; - } - - monitor_printf(mon, "%s:\n", path); - monitor_printf(mon, " device_name: %s %s\n", - s->name, s->vhost_dev ? "(vhost)" : ""); - monitor_printf(mon, " device_id: %d\n", s->device_id); - monitor_printf(mon, " vhost_started: %s\n", - s->vhost_started ? "true" : "false"); - monitor_printf(mon, " bus_name: %s\n", s->bus_name); - monitor_printf(mon, " broken: %s\n", - s->broken ? "true" : "false"); - monitor_printf(mon, " disabled: %s\n", - s->disabled ? "true" : "false"); - monitor_printf(mon, " disable_legacy_check: %s\n", - s->disable_legacy_check ? "true" : "false"); - monitor_printf(mon, " started: %s\n", - s->started ? "true" : "false"); - monitor_printf(mon, " use_started: %s\n", - s->use_started ? "true" : "false"); - monitor_printf(mon, " start_on_kick: %s\n", - s->start_on_kick ? "true" : "false"); - monitor_printf(mon, " use_guest_notifier_mask: %s\n", - s->use_guest_notifier_mask ? "true" : "false"); - monitor_printf(mon, " vm_running: %s\n", - s->vm_running ? "true" : "false"); - monitor_printf(mon, " num_vqs: %"PRId64"\n", s->num_vqs); - monitor_printf(mon, " queue_sel: %d\n", - s->queue_sel); - monitor_printf(mon, " isr: %d\n", s->isr); - monitor_printf(mon, " endianness: %s\n", - s->device_endian); - monitor_printf(mon, " status:\n"); - hmp_virtio_dump_status(mon, s->status); - monitor_printf(mon, " Guest features:\n"); - hmp_virtio_dump_features(mon, s->guest_features); - monitor_printf(mon, " Host features:\n"); - hmp_virtio_dump_features(mon, s->host_features); - monitor_printf(mon, " Backend features:\n"); - hmp_virtio_dump_features(mon, s->backend_features); - - if (s->vhost_dev) { - monitor_printf(mon, " VHost:\n"); - monitor_printf(mon, " nvqs: %d\n", - s->vhost_dev->nvqs); - monitor_printf(mon, " vq_index: %"PRId64"\n", - s->vhost_dev->vq_index); - monitor_printf(mon, " max_queues: %"PRId64"\n", - s->vhost_dev->max_queues); - monitor_printf(mon, " n_mem_sections: %"PRId64"\n", - s->vhost_dev->n_mem_sections); - monitor_printf(mon, " n_tmp_sections: %"PRId64"\n", - s->vhost_dev->n_tmp_sections); - monitor_printf(mon, " backend_cap: %"PRId64"\n", - s->vhost_dev->backend_cap); - monitor_printf(mon, " log_enabled: %s\n", - s->vhost_dev->log_enabled ? "true" : "false"); - monitor_printf(mon, " log_size: %"PRId64"\n", - s->vhost_dev->log_size); - monitor_printf(mon, " Features:\n"); - hmp_virtio_dump_features(mon, s->vhost_dev->features); - monitor_printf(mon, " Acked features:\n"); - hmp_virtio_dump_features(mon, s->vhost_dev->acked_features); - monitor_printf(mon, " Backend features:\n"); - hmp_virtio_dump_features(mon, s->vhost_dev->backend_features); - monitor_printf(mon, " Protocol features:\n"); - hmp_virtio_dump_protocols(mon, s->vhost_dev->protocol_features); - } + addr &= IOPORTS_MASK; - qapi_free_VirtioStatus(s); -} - -void hmp_vhost_queue_status(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *path = qdict_get_try_str(qdict, "path"); - int queue = qdict_get_int(qdict, "queue"); - VirtVhostQueueStatus *s = - qmp_x_query_virtio_vhost_queue_status(path, queue, &err); - - if (err != NULL) { - hmp_handle_error(mon, err); - return; + switch (size) { + default: + case 1: + cpu_outb(addr, val); + break; + case 2: + cpu_outw(addr, val); + break; + case 4: + cpu_outl(addr, val); + break; } - - monitor_printf(mon, "%s:\n", path); - monitor_printf(mon, " device_name: %s (vhost)\n", - s->name); - monitor_printf(mon, " kick: %"PRId64"\n", s->kick); - monitor_printf(mon, " call: %"PRId64"\n", s->call); - monitor_printf(mon, " VRing:\n"); - monitor_printf(mon, " num: %"PRId64"\n", s->num); - monitor_printf(mon, " desc: 0x%016"PRIx64"\n", s->desc); - monitor_printf(mon, " desc_phys: 0x%016"PRIx64"\n", - s->desc_phys); - monitor_printf(mon, " desc_size: %"PRId32"\n", s->desc_size); - monitor_printf(mon, " avail: 0x%016"PRIx64"\n", s->avail); - monitor_printf(mon, " avail_phys: 0x%016"PRIx64"\n", - s->avail_phys); - monitor_printf(mon, " avail_size: %"PRId32"\n", s->avail_size); - monitor_printf(mon, " used: 0x%016"PRIx64"\n", s->used); - monitor_printf(mon, " used_phys: 0x%016"PRIx64"\n", - s->used_phys); - monitor_printf(mon, " used_size: %"PRId32"\n", s->used_size); - - qapi_free_VirtVhostQueueStatus(s); } -void hmp_virtio_queue_status(Monitor *mon, const QDict *qdict) +void hmp_boot_set(Monitor *mon, const QDict *qdict) { - Error *err = NULL; - const char *path = qdict_get_try_str(qdict, "path"); - int queue = qdict_get_int(qdict, "queue"); - VirtQueueStatus *s = qmp_x_query_virtio_queue_status(path, queue, &err); - - if (err != NULL) { - hmp_handle_error(mon, err); - return; - } + Error *local_err = NULL; + const char *bootdevice = qdict_get_str(qdict, "bootdevice"); - monitor_printf(mon, "%s:\n", path); - monitor_printf(mon, " device_name: %s\n", s->name); - monitor_printf(mon, " queue_index: %d\n", s->queue_index); - monitor_printf(mon, " inuse: %d\n", s->inuse); - monitor_printf(mon, " used_idx: %d\n", s->used_idx); - monitor_printf(mon, " signalled_used: %d\n", - s->signalled_used); - monitor_printf(mon, " signalled_used_valid: %s\n", - s->signalled_used_valid ? "true" : "false"); - if (s->has_last_avail_idx) { - monitor_printf(mon, " last_avail_idx: %d\n", - s->last_avail_idx); - } - if (s->has_shadow_avail_idx) { - monitor_printf(mon, " shadow_avail_idx: %d\n", - s->shadow_avail_idx); + qemu_boot_set(bootdevice, &local_err); + if (local_err) { + error_report_err(local_err); + } else { + monitor_printf(mon, "boot device list now set to %s\n", bootdevice); } - monitor_printf(mon, " VRing:\n"); - monitor_printf(mon, " num: %"PRId32"\n", s->vring_num); - monitor_printf(mon, " num_default: %"PRId32"\n", - s->vring_num_default); - monitor_printf(mon, " align: %"PRId32"\n", - s->vring_align); - monitor_printf(mon, " desc: 0x%016"PRIx64"\n", - s->vring_desc); - monitor_printf(mon, " avail: 0x%016"PRIx64"\n", - s->vring_avail); - monitor_printf(mon, " used: 0x%016"PRIx64"\n", - s->vring_used); - - qapi_free_VirtQueueStatus(s); } -void hmp_virtio_queue_element(Monitor *mon, const QDict *qdict) +void hmp_info_mtree(Monitor *mon, const QDict *qdict) { - Error *err = NULL; - const char *path = qdict_get_try_str(qdict, "path"); - int queue = qdict_get_int(qdict, "queue"); - int index = qdict_get_try_int(qdict, "index", -1); - VirtioQueueElement *e; - VirtioRingDescList *list; - - e = qmp_x_query_virtio_queue_element(path, queue, index != -1, - index, &err); - if (err != NULL) { - hmp_handle_error(mon, err); - return; - } + bool flatview = qdict_get_try_bool(qdict, "flatview", false); + bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false); + bool owner = qdict_get_try_bool(qdict, "owner", false); + bool disabled = qdict_get_try_bool(qdict, "disabled", false); - monitor_printf(mon, "%s:\n", path); - monitor_printf(mon, " device_name: %s\n", e->name); - monitor_printf(mon, " index: %d\n", e->index); - monitor_printf(mon, " desc:\n"); - monitor_printf(mon, " descs:\n"); - - list = e->descs; - while (list) { - monitor_printf(mon, " addr 0x%"PRIx64" len %d", - list->value->addr, list->value->len); - if (list->value->flags) { - strList *flag = list->value->flags; - monitor_printf(mon, " ("); - while (flag) { - monitor_printf(mon, "%s", flag->value); - flag = flag->next; - if (flag) { - monitor_printf(mon, ", "); - } - } - monitor_printf(mon, ")"); - } - list = list->next; - if (list) { - monitor_printf(mon, ",\n"); - } - } - monitor_printf(mon, "\n"); - monitor_printf(mon, " avail:\n"); - monitor_printf(mon, " flags: %d\n", e->avail->flags); - monitor_printf(mon, " idx: %d\n", e->avail->idx); - monitor_printf(mon, " ring: %d\n", e->avail->ring); - monitor_printf(mon, " used:\n"); - monitor_printf(mon, " flags: %d\n", e->used->flags); - monitor_printf(mon, " idx: %d\n", e->used->idx); - - qapi_free_VirtioQueueElement(e); + mtree_info(flatview, dispatch_tree, owner, disabled); } diff --git a/monitor/hmp-target.c b/monitor/hmp-target.c new file mode 100644 index 0000000000..1eb72ac1bf --- /dev/null +++ b/monitor/hmp-target.c @@ -0,0 +1,178 @@ +/* + * QEMU monitor, target-dependent part + * + * Copyright (c) 2003-2004 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. + */ + +#include "qemu/osdep.h" +#include "monitor-internal.h" +#include "monitor/qdev.h" +#include "net/slirp.h" +#include "sysemu/device_tree.h" +#include "monitor/hmp-target.h" +#include "monitor/hmp.h" +#include "block/block-hmp-cmds.h" +#include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/error.h" +#include "qemu/cutils.h" + +#if defined(TARGET_S390X) +#include "hw/s390x/storage-keys.h" +#include "hw/s390x/storage-attributes.h" +#endif + +/* Make devices configuration available for use in hmp-commands*.hx templates */ +#include CONFIG_DEVICES + +static HMPCommand hmp_info_cmds[]; + +/** + * Is @name in the '|' separated list of names @list? + */ +int hmp_compare_cmd(const char *name, const char *list) +{ + const char *p, *pstart; + int len; + len = strlen(name); + p = list; + for (;;) { + pstart = p; + p = qemu_strchrnul(p, '|'); + if ((p - pstart) == len && !memcmp(pstart, name, len)) { + return 1; + } + if (*p == '\0') { + break; + } + p++; + } + return 0; +} + +/* Please update hmp-commands.hx when adding or changing commands */ +static HMPCommand hmp_info_cmds[] = { +#include "hmp-commands-info.h" + { NULL, NULL, }, +}; + +/* hmp_cmds and hmp_info_cmds would be sorted at runtime */ +HMPCommand hmp_cmds[] = { +#include "hmp-commands.h" + { NULL, NULL, }, +}; + +/* + * Set @pval to the value in the register identified by @name. + * return 0 if OK, -1 if not found + */ +int get_monitor_def(Monitor *mon, int64_t *pval, const char *name) +{ + const MonitorDef *md = target_monitor_defs(); + CPUState *cs = mon_get_cpu(mon); + void *ptr; + uint64_t tmp = 0; + int ret; + + if (cs == NULL || md == NULL) { + return -1; + } + + for(; md->name != NULL; md++) { + if (hmp_compare_cmd(name, md->name)) { + if (md->get_value) { + *pval = md->get_value(mon, md, md->offset); + } else { + CPUArchState *env = mon_get_cpu_env(mon); + ptr = (uint8_t *)env + md->offset; + switch(md->type) { + case MD_I32: + *pval = *(int32_t *)ptr; + break; + case MD_TLONG: + *pval = *(target_long *)ptr; + break; + default: + *pval = 0; + break; + } + } + return 0; + } + } + + ret = target_get_monitor_def(cs, name, &tmp); + if (!ret) { + *pval = (target_long) tmp; + } + + return ret; +} + +static int +compare_mon_cmd(const void *a, const void *b) +{ + return strcmp(((const HMPCommand *)a)->name, + ((const HMPCommand *)b)->name); +} + +static void __attribute__((__constructor__)) sortcmdlist(void) +{ + qsort(hmp_cmds, ARRAY_SIZE(hmp_cmds) - 1, + sizeof(*hmp_cmds), + compare_mon_cmd); + qsort(hmp_info_cmds, ARRAY_SIZE(hmp_info_cmds) - 1, + sizeof(*hmp_info_cmds), + compare_mon_cmd); +} + +void monitor_register_hmp(const char *name, bool info, + void (*cmd)(Monitor *mon, const QDict *qdict)) +{ + HMPCommand *table = info ? hmp_info_cmds : hmp_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); + table->cmd = cmd; + return; + } + table++; + } + g_assert_not_reached(); +} + +void monitor_register_hmp_info_hrt(const char *name, + HumanReadableText *(*handler)(Error **errp)) +{ + HMPCommand *table = hmp_info_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); + table->cmd_info_hrt = handler; + return; + } + table++; + } + g_assert_not_reached(); +} diff --git a/monitor/hmp.c b/monitor/hmp.c index 43fd69f984..2aa85d3982 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -27,7 +27,6 @@ #include "hw/qdev-core.h" #include "monitor-internal.h" #include "monitor/hmp.h" -#include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" #include "qemu/config-file.h" @@ -37,7 +36,6 @@ #include "qemu/option.h" #include "qemu/units.h" #include "sysemu/block-backend.h" -#include "sysemu/runstate.h" #include "trace.h" static void monitor_command_cb(void *opaque, const char *cmdline, @@ -274,7 +272,7 @@ static void help_cmd_dump(Monitor *mon, const HMPCommand *cmds, } } -void help_cmd(Monitor *mon, const char *name) +void hmp_help_cmd(Monitor *mon, const char *name) { char *args[MAX_ARGS]; int nb_args = 0; @@ -1191,8 +1189,8 @@ static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) } memcpy(cmd, pstart, len); cmd[len] = '\0'; - if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) { - readline_add_completion(mon->rs, cmd); + if (name[0] == '\0') { + readline_add_completion_of(mon->rs, name, cmd); } if (*p == '\0') { break; @@ -1272,7 +1270,7 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, { const char *cmdname; int i; - const char *ptype, *old_ptype, *str, *name; + const char *ptype, *old_ptype, *str; const HMPCommand *cmd; BlockBackend *blk = NULL; @@ -1337,10 +1335,8 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, /* block device name completion */ readline_set_completion_index(mon->rs, strlen(str)); while ((blk = blk_next(blk)) != NULL) { - name = blk_name(blk); - if (str[0] == '\0' || - !strncmp(name, str, strlen(str))) { - readline_add_completion(mon->rs, name); + if (str[0] == '\0') { + readline_add_completion_of(mon->rs, str, blk_name(blk)); } } break; diff --git a/monitor/meson.build b/monitor/meson.build index 6d00985ace..ccb4d1a8e6 100644 --- a/monitor/meson.build +++ b/monitor/meson.build @@ -1,9 +1,11 @@ qmp_ss.add(files('monitor.c', 'qmp.c', 'qmp-cmds-control.c')) softmmu_ss.add(files( + 'fds.c', 'hmp-cmds.c', 'hmp.c', )) softmmu_ss.add([spice_headers, files('qmp-cmds.c')]) -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files('misc.c'), spice]) +specific_ss.add(when: 'CONFIG_SOFTMMU', + if_true: [files( 'hmp-cmds-target.c', 'hmp-target.c'), spice]) diff --git a/monitor/misc.c b/monitor/misc.c deleted file mode 100644 index 053af4045e..0000000000 --- a/monitor/misc.c +++ /dev/null @@ -1,1906 +0,0 @@ -/* - * QEMU monitor - * - * Copyright (c) 2003-2004 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. - */ - -#include "qemu/osdep.h" -#include "monitor-internal.h" -#include "monitor/qdev.h" -#include "hw/pci/pci.h" -#include "sysemu/watchdog.h" -#include "exec/gdbstub.h" -#include "net/net.h" -#include "net/slirp.h" -#include "ui/qemu-spice.h" -#include "qemu/config-file.h" -#include "qemu/ctype.h" -#include "audio/audio.h" -#include "disas/disas.h" -#include "qemu/timer.h" -#include "qemu/log.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "authz/list.h" -#include "qapi/util.h" -#include "sysemu/sysemu.h" -#include "sysemu/device_tree.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" -#include "qapi/qmp/qstring.h" -#include "qom/object_interfaces.h" -#include "trace/control.h" -#include "monitor/hmp-target.h" -#include "monitor/hmp.h" -#ifdef CONFIG_TRACE_SIMPLE -#include "trace/simple.h" -#endif -#include "exec/memory.h" -#include "exec/exec-all.h" -#include "qemu/option.h" -#include "qemu/thread.h" -#include "block/qapi.h" -#include "block/block-hmp-cmds.h" -#include "qapi/qapi-commands-char.h" -#include "qapi/qapi-commands-control.h" -#include "qapi/qapi-commands-migration.h" -#include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-commands-qom.h" -#include "qapi/qapi-commands-run-state.h" -#include "qapi/qapi-commands-trace.h" -#include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-init-commands.h" -#include "qapi/error.h" -#include "qapi/qmp-event.h" -#include "qemu/cutils.h" - -#if defined(TARGET_S390X) -#include "hw/s390x/storage-keys.h" -#include "hw/s390x/storage-attributes.h" -#endif - -/* Make devices configuration available for use in hmp-commands*.hx templates */ -#include CONFIG_DEVICES - -/* file descriptors passed via SCM_RIGHTS */ -typedef struct mon_fd_t mon_fd_t; -struct mon_fd_t { - char *name; - int fd; - QLIST_ENTRY(mon_fd_t) next; -}; - -/* file descriptor associated with a file descriptor set */ -typedef struct MonFdsetFd MonFdsetFd; -struct MonFdsetFd { - int fd; - bool removed; - char *opaque; - QLIST_ENTRY(MonFdsetFd) next; -}; - -/* file descriptor set containing fds passed via SCM_RIGHTS */ -typedef struct MonFdset MonFdset; -struct MonFdset { - int64_t id; - QLIST_HEAD(, MonFdsetFd) fds; - QLIST_HEAD(, MonFdsetFd) dup_fds; - QLIST_ENTRY(MonFdset) next; -}; - -/* Protects mon_fdsets */ -static QemuMutex mon_fdsets_lock; -static QLIST_HEAD(, MonFdset) mon_fdsets; - -static HMPCommand hmp_info_cmds[]; - -char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, - int64_t cpu_index, Error **errp) -{ - char *output = NULL; - MonitorHMP hmp = {}; - - monitor_data_init(&hmp.common, false, true, false); - - if (has_cpu_index) { - int ret = monitor_set_cpu(&hmp.common, cpu_index); - if (ret < 0) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", - "a CPU number"); - goto out; - } - } - - handle_hmp_command(&hmp, command_line); - - WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { - output = g_strdup(hmp.common.outbuf->str); - } - -out: - monitor_data_destroy(&hmp.common); - return output; -} - -/** - * Is @name in the '|' separated list of names @list? - */ -int hmp_compare_cmd(const char *name, const char *list) -{ - const char *p, *pstart; - int len; - len = strlen(name); - p = list; - for (;;) { - pstart = p; - p = qemu_strchrnul(p, '|'); - if ((p - pstart) == len && !memcmp(pstart, name, len)) { - return 1; - } - if (*p == '\0') { - break; - } - p++; - } - return 0; -} - -static void do_help_cmd(Monitor *mon, const QDict *qdict) -{ - help_cmd(mon, qdict_get_try_str(qdict, "name")); -} - -static void hmp_trace_event(Monitor *mon, const QDict *qdict) -{ - const char *tp_name = qdict_get_str(qdict, "name"); - bool new_state = qdict_get_bool(qdict, "option"); - bool has_vcpu = qdict_haskey(qdict, "vcpu"); - int vcpu = qdict_get_try_int(qdict, "vcpu", 0); - Error *local_err = NULL; - - if (vcpu < 0) { - monitor_printf(mon, "argument vcpu must be positive"); - return; - } - - qmp_trace_event_set_state(tp_name, new_state, true, true, has_vcpu, vcpu, &local_err); - if (local_err) { - error_report_err(local_err); - } -} - -#ifdef CONFIG_TRACE_SIMPLE -static void hmp_trace_file(Monitor *mon, const QDict *qdict) -{ - const char *op = qdict_get_try_str(qdict, "op"); - const char *arg = qdict_get_try_str(qdict, "arg"); - - if (!op) { - st_print_trace_file_status(); - } else if (!strcmp(op, "on")) { - st_set_trace_file_enabled(true); - } else if (!strcmp(op, "off")) { - st_set_trace_file_enabled(false); - } else if (!strcmp(op, "flush")) { - st_flush_trace_buffer(); - } else if (!strcmp(op, "set")) { - if (arg) { - st_set_trace_file(arg); - } - } else { - monitor_printf(mon, "unexpected argument \"%s\"\n", op); - help_cmd(mon, "trace-file"); - } -} -#endif - -static void hmp_info_help(Monitor *mon, const QDict *qdict) -{ - help_cmd(mon, "info"); -} - -static void monitor_init_qmp_commands(void) -{ - /* - * Two command lists: - * - qmp_commands contains all QMP commands - * - qmp_cap_negotiation_commands contains just - * "qmp_capabilities", to enforce capability negotiation - */ - - qmp_init_marshal(&qmp_commands); - - qmp_register_command(&qmp_commands, "device_add", - qmp_device_add, 0, 0); - - QTAILQ_INIT(&qmp_cap_negotiation_commands); - qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, - QCO_ALLOW_PRECONFIG, 0); -} - -/* Set the current CPU defined by the user. Callers must hold BQL. */ -int monitor_set_cpu(Monitor *mon, int cpu_index) -{ - CPUState *cpu; - - cpu = qemu_get_cpu(cpu_index); - if (cpu == NULL) { - return -1; - } - g_free(mon->mon_cpu_path); - mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); - return 0; -} - -/* Callers must hold BQL. */ -static CPUState *mon_get_cpu_sync(Monitor *mon, bool synchronize) -{ - CPUState *cpu = NULL; - - if (mon->mon_cpu_path) { - cpu = (CPUState *) object_resolve_path_type(mon->mon_cpu_path, - TYPE_CPU, NULL); - if (!cpu) { - g_free(mon->mon_cpu_path); - mon->mon_cpu_path = NULL; - } - } - if (!mon->mon_cpu_path) { - if (!first_cpu) { - return NULL; - } - monitor_set_cpu(mon, first_cpu->cpu_index); - cpu = first_cpu; - } - assert(cpu != NULL); - if (synchronize) { - cpu_synchronize_state(cpu); - } - return cpu; -} - -CPUState *mon_get_cpu(Monitor *mon) -{ - return mon_get_cpu_sync(mon, true); -} - -CPUArchState *mon_get_cpu_env(Monitor *mon) -{ - CPUState *cs = mon_get_cpu(mon); - - return cs ? cs->env_ptr : NULL; -} - -int monitor_get_cpu_index(Monitor *mon) -{ - CPUState *cs = mon_get_cpu_sync(mon, false); - - return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; -} - -static void hmp_info_registers(Monitor *mon, const QDict *qdict) -{ - bool all_cpus = qdict_get_try_bool(qdict, "cpustate_all", false); - int vcpu = qdict_get_try_int(qdict, "vcpu", -1); - CPUState *cs; - - if (all_cpus) { - CPU_FOREACH(cs) { - monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); - } - } else { - cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : mon_get_cpu(mon); - - if (!cs) { - if (vcpu >= 0) { - monitor_printf(mon, "CPU#%d not available\n", vcpu); - } else { - monitor_printf(mon, "No CPU available\n"); - } - return; - } - - monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); - } -} - -static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) -{ - int64_t max = qdict_get_try_int(qdict, "max", 10); - bool mean = qdict_get_try_bool(qdict, "mean", false); - bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false); - enum QSPSortBy sort_by; - - sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME; - qsp_report(max, sort_by, coalesce); -} - -static void hmp_info_history(Monitor *mon, const QDict *qdict) -{ - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); - int i; - const char *str; - - if (!hmp_mon->rs) { - return; - } - i = 0; - for(;;) { - str = readline_get_history(hmp_mon->rs, i); - if (!str) { - break; - } - monitor_printf(mon, "%d: '%s'\n", i, str); - i++; - } -} - -static void hmp_info_trace_events(Monitor *mon, const QDict *qdict) -{ - const char *name = qdict_get_try_str(qdict, "name"); - bool has_vcpu = qdict_haskey(qdict, "vcpu"); - int vcpu = qdict_get_try_int(qdict, "vcpu", 0); - TraceEventInfoList *events; - TraceEventInfoList *elem; - Error *local_err = NULL; - - if (name == NULL) { - name = "*"; - } - if (vcpu < 0) { - monitor_printf(mon, "argument vcpu must be positive"); - return; - } - - events = qmp_trace_event_get_state(name, has_vcpu, vcpu, &local_err); - if (local_err) { - error_report_err(local_err); - return; - } - - for (elem = events; elem != NULL; elem = elem->next) { - monitor_printf(mon, "%s : state %u\n", - elem->value->name, - elem->value->state == TRACE_EVENT_STATE_ENABLED ? 1 : 0); - } - qapi_free_TraceEventInfoList(events); -} - -void qmp_client_migrate_info(const char *protocol, const char *hostname, - bool has_port, int64_t port, - bool has_tls_port, int64_t tls_port, - const char *cert_subject, - Error **errp) -{ - if (strcmp(protocol, "spice") == 0) { - if (!qemu_using_spice(errp)) { - return; - } - - if (!has_port && !has_tls_port) { - error_setg(errp, QERR_MISSING_PARAMETER, "port/tls-port"); - return; - } - - if (qemu_spice.migrate_info(hostname, - has_port ? port : -1, - has_tls_port ? tls_port : -1, - cert_subject)) { - error_setg(errp, "Could not set up display for migration"); - return; - } - return; - } - - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'"); -} - -static void hmp_logfile(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - if (!qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err)) { - error_report_err(err); - } -} - -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; - } else { - mask = qemu_str_to_log_mask(items); - if (!mask) { - help_cmd(mon, "log"); - return; - } - } - - if (!qemu_set_log(mask, &err)) { - error_report_err(err); - } -} - -static void hmp_singlestep(Monitor *mon, const QDict *qdict) -{ - const char *option = qdict_get_try_str(qdict, "option"); - if (!option || !strcmp(option, "on")) { - singlestep = 1; - } else if (!strcmp(option, "off")) { - singlestep = 0; - } else { - monitor_printf(mon, "unexpected option %s\n", option); - } -} - -static void hmp_gdbserver(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_try_str(qdict, "device"); - if (!device) { - device = "tcp::" DEFAULT_GDBSTUB_PORT; - } - - if (gdbserver_start(device) < 0) { - monitor_printf(mon, "Could not open gdbserver on device '%s'\n", - device); - } else if (strcmp(device, "none") == 0) { - monitor_printf(mon, "Disabled gdbserver\n"); - } else { - monitor_printf(mon, "Waiting for gdb connection on device '%s'\n", - device); - } -} - -static void hmp_watchdog_action(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - WatchdogAction action; - char *qapi_value; - - qapi_value = g_ascii_strdown(qdict_get_str(qdict, "action"), -1); - action = qapi_enum_parse(&WatchdogAction_lookup, qapi_value, -1, &err); - g_free(qapi_value); - if (err) { - hmp_handle_error(mon, err); - return; - } - qmp_watchdog_set_action(action, &error_abort); -} - -static void monitor_printc(Monitor *mon, int c) -{ - monitor_printf(mon, "'"); - switch(c) { - case '\'': - monitor_printf(mon, "\\'"); - break; - case '\\': - monitor_printf(mon, "\\\\"); - break; - case '\n': - monitor_printf(mon, "\\n"); - break; - case '\r': - monitor_printf(mon, "\\r"); - break; - default: - if (c >= 32 && c <= 126) { - monitor_printf(mon, "%c", c); - } else { - monitor_printf(mon, "\\x%02x", c); - } - break; - } - monitor_printf(mon, "'"); -} - -static void memory_dump(Monitor *mon, int count, int format, int wsize, - hwaddr addr, int is_physical) -{ - int l, line_size, i, max_digits, len; - uint8_t buf[16]; - uint64_t v; - CPUState *cs = mon_get_cpu(mon); - - if (!cs && (format == 'i' || !is_physical)) { - monitor_printf(mon, "Can not dump without CPU\n"); - return; - } - - if (format == 'i') { - monitor_disas(mon, cs, addr, count, is_physical); - return; - } - - len = wsize * count; - if (wsize == 1) { - line_size = 8; - } else { - line_size = 16; - } - max_digits = 0; - - switch(format) { - case 'o': - max_digits = DIV_ROUND_UP(wsize * 8, 3); - break; - default: - case 'x': - max_digits = (wsize * 8) / 4; - break; - case 'u': - case 'd': - max_digits = DIV_ROUND_UP(wsize * 8 * 10, 33); - break; - case 'c': - wsize = 1; - break; - } - - while (len > 0) { - if (is_physical) { - monitor_printf(mon, HWADDR_FMT_plx ":", addr); - } else { - monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr); - } - l = len; - if (l > line_size) - l = line_size; - if (is_physical) { - AddressSpace *as = cs ? cs->as : &address_space_memory; - MemTxResult r = address_space_read(as, addr, - MEMTXATTRS_UNSPECIFIED, buf, l); - if (r != MEMTX_OK) { - monitor_printf(mon, " Cannot access memory\n"); - break; - } - } else { - if (cpu_memory_rw_debug(cs, addr, buf, l, 0) < 0) { - monitor_printf(mon, " Cannot access memory\n"); - break; - } - } - i = 0; - while (i < l) { - switch(wsize) { - default: - case 1: - v = ldub_p(buf + i); - break; - case 2: - v = lduw_p(buf + i); - break; - case 4: - v = (uint32_t)ldl_p(buf + i); - break; - case 8: - v = ldq_p(buf + i); - break; - } - monitor_printf(mon, " "); - switch(format) { - case 'o': - monitor_printf(mon, "%#*" PRIo64, max_digits, v); - break; - case 'x': - monitor_printf(mon, "0x%0*" PRIx64, max_digits, v); - break; - case 'u': - monitor_printf(mon, "%*" PRIu64, max_digits, v); - break; - case 'd': - monitor_printf(mon, "%*" PRId64, max_digits, v); - break; - case 'c': - monitor_printc(mon, v); - break; - } - i += wsize; - } - monitor_printf(mon, "\n"); - addr += l; - len -= l; - } -} - -static void hmp_memory_dump(Monitor *mon, const QDict *qdict) -{ - int count = qdict_get_int(qdict, "count"); - int format = qdict_get_int(qdict, "format"); - int size = qdict_get_int(qdict, "size"); - target_long addr = qdict_get_int(qdict, "addr"); - - memory_dump(mon, count, format, size, addr, 0); -} - -static void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict) -{ - int count = qdict_get_int(qdict, "count"); - int format = qdict_get_int(qdict, "format"); - int size = qdict_get_int(qdict, "size"); - hwaddr addr = qdict_get_int(qdict, "addr"); - - memory_dump(mon, count, format, size, addr, 1); -} - -void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp) -{ - Int128 gpa_region_size; - MemoryRegionSection mrs = memory_region_find(get_system_memory(), - addr, size); - - if (!mrs.mr) { - error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr); - return NULL; - } - - if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) { - error_setg(errp, "Memory at address 0x%" HWADDR_PRIx "is not RAM", addr); - memory_region_unref(mrs.mr); - return NULL; - } - - gpa_region_size = int128_make64(size); - if (int128_lt(mrs.size, gpa_region_size)) { - error_setg(errp, "Size of memory region at 0x%" HWADDR_PRIx - " exceeded.", addr); - memory_region_unref(mrs.mr); - return NULL; - } - - *p_mr = mrs.mr; - return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); -} - -static void hmp_gpa2hva(Monitor *mon, const QDict *qdict) -{ - hwaddr addr = qdict_get_int(qdict, "addr"); - Error *local_err = NULL; - MemoryRegion *mr = NULL; - void *ptr; - - ptr = gpa2hva(&mr, addr, 1, &local_err); - if (local_err) { - error_report_err(local_err); - return; - } - - monitor_printf(mon, "Host virtual address for 0x%" HWADDR_PRIx - " (%s) is %p\n", - addr, mr->name, ptr); - - memory_region_unref(mr); -} - -static void hmp_gva2gpa(Monitor *mon, const QDict *qdict) -{ - target_ulong addr = qdict_get_int(qdict, "addr"); - MemTxAttrs attrs; - CPUState *cs = mon_get_cpu(mon); - hwaddr gpa; - - if (!cs) { - monitor_printf(mon, "No cpu\n"); - return; - } - - gpa = cpu_get_phys_page_attrs_debug(cs, addr & TARGET_PAGE_MASK, &attrs); - if (gpa == -1) { - monitor_printf(mon, "Unmapped\n"); - } else { - monitor_printf(mon, "gpa: %#" HWADDR_PRIx "\n", - gpa + (addr & ~TARGET_PAGE_MASK)); - } -} - -#ifdef CONFIG_LINUX -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(); - off_t offset = addr / pagesize * sizeof(pinfo); - int fd; - - fd = open("/proc/self/pagemap", O_RDONLY); - if (fd == -1) { - error_setg_errno(errp, errno, "Cannot open /proc/self/pagemap"); - return -1; - } - - /* Force copy-on-write if necessary. */ - qatomic_add((uint8_t *)ptr, 0); - - if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) { - error_setg_errno(errp, errno, "Cannot read pagemap"); - goto out; - } - if ((pinfo & (1ull << 63)) == 0) { - error_setg(errp, "Page not present"); - goto out; - } - ret = ((pinfo & 0x007fffffffffffffull) * pagesize) | (addr & (pagesize - 1)); - -out: - close(fd); - return ret; -} - -static void hmp_gpa2hpa(Monitor *mon, const QDict *qdict) -{ - hwaddr addr = qdict_get_int(qdict, "addr"); - Error *local_err = NULL; - MemoryRegion *mr = NULL; - void *ptr; - uint64_t physaddr; - - ptr = gpa2hva(&mr, addr, 1, &local_err); - if (local_err) { - error_report_err(local_err); - return; - } - - physaddr = vtop(ptr, &local_err); - if (local_err) { - error_report_err(local_err); - } else { - monitor_printf(mon, "Host physical address for 0x%" HWADDR_PRIx - " (%s) is 0x%" PRIx64 "\n", - addr, mr->name, (uint64_t) physaddr); - } - - memory_region_unref(mr); -} -#endif - -static void do_print(Monitor *mon, const QDict *qdict) -{ - int format = qdict_get_int(qdict, "format"); - hwaddr val = qdict_get_int(qdict, "val"); - - switch(format) { - case 'o': - monitor_printf(mon, "%#" HWADDR_PRIo, val); - break; - case 'x': - monitor_printf(mon, "%#" HWADDR_PRIx, val); - break; - case 'u': - monitor_printf(mon, "%" HWADDR_PRIu, val); - break; - default: - case 'd': - monitor_printf(mon, "%" HWADDR_PRId, val); - break; - case 'c': - monitor_printc(mon, val); - break; - } - monitor_printf(mon, "\n"); -} - -static void hmp_sum(Monitor *mon, const QDict *qdict) -{ - uint32_t addr; - uint16_t sum; - uint32_t start = qdict_get_int(qdict, "start"); - uint32_t size = qdict_get_int(qdict, "size"); - - sum = 0; - for(addr = start; addr < (start + size); addr++) { - uint8_t val = address_space_ldub(&address_space_memory, addr, - MEMTXATTRS_UNSPECIFIED, NULL); - /* BSD sum algorithm ('sum' Unix command) */ - sum = (sum >> 1) | (sum << 15); - sum += val; - } - monitor_printf(mon, "%05d\n", sum); -} - -static void hmp_ioport_read(Monitor *mon, const QDict *qdict) -{ - int size = qdict_get_int(qdict, "size"); - int addr = qdict_get_int(qdict, "addr"); - int has_index = qdict_haskey(qdict, "index"); - uint32_t val; - int suffix; - - if (has_index) { - int index = qdict_get_int(qdict, "index"); - cpu_outb(addr & IOPORTS_MASK, index & 0xff); - addr++; - } - addr &= 0xffff; - - switch(size) { - default: - case 1: - val = cpu_inb(addr); - suffix = 'b'; - break; - case 2: - val = cpu_inw(addr); - suffix = 'w'; - break; - case 4: - val = cpu_inl(addr); - suffix = 'l'; - break; - } - monitor_printf(mon, "port%c[0x%04x] = 0x%0*x\n", - suffix, addr, size * 2, val); -} - -static void hmp_ioport_write(Monitor *mon, const QDict *qdict) -{ - int size = qdict_get_int(qdict, "size"); - int addr = qdict_get_int(qdict, "addr"); - int val = qdict_get_int(qdict, "val"); - - addr &= IOPORTS_MASK; - - switch (size) { - default: - case 1: - cpu_outb(addr, val); - break; - case 2: - cpu_outw(addr, val); - break; - case 4: - cpu_outl(addr, val); - break; - } -} - -static void hmp_boot_set(Monitor *mon, const QDict *qdict) -{ - Error *local_err = NULL; - const char *bootdevice = qdict_get_str(qdict, "bootdevice"); - - qemu_boot_set(bootdevice, &local_err); - if (local_err) { - error_report_err(local_err); - } else { - monitor_printf(mon, "boot device list now set to %s\n", bootdevice); - } -} - -static void hmp_info_mtree(Monitor *mon, const QDict *qdict) -{ - bool flatview = qdict_get_try_bool(qdict, "flatview", false); - bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false); - bool owner = qdict_get_try_bool(qdict, "owner", false); - bool disabled = qdict_get_try_bool(qdict, "disabled", false); - - mtree_info(flatview, dispatch_tree, owner, disabled); -} - -/* Capture support */ -static QLIST_HEAD (capture_list_head, CaptureState) capture_head; - -static void hmp_info_capture(Monitor *mon, const QDict *qdict) -{ - int i; - CaptureState *s; - - for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { - monitor_printf(mon, "[%d]: ", i); - s->ops.info (s->opaque); - } -} - -static void hmp_stopcapture(Monitor *mon, const QDict *qdict) -{ - int i; - int n = qdict_get_int(qdict, "n"); - CaptureState *s; - - for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { - if (i == n) { - s->ops.destroy (s->opaque); - QLIST_REMOVE (s, entries); - g_free (s); - return; - } - } -} - -static void hmp_wavcapture(Monitor *mon, const QDict *qdict) -{ - const char *path = qdict_get_str(qdict, "path"); - int freq = qdict_get_try_int(qdict, "freq", 44100); - int bits = qdict_get_try_int(qdict, "bits", 16); - int nchannels = qdict_get_try_int(qdict, "nchannels", 2); - const char *audiodev = qdict_get_str(qdict, "audiodev"); - CaptureState *s; - AudioState *as = audio_state_by_name(audiodev); - - if (!as) { - monitor_printf(mon, "Audiodev '%s' not found\n", audiodev); - return; - } - - s = g_malloc0 (sizeof (*s)); - - if (wav_start_capture(as, s, path, freq, bits, nchannels)) { - monitor_printf(mon, "Failed to add wave capture\n"); - g_free (s); - return; - } - QLIST_INSERT_HEAD (&capture_head, s, entries); -} - -void qmp_getfd(const char *fdname, Error **errp) -{ - Monitor *cur_mon = monitor_cur(); - mon_fd_t *monfd; - int fd, tmp_fd; - - fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); - if (fd == -1) { - error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); - return; - } - - if (qemu_isdigit(fdname[0])) { - close(fd); - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", - "a name not starting with a digit"); - return; - } - - QEMU_LOCK_GUARD(&cur_mon->mon_lock); - QLIST_FOREACH(monfd, &cur_mon->fds, next) { - if (strcmp(monfd->name, fdname) != 0) { - continue; - } - - tmp_fd = monfd->fd; - monfd->fd = fd; - /* Make sure close() is outside critical section */ - close(tmp_fd); - return; - } - - monfd = g_new0(mon_fd_t, 1); - monfd->name = g_strdup(fdname); - monfd->fd = fd; - - QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); -} - -void qmp_closefd(const char *fdname, Error **errp) -{ - Monitor *cur_mon = monitor_cur(); - mon_fd_t *monfd; - int tmp_fd; - - qemu_mutex_lock(&cur_mon->mon_lock); - QLIST_FOREACH(monfd, &cur_mon->fds, next) { - if (strcmp(monfd->name, fdname) != 0) { - continue; - } - - QLIST_REMOVE(monfd, next); - tmp_fd = monfd->fd; - g_free(monfd->name); - g_free(monfd); - qemu_mutex_unlock(&cur_mon->mon_lock); - /* Make sure close() is outside critical section */ - close(tmp_fd); - return; - } - - qemu_mutex_unlock(&cur_mon->mon_lock); - error_setg(errp, "File descriptor named '%s' not found", fdname); -} - -int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) -{ - mon_fd_t *monfd; - - QEMU_LOCK_GUARD(&mon->mon_lock); - QLIST_FOREACH(monfd, &mon->fds, next) { - int fd; - - if (strcmp(monfd->name, fdname) != 0) { - continue; - } - - fd = monfd->fd; - assert(fd >= 0); - - /* caller takes ownership of fd */ - QLIST_REMOVE(monfd, next); - g_free(monfd->name); - g_free(monfd); - - return fd; - } - - error_setg(errp, "File descriptor named '%s' has not been found", fdname); - return -1; -} - -static void monitor_fdset_cleanup(MonFdset *mon_fdset) -{ - MonFdsetFd *mon_fdset_fd; - MonFdsetFd *mon_fdset_fd_next; - - QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) { - if ((mon_fdset_fd->removed || - (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) && - runstate_is_running()) { - close(mon_fdset_fd->fd); - g_free(mon_fdset_fd->opaque); - QLIST_REMOVE(mon_fdset_fd, next); - g_free(mon_fdset_fd); - } - } - - if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { - QLIST_REMOVE(mon_fdset, next); - g_free(mon_fdset); - } -} - -void monitor_fdsets_cleanup(void) -{ - MonFdset *mon_fdset; - MonFdset *mon_fdset_next; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { - monitor_fdset_cleanup(mon_fdset); - } -} - -AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, - const char *opaque, Error **errp) -{ - int fd; - Monitor *mon = monitor_cur(); - AddfdInfo *fdinfo; - - fd = qemu_chr_fe_get_msgfd(&mon->chr); - if (fd == -1) { - error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); - goto error; - } - - fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, opaque, errp); - if (fdinfo) { - return fdinfo; - } - -error: - if (fd != -1) { - close(fd); - } - return NULL; -} - -void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) -{ - MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd; - char fd_str[60]; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - if (mon_fdset->id != fdset_id) { - continue; - } - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { - if (has_fd) { - if (mon_fdset_fd->fd != fd) { - continue; - } - mon_fdset_fd->removed = true; - break; - } else { - mon_fdset_fd->removed = true; - } - } - if (has_fd && !mon_fdset_fd) { - goto error; - } - monitor_fdset_cleanup(mon_fdset); - return; - } - -error: - if (has_fd) { - snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64, - fdset_id, fd); - } else { - snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id); - } - error_setg(errp, "File descriptor named '%s' not found", fd_str); -} - -FdsetInfoList *qmp_query_fdsets(Error **errp) -{ - MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd; - FdsetInfoList *fdset_list = NULL; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info)); - - fdset_info->fdset_id = mon_fdset->id; - - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { - FdsetFdInfo *fdsetfd_info; - - fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info)); - fdsetfd_info->fd = mon_fdset_fd->fd; - fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque); - - QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info); - } - - QAPI_LIST_PREPEND(fdset_list, fdset_info); - } - - return fdset_list; -} - -AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, - const char *opaque, Error **errp) -{ - MonFdset *mon_fdset = NULL; - MonFdsetFd *mon_fdset_fd; - AddfdInfo *fdinfo; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - if (has_fdset_id) { - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - /* Break if match found or match impossible due to ordering by ID */ - if (fdset_id <= mon_fdset->id) { - if (fdset_id < mon_fdset->id) { - mon_fdset = NULL; - } - break; - } - } - } - - if (mon_fdset == NULL) { - int64_t fdset_id_prev = -1; - MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets); - - if (has_fdset_id) { - if (fdset_id < 0) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id", - "a non-negative value"); - return NULL; - } - /* Use specified fdset ID */ - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - mon_fdset_cur = mon_fdset; - if (fdset_id < mon_fdset_cur->id) { - break; - } - } - } else { - /* Use first available fdset ID */ - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - mon_fdset_cur = mon_fdset; - if (fdset_id_prev == mon_fdset_cur->id - 1) { - fdset_id_prev = mon_fdset_cur->id; - continue; - } - break; - } - } - - mon_fdset = g_malloc0(sizeof(*mon_fdset)); - if (has_fdset_id) { - mon_fdset->id = fdset_id; - } else { - mon_fdset->id = fdset_id_prev + 1; - } - - /* The fdset list is ordered by fdset ID */ - if (!mon_fdset_cur) { - QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next); - } else if (mon_fdset->id < mon_fdset_cur->id) { - QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next); - } else { - QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next); - } - } - - mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd)); - mon_fdset_fd->fd = fd; - mon_fdset_fd->removed = false; - mon_fdset_fd->opaque = g_strdup(opaque); - QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next); - - fdinfo = g_malloc0(sizeof(*fdinfo)); - fdinfo->fdset_id = mon_fdset->id; - fdinfo->fd = mon_fdset_fd->fd; - - return fdinfo; -} - -int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) -{ -#ifdef _WIN32 - return -ENOENT; -#else - MonFdset *mon_fdset; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - MonFdsetFd *mon_fdset_fd; - MonFdsetFd *mon_fdset_fd_dup; - int fd = -1; - int dup_fd; - int mon_fd_flags; - - if (mon_fdset->id != fdset_id) { - continue; - } - - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { - mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); - if (mon_fd_flags == -1) { - return -1; - } - - if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { - fd = mon_fdset_fd->fd; - break; - } - } - - if (fd == -1) { - errno = EACCES; - return -1; - } - - dup_fd = qemu_dup_flags(fd, flags); - if (dup_fd == -1) { - return -1; - } - - mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup)); - mon_fdset_fd_dup->fd = dup_fd; - QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next); - return dup_fd; - } - - errno = ENOENT; - return -1; -#endif -} - -static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) -{ - MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd_dup; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { - if (mon_fdset_fd_dup->fd == dup_fd) { - if (remove) { - QLIST_REMOVE(mon_fdset_fd_dup, next); - g_free(mon_fdset_fd_dup); - if (QLIST_EMPTY(&mon_fdset->dup_fds)) { - monitor_fdset_cleanup(mon_fdset); - } - return -1; - } else { - return mon_fdset->id; - } - } - } - } - - return -1; -} - -int64_t monitor_fdset_dup_fd_find(int dup_fd) -{ - return monitor_fdset_dup_fd_find_remove(dup_fd, false); -} - -void monitor_fdset_dup_fd_remove(int dup_fd) -{ - monitor_fdset_dup_fd_find_remove(dup_fd, true); -} - -int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) -{ - int fd; - - if (!qemu_isdigit(fdname[0]) && mon) { - fd = monitor_get_fd(mon, fdname, errp); - } else { - fd = qemu_parse_fd(fdname); - if (fd < 0) { - error_setg(errp, "Invalid file descriptor number '%s'", - fdname); - } - } - - return fd; -} - -/* Please update hmp-commands.hx when adding or changing commands */ -static HMPCommand hmp_info_cmds[] = { -#include "hmp-commands-info.h" - { NULL, NULL, }, -}; - -/* hmp_cmds and hmp_info_cmds would be sorted at runtime */ -HMPCommand hmp_cmds[] = { -#include "hmp-commands.h" - { NULL, NULL, }, -}; - -/* - * Set @pval to the value in the register identified by @name. - * return 0 if OK, -1 if not found - */ -int get_monitor_def(Monitor *mon, int64_t *pval, const char *name) -{ - const MonitorDef *md = target_monitor_defs(); - CPUState *cs = mon_get_cpu(mon); - void *ptr; - uint64_t tmp = 0; - int ret; - - if (cs == NULL || md == NULL) { - return -1; - } - - for(; md->name != NULL; md++) { - if (hmp_compare_cmd(name, md->name)) { - if (md->get_value) { - *pval = md->get_value(mon, md, md->offset); - } else { - CPUArchState *env = mon_get_cpu_env(mon); - ptr = (uint8_t *)env + md->offset; - switch(md->type) { - case MD_I32: - *pval = *(int32_t *)ptr; - break; - case MD_TLONG: - *pval = *(target_long *)ptr; - break; - default: - *pval = 0; - break; - } - } - return 0; - } - } - - ret = target_get_monitor_def(cs, name, &tmp); - if (!ret) { - *pval = (target_long) tmp; - } - - return ret; -} - -static void add_completion_option(ReadLineState *rs, const char *str, - const char *option) -{ - if (!str || !option) { - return; - } - if (!strncmp(option, str, strlen(str))) { - readline_add_completion(rs, option); - } -} - -void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - ChardevBackendInfoList *list, *start; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_query_chardev_backends(NULL); - while (list) { - const char *chr_name = list->value->name; - - if (!strncmp(chr_name, str, len)) { - readline_add_completion(rs, chr_name); - } - list = list->next; - } - qapi_free_ChardevBackendInfoList(start); -} - -void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - int i; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - for (i = 0; i < NET_CLIENT_DRIVER__MAX; i++) { - add_completion_option(rs, str, NetClientDriver_str(i)); - } -} - -void device_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - GSList *list, *elt; - size_t len; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - list = elt = object_class_get_list(TYPE_DEVICE, false); - while (elt) { - const char *name; - DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data, - TYPE_DEVICE); - name = object_class_get_name(OBJECT_CLASS(dc)); - - if (dc->user_creatable - && !strncmp(name, str, len)) { - readline_add_completion(rs, name); - } - elt = elt->next; - } - g_slist_free(list); -} - -void object_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - GSList *list, *elt; - size_t len; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - list = elt = object_class_get_list(TYPE_USER_CREATABLE, false); - while (elt) { - const char *name; - - name = object_class_get_name(OBJECT_CLASS(elt->data)); - if (!strncmp(name, str, len) && strcmp(name, TYPE_USER_CREATABLE)) { - readline_add_completion(rs, name); - } - elt = elt->next; - } - g_slist_free(list); -} - -static int qdev_add_hotpluggable_device(Object *obj, void *opaque) -{ - GSList **list = opaque; - DeviceState *dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE); - - if (dev == NULL) { - return 0; - } - - if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { - *list = g_slist_append(*list, dev); - } - - return 0; -} - -static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) -{ - GSList *list = NULL; - - object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); - - return list; -} - -static void peripheral_device_del_completion(ReadLineState *rs, - const char *str, size_t len) -{ - Object *peripheral = container_get(qdev_get_machine(), "/peripheral"); - GSList *list, *item; - - list = qdev_build_hotpluggable_device_list(peripheral); - if (!list) { - return; - } - - for (item = list; item; item = g_slist_next(item)) { - DeviceState *dev = item->data; - - if (dev->id && !strncmp(str, dev->id, len)) { - readline_add_completion(rs, dev->id); - } - } - - g_slist_free(list); -} - -void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - ChardevInfoList *list, *start; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_query_chardev(NULL); - while (list) { - ChardevInfo *chr = list->value; - - if (!strncmp(chr->label, str, len)) { - readline_add_completion(rs, chr->label); - } - list = list->next; - } - qapi_free_ChardevInfoList(start); -} - -static void ringbuf_completion(ReadLineState *rs, const char *str) -{ - size_t len; - ChardevInfoList *list, *start; - - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_query_chardev(NULL); - while (list) { - ChardevInfo *chr_info = list->value; - - if (!strncmp(chr_info->label, str, len)) { - Chardev *chr = qemu_chr_find(chr_info->label); - if (chr && CHARDEV_IS_RINGBUF(chr)) { - readline_add_completion(rs, chr_info->label); - } - } - list = list->next; - } - qapi_free_ChardevInfoList(start); -} - -void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str) -{ - if (nb_args != 2) { - return; - } - ringbuf_completion(rs, str); -} - -void device_del_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - peripheral_device_del_completion(rs, str, len); -} - -void object_del_completion(ReadLineState *rs, int nb_args, const char *str) -{ - ObjectPropertyInfoList *list, *start; - size_t len; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_qom_list("/objects", NULL); - while (list) { - ObjectPropertyInfo *info = list->value; - - if (!strncmp(info->type, "child<", 5) - && !strncmp(info->name, str, len)) { - readline_add_completion(rs, info->name); - } - list = list->next; - } - qapi_free_ObjectPropertyInfoList(start); -} - -void set_link_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - NetClientState *ncs[MAX_QUEUE_NUM]; - int count, i; - count = qemu_find_net_clients_except(NULL, ncs, - NET_CLIENT_DRIVER_NONE, - MAX_QUEUE_NUM); - for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { - const char *name = ncs[i]->name; - if (!strncmp(str, name, len)) { - readline_add_completion(rs, name); - } - } - } else if (nb_args == 3) { - add_completion_option(rs, str, "on"); - add_completion_option(rs, str, "off"); - } -} - -void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) -{ - int len, count, i; - NetClientState *ncs[MAX_QUEUE_NUM]; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, - MAX_QUEUE_NUM); - for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { - const char *name = ncs[i]->name; - if (strncmp(str, name, len)) { - continue; - } - if (ncs[i]->is_netdev) { - readline_add_completion(rs, name); - } - } -} - -void info_trace_events_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - TraceEventIter iter; - TraceEvent *ev; - char *pattern = g_strdup_printf("%s*", str); - trace_event_iter_init_pattern(&iter, pattern); - while ((ev = trace_event_iter_next(&iter)) != NULL) { - readline_add_completion(rs, trace_event_get_name(ev)); - } - g_free(pattern); - } -} - -void trace_event_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - TraceEventIter iter; - TraceEvent *ev; - char *pattern = g_strdup_printf("%s*", str); - trace_event_iter_init_pattern(&iter, pattern); - while ((ev = trace_event_iter_next(&iter)) != NULL) { - readline_add_completion(rs, trace_event_get_name(ev)); - } - g_free(pattern); - } else if (nb_args == 3) { - add_completion_option(rs, str, "on"); - add_completion_option(rs, str, "off"); - } -} - -void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str) -{ - int i; - - if (nb_args != 2) { - return; - } - readline_set_completion_index(rs, strlen(str)); - for (i = 0; i < WATCHDOG_ACTION__MAX; i++) { - add_completion_option(rs, str, WatchdogAction_str(i)); - } -} - -void migrate_set_capability_completion(ReadLineState *rs, int nb_args, - const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - int i; - for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { - const char *name = MigrationCapability_str(i); - if (!strncmp(str, name, len)) { - readline_add_completion(rs, name); - } - } - } else if (nb_args == 3) { - add_completion_option(rs, str, "on"); - add_completion_option(rs, str, "off"); - } -} - -void migrate_set_parameter_completion(ReadLineState *rs, int nb_args, - const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - int i; - for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { - const char *name = MigrationParameter_str(i); - if (!strncmp(str, name, len)) { - readline_add_completion(rs, name); - } - } - } -} - -static void vm_completion(ReadLineState *rs, const char *str) -{ - size_t len; - BlockDriverState *bs; - BdrvNextIterator it; - - len = strlen(str); - readline_set_completion_index(rs, len); - - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - SnapshotInfoList *snapshots, *snapshot; - AioContext *ctx = bdrv_get_aio_context(bs); - bool ok = false; - - aio_context_acquire(ctx); - if (bdrv_can_snapshot(bs)) { - ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0; - } - aio_context_release(ctx); - if (!ok) { - continue; - } - - snapshot = snapshots; - while (snapshot) { - char *completion = snapshot->value->name; - if (!strncmp(str, completion, len)) { - readline_add_completion(rs, completion); - } - completion = snapshot->value->id; - if (!strncmp(str, completion, len)) { - readline_add_completion(rs, completion); - } - snapshot = snapshot->next; - } - qapi_free_SnapshotInfoList(snapshots); - } - -} - -void delvm_completion(ReadLineState *rs, int nb_args, const char *str) -{ - if (nb_args == 2) { - vm_completion(rs, str); - } -} - -void loadvm_completion(ReadLineState *rs, int nb_args, const char *str) -{ - if (nb_args == 2) { - vm_completion(rs, str); - } -} - -static int -compare_mon_cmd(const void *a, const void *b) -{ - return strcmp(((const HMPCommand *)a)->name, - ((const HMPCommand *)b)->name); -} - -static void sortcmdlist(void) -{ - qsort(hmp_cmds, ARRAY_SIZE(hmp_cmds) - 1, - sizeof(*hmp_cmds), - compare_mon_cmd); - qsort(hmp_info_cmds, ARRAY_SIZE(hmp_info_cmds) - 1, - sizeof(*hmp_info_cmds), - compare_mon_cmd); -} - -void monitor_register_hmp(const char *name, bool info, - void (*cmd)(Monitor *mon, const QDict *qdict)) -{ - HMPCommand *table = info ? hmp_info_cmds : hmp_cmds; - - while (table->name != NULL) { - if (strcmp(table->name, name) == 0) { - g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); - table->cmd = cmd; - return; - } - table++; - } - g_assert_not_reached(); -} - -void monitor_register_hmp_info_hrt(const char *name, - HumanReadableText *(*handler)(Error **errp)) -{ - HMPCommand *table = hmp_info_cmds; - - while (table->name != NULL) { - if (strcmp(table->name, name) == 0) { - g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); - table->cmd_info_hrt = handler; - return; - } - table++; - } - g_assert_not_reached(); -} - -void monitor_init_globals(void) -{ - monitor_init_globals_core(); - monitor_init_qmp_commands(); - sortcmdlist(); - qemu_mutex_init(&mon_fdsets_lock); -} diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index a2cdbbf646..53e3808054 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -186,7 +186,6 @@ void monitor_data_destroy_qmp(MonitorQMP *mon); void coroutine_fn monitor_qmp_dispatcher_co(void *data); int get_monitor_def(Monitor *mon, int64_t *pval, const char *name); -void help_cmd(Monitor *mon, const char *name); void handle_hmp_command(MonitorHMP *mon, const char *cmdline); int hmp_compare_cmd(const char *name, const char *list); diff --git a/monitor/monitor.c b/monitor/monitor.c index 7ed7bd5342..8dc96f6af9 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -260,6 +260,33 @@ int monitor_printf(Monitor *mon, const char *fmt, ...) return ret; } +void monitor_printc(Monitor *mon, int c) +{ + monitor_printf(mon, "'"); + switch(c) { + case '\'': + monitor_printf(mon, "\\'"); + break; + case '\\': + monitor_printf(mon, "\\\\"); + break; + case '\n': + monitor_printf(mon, "\\n"); + break; + case '\r': + monitor_printf(mon, "\\r"); + break; + default: + if (c >= 32 && c <= 126) { + monitor_printf(mon, "%c", c); + } else { + monitor_printf(mon, "\\x%02x", c); + } + break; + } + monitor_printf(mon, "'"); +} + /* * Print to current monitor if we have one, else to stderr. */ @@ -693,7 +720,7 @@ static void monitor_qapi_event_init(void) qapi_event_throttle_equal); } -void monitor_init_globals_core(void) +void monitor_init_globals(void) { monitor_qapi_event_init(); qemu_mutex_init(&monitor_lock); diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c index 6e581713a3..f21506efa5 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -30,7 +30,6 @@ #include "qapi/error.h" #include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-introspect.h" -#include "qapi/qapi-emit-events.h" #include "qapi/qapi-introspect.h" #include "qapi/qapi-visit-introspect.h" #include "qapi/qobject-input-visitor.h" diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index bf22a8c5a6..859012aef4 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -14,34 +14,23 @@ */ #include "qemu/osdep.h" -#include "block/blockjob.h" -#include "qemu/cutils.h" -#include "qemu/option.h" -#include "monitor/monitor.h" +#include "monitor-internal.h" +#include "monitor/qdev.h" #include "monitor/qmp-helpers.h" #include "sysemu/sysemu.h" -#include "qemu/config-file.h" -#include "qemu/uuid.h" -#include "chardev/char.h" #include "sysemu/kvm.h" #include "sysemu/runstate.h" #include "sysemu/runstate-action.h" -#include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "qapi/error.h" -#include "qapi/qapi-commands-acpi.h" -#include "qapi/qapi-commands-block.h" +#include "qapi/qapi-init-commands.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-commands-stats.h" +#include "qapi/qmp/qerror.h" #include "qapi/type-helpers.h" -#include "exec/ramlist.h" #include "hw/mem/memory-device.h" -#include "hw/acpi/acpi_dev_interface.h" #include "hw/intc/intc.h" #include "hw/rdma/rdma.h" -#include "monitor/stats.h" NameInfo *qmp_query_name(Error **errp) { @@ -51,24 +40,6 @@ NameInfo *qmp_query_name(Error **errp) return info; } -KvmInfo *qmp_query_kvm(Error **errp) -{ - KvmInfo *info = g_malloc0(sizeof(*info)); - - info->enabled = kvm_enabled(); - info->present = accel_find("kvm"); - - return info; -} - -UuidInfo *qmp_query_uuid(Error **errp) -{ - UuidInfo *info = g_malloc0(sizeof(*info)); - - info->UUID = qemu_uuid_unparse_strdup(&qemu_uuid); - return info; -} - void qmp_quit(Error **errp) { shutdown_action = SHUTDOWN_ACTION_POWEROFF; @@ -91,16 +62,6 @@ void qmp_stop(Error **errp) } } -void qmp_system_reset(Error **errp) -{ - qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET); -} - -void qmp_system_powerdown(Error **errp) -{ - qemu_system_powerdown_request(); -} - void qmp_cont(Error **errp) { BlockBackend *blk; @@ -154,17 +115,6 @@ void qmp_cont(Error **errp) } } -void qmp_system_wakeup(Error **errp) -{ - if (!qemu_wakeup_suspend_enabled()) { - error_setg(errp, - "wake-up from suspend is not supported by this guest"); - return; - } - - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); -} - void qmp_add_client(const char *protocol, const char *fdname, bool has_skipauth, bool skipauth, bool has_tls, bool tls, Error **errp) @@ -182,7 +132,6 @@ void qmp_add_client(const char *protocol, const char *fdname, { "@dbus-display", qmp_add_client_dbus_display }, #endif }; - Chardev *s; int fd, i; fd = monitor_get_fd(monitor_cur(), fdname, errp); @@ -200,284 +149,56 @@ void qmp_add_client(const char *protocol, const char *fdname, } } - s = qemu_chr_find(protocol); - if (!s) { - error_setg(errp, "protocol '%s' is invalid", protocol); - close(fd); - return; - } - if (qemu_chr_add_client(s, fd) < 0) { - error_setg(errp, "failed to add client"); + if (!qmp_add_client_char(fd, has_skipauth, skipauth, has_tls, tls, + protocol, errp)) { close(fd); - return; - } -} - -MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) -{ - return qmp_memory_device_list(); -} - -ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp) -{ - bool ambig; - ACPIOSTInfoList *head = NULL; - ACPIOSTInfoList **prev = &head; - Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, &ambig); - - if (obj) { - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); - AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj); - - adevc->ospm_status(adev, &prev); - } else { - error_setg(errp, "command is not supported, missing ACPI device"); - } - - return head; -} - -MemoryInfo *qmp_query_memory_size_summary(Error **errp) -{ - MemoryInfo *mem_info = g_new0(MemoryInfo, 1); - MachineState *ms = MACHINE(qdev_get_machine()); - - mem_info->base_memory = ms->ram_size; - - mem_info->plugged_memory = get_plugged_memory_size(); - mem_info->has_plugged_memory = - mem_info->plugged_memory != (uint64_t)-1; - - return mem_info; -} - -static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) -{ - RdmaProvider *rdma; - RdmaProviderClass *k; - GString *buf = opaque; - - if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { - rdma = RDMA_PROVIDER(obj); - k = RDMA_PROVIDER_GET_CLASS(obj); - if (k->format_statistics) { - k->format_statistics(rdma, buf); - } else { - g_string_append_printf(buf, - "RDMA statistics not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -HumanReadableText *qmp_x_query_rdma(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - object_child_foreach_recursive(object_get_root(), - qmp_x_query_rdma_foreach, buf); - - return human_readable_text_from_str(buf); -} - -HumanReadableText *qmp_x_query_ramblock(Error **errp) -{ - g_autoptr(GString) buf = ram_block_format(); - - return human_readable_text_from_str(buf); -} - -static int qmp_x_query_irq_foreach(Object *obj, void *opaque) -{ - InterruptStatsProvider *intc; - InterruptStatsProviderClass *k; - GString *buf = opaque; - - if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { - intc = INTERRUPT_STATS_PROVIDER(obj); - k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); - uint64_t *irq_counts; - unsigned int nb_irqs, i; - if (k->get_statistics && - k->get_statistics(intc, &irq_counts, &nb_irqs)) { - if (nb_irqs > 0) { - g_string_append_printf(buf, "IRQ statistics for %s:\n", - object_get_typename(obj)); - for (i = 0; i < nb_irqs; i++) { - if (irq_counts[i] > 0) { - g_string_append_printf(buf, "%2d: %" PRId64 "\n", i, - irq_counts[i]); - } - } - } - } else { - g_string_append_printf(buf, - "IRQ statistics not available for %s.\n", - object_get_typename(obj)); - } } - - return 0; } -HumanReadableText *qmp_x_query_irq(Error **errp) +char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, + int64_t cpu_index, Error **errp) { - g_autoptr(GString) buf = g_string_new(""); - - object_child_foreach_recursive(object_get_root(), - qmp_x_query_irq_foreach, buf); - - return human_readable_text_from_str(buf); -} - -typedef struct StatsCallbacks { - StatsProvider provider; - StatRetrieveFunc *stats_cb; - SchemaRetrieveFunc *schemas_cb; - QTAILQ_ENTRY(StatsCallbacks) next; -} StatsCallbacks; - -static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks = - QTAILQ_HEAD_INITIALIZER(stats_callbacks); - -void add_stats_callbacks(StatsProvider provider, - StatRetrieveFunc *stats_fn, - SchemaRetrieveFunc *schemas_fn) -{ - StatsCallbacks *entry = g_new(StatsCallbacks, 1); - entry->provider = provider; - entry->stats_cb = stats_fn; - entry->schemas_cb = schemas_fn; - - QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next); -} - -static bool invoke_stats_cb(StatsCallbacks *entry, - StatsResultList **stats_results, - StatsFilter *filter, StatsRequest *request, - Error **errp) -{ - ERRP_GUARD(); - strList *targets = NULL; - strList *names = NULL; - - if (request) { - if (request->provider != entry->provider) { - return true; - } - if (request->has_names && !request->names) { - return true; - } - names = request->has_names ? request->names : NULL; - } - - switch (filter->target) { - case STATS_TARGET_VM: - break; - case STATS_TARGET_VCPU: - if (filter->u.vcpu.has_vcpus) { - if (!filter->u.vcpu.vcpus) { - /* No targets allowed? Return no statistics. */ - return true; - } - targets = filter->u.vcpu.vcpus; - } - break; - default: - abort(); - } - - entry->stats_cb(stats_results, filter->target, names, targets, errp); - if (*errp) { - qapi_free_StatsResultList(*stats_results); - *stats_results = NULL; - return false; - } - return true; -} + char *output = NULL; + MonitorHMP hmp = {}; -StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp) -{ - StatsResultList *stats_results = NULL; - StatsCallbacks *entry; - StatsRequestList *request; + monitor_data_init(&hmp.common, false, true, false); - QTAILQ_FOREACH(entry, &stats_callbacks, next) { - if (filter->has_providers) { - for (request = filter->providers; request; request = request->next) { - if (!invoke_stats_cb(entry, &stats_results, filter, - request->value, errp)) { - break; - } - } - } else { - if (!invoke_stats_cb(entry, &stats_results, filter, NULL, errp)) { - break; - } + if (has_cpu_index) { + int ret = monitor_set_cpu(&hmp.common, cpu_index); + if (ret < 0) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + goto out; } } - return stats_results; -} + handle_hmp_command(&hmp, command_line); -StatsSchemaList *qmp_query_stats_schemas(bool has_provider, - StatsProvider provider, - Error **errp) -{ - ERRP_GUARD(); - StatsSchemaList *stats_results = NULL; - StatsCallbacks *entry; - - QTAILQ_FOREACH(entry, &stats_callbacks, next) { - if (!has_provider || provider == entry->provider) { - entry->schemas_cb(&stats_results, errp); - if (*errp) { - qapi_free_StatsSchemaList(stats_results); - return NULL; - } - } + WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { + output = g_strdup(hmp.common.outbuf->str); } - return stats_results; +out: + monitor_data_destroy(&hmp.common); + return output; } -void add_stats_entry(StatsResultList **stats_results, StatsProvider provider, - const char *qom_path, StatsList *stats_list) +static void __attribute__((__constructor__)) monitor_init_qmp_commands(void) { - StatsResult *entry = g_new0(StatsResult, 1); + /* + * Two command lists: + * - qmp_commands contains all QMP commands + * - qmp_cap_negotiation_commands contains just + * "qmp_capabilities", to enforce capability negotiation + */ - entry->provider = provider; - entry->qom_path = g_strdup(qom_path); - entry->stats = stats_list; + qmp_init_marshal(&qmp_commands); - QAPI_LIST_PREPEND(*stats_results, entry); -} + qmp_register_command(&qmp_commands, "device_add", + qmp_device_add, 0, 0); -void add_stats_schema(StatsSchemaList **schema_results, - StatsProvider provider, StatsTarget target, - StatsSchemaValueList *stats_list) -{ - StatsSchema *entry = g_new0(StatsSchema, 1); - - entry->provider = provider; - entry->target = target; - entry->stats = stats_list; - QAPI_LIST_PREPEND(*schema_results, entry); -} - -bool apply_str_list_filter(const char *string, strList *list) -{ - strList *str_list = NULL; - - if (!list) { - return true; - } - for (str_list = list; str_list; str_list = str_list->next) { - if (g_str_equal(string, str_list->value)) { - return true; - } - } - return false; + QTAILQ_INIT(&qmp_cap_negotiation_commands); + qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", + qmp_marshal_qmp_capabilities, + QCO_ALLOW_PRECONFIG, 0); } diff --git a/net/meson.build b/net/meson.build index 6cd1e3dab3..87afca3e93 100644 --- a/net/meson.build +++ b/net/meson.build @@ -10,6 +10,7 @@ softmmu_ss.add(files( 'filter-rewriter.c', 'filter.c', 'hub.c', + 'net-hmp-cmds.c', 'net.c', 'queue.c', 'socket.c', diff --git a/net/net-hmp-cmds.c b/net/net-hmp-cmds.c new file mode 100644 index 0000000000..41d326bf5f --- /dev/null +++ b/net/net-hmp-cmds.c @@ -0,0 +1,170 @@ +/* + * Human Monitor Interface commands + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "migration/misc.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "net/net.h" +#include "net/hub.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-commands-net.h" +#include "qapi/qapi-visit-net.h" +#include "qapi/qmp/qdict.h" +#include "qemu/config-file.h" +#include "qemu/help_option.h" +#include "qemu/option.h" + +void hmp_info_network(Monitor *mon, const QDict *qdict) +{ + NetClientState *nc, *peer; + NetClientDriver type; + + net_hub_info(mon); + + QTAILQ_FOREACH(nc, &net_clients, next) { + peer = nc->peer; + type = nc->info->type; + + /* Skip if already printed in hub info */ + if (net_hub_id_for_client(nc, NULL) == 0) { + continue; + } + + if (!peer || type == NET_CLIENT_DRIVER_NIC) { + print_net_client(mon, nc); + } /* else it's a netdev connected to a NIC, printed with the NIC */ + if (peer && type == NET_CLIENT_DRIVER_NIC) { + monitor_printf(mon, " \\ "); + print_net_client(mon, peer); + } + } +} + +void hmp_set_link(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + bool up = qdict_get_bool(qdict, "up"); + Error *err = NULL; + + qmp_set_link(name, up, &err); + hmp_handle_error(mon, err); +} + + +void hmp_announce_self(Monitor *mon, const QDict *qdict) +{ + const char *interfaces_str = qdict_get_try_str(qdict, "interfaces"); + const char *id = qdict_get_try_str(qdict, "id"); + AnnounceParameters *params = QAPI_CLONE(AnnounceParameters, + migrate_announce_params()); + + qapi_free_strList(params->interfaces); + params->interfaces = hmp_split_at_comma(interfaces_str); + params->has_interfaces = params->interfaces != NULL; + params->id = g_strdup(id); + qmp_announce_self(params, NULL); + qapi_free_AnnounceParameters(params); +} + +void hmp_netdev_add(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + QemuOpts *opts; + const char *type = qdict_get_try_str(qdict, "type"); + + if (type && is_help_option(type)) { + show_netdevs(); + return; + } + opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err); + if (err) { + goto out; + } + + netdev_add(opts, &err); + if (err) { + qemu_opts_del(opts); + } + +out: + hmp_handle_error(mon, err); +} + +void hmp_netdev_del(Monitor *mon, const QDict *qdict) +{ + const char *id = qdict_get_str(qdict, "id"); + Error *err = NULL; + + qmp_netdev_del(id, &err); + hmp_handle_error(mon, err); +} + + +void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + int i; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + for (i = 0; i < NET_CLIENT_DRIVER__MAX; i++) { + readline_add_completion_of(rs, str, NetClientDriver_str(i)); + } +} + +void set_link_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + NetClientState *ncs[MAX_QUEUE_NUM]; + int count, i; + count = qemu_find_net_clients_except(NULL, ncs, + NET_CLIENT_DRIVER_NONE, + MAX_QUEUE_NUM); + for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { + readline_add_completion_of(rs, str, ncs[i]->name); + } + } else if (nb_args == 3) { + readline_add_completion_of(rs, str, "on"); + readline_add_completion_of(rs, str, "off"); + } +} + +void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) +{ + int len, count, i; + NetClientState *ncs[MAX_QUEUE_NUM]; + + if (nb_args != 2) { + return; + } + + len = strlen(str); + readline_set_completion_index(rs, len); + count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, + MAX_QUEUE_NUM); + for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { + if (ncs[i]->is_netdev) { + readline_add_completion_of(rs, str, ncs[i]->name); + } + } +} diff --git a/net/net.c b/net/net.c index 2d01472998..251fc5ab55 100644 --- a/net/net.c +++ b/net/net.c @@ -63,7 +63,7 @@ #endif static VMChangeStateEntry *net_change_state_entry; -static QTAILQ_HEAD(, NetClientState) net_clients; +NetClientStateList net_clients; typedef struct NetdevQueueEntry { Netdev *nd; @@ -1345,32 +1345,6 @@ RxFilterInfoList *qmp_query_rx_filter(const char *name, Error **errp) return filter_list; } -void hmp_info_network(Monitor *mon, const QDict *qdict) -{ - NetClientState *nc, *peer; - NetClientDriver type; - - net_hub_info(mon); - - QTAILQ_FOREACH(nc, &net_clients, next) { - peer = nc->peer; - type = nc->info->type; - - /* Skip if already printed in hub info */ - if (net_hub_id_for_client(nc, NULL) == 0) { - continue; - } - - if (!peer || type == NET_CLIENT_DRIVER_NIC) { - print_net_client(mon, nc); - } /* else it's a netdev connected to a NIC, printed with the NIC */ - if (peer && type == NET_CLIENT_DRIVER_NIC) { - monitor_printf(mon, " \\ "); - print_net_client(mon, peer); - } - } -} - void colo_notify_filters_event(int event, Error **errp) { NetClientState *nc; diff --git a/plugins/core.c b/plugins/core.c index ccb770a485..e04ffa1ba4 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -500,26 +500,33 @@ void qemu_plugin_user_exit(void) enum qemu_plugin_event ev; CPUState *cpu; - QEMU_LOCK_GUARD(&plugin.lock); - + /* + * Locking order: we must acquire locks in an order that is consistent + * with the one in fork_start(). That is: + * - start_exclusive(), which acquires qemu_cpu_list_lock, + * must be called before acquiring plugin.lock. + * - tb_flush(), which acquires mmap_lock(), must be called + * while plugin.lock is not held. + */ start_exclusive(); + qemu_rec_mutex_lock(&plugin.lock); /* un-register all callbacks except the final AT_EXIT one */ for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) { if (ev != QEMU_PLUGIN_EV_ATEXIT) { - struct qemu_plugin_ctx *ctx; - QTAILQ_FOREACH(ctx, &plugin.ctxs, entry) { - plugin_unregister_cb__locked(ctx, ev); + struct qemu_plugin_cb *cb, *next; + + QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { + plugin_unregister_cb__locked(cb->ctx, ev); } } } - - tb_flush(current_cpu); - CPU_FOREACH(cpu) { qemu_plugin_disable_mem_helpers(cpu); } + qemu_rec_mutex_unlock(&plugin.lock); + tb_flush(current_cpu); end_exclusive(); /* now it's safe to handle the exit case */ diff --git a/python/qemu/machine/console_socket.py b/python/qemu/machine/console_socket.py index 8c4ff598ad..4e28ba9bb2 100644 --- a/python/qemu/machine/console_socket.py +++ b/python/qemu/machine/console_socket.py @@ -68,7 +68,7 @@ class ConsoleSocket(socket.socket): """Kick off a thread to drain the socket.""" # Configure socket to not block and timeout. # This allows our drain thread to not block - # on recieve and exit smoothly. + # on receive and exit smoothly. socket.socket.setblocking(self, False) socket.socket.settimeout(self, 1) drain_thread = threading.Thread(target=self._drain_fn) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 748a0d807c..e57c254484 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -131,7 +131,7 @@ class QEMUMachine: drain_console: bool = False, console_log: Optional[str] = None, log_dir: Optional[str] = None, - qmp_timer: Optional[float] = None): + qmp_timer: Optional[float] = 30): ''' Initialize a QEMUMachine @@ -157,18 +157,14 @@ class QEMUMachine: self._wrapper = wrapper self._qmp_timer = qmp_timer - self._name = name or f"qemu-{os.getpid()}-{id(self):02x}" + self._name = name or f"{id(self):x}" + self._sock_pair: Optional[Tuple[socket.socket, socket.socket]] = None self._temp_dir: Optional[str] = None self._base_temp_dir = base_temp_dir self._sock_dir = sock_dir self._log_dir = log_dir - if monitor_address is not None: - self._monitor_address = monitor_address - else: - self._monitor_address = os.path.join( - self.sock_dir, f"{self._name}-monitor.sock" - ) + self._monitor_address = monitor_address self._console_log_path = console_log if self._console_log_path: @@ -192,7 +188,7 @@ class QEMUMachine: self._console_set = False self._console_device_type: Optional[str] = None self._console_address = os.path.join( - self.sock_dir, f"{self._name}-console.sock" + self.sock_dir, f"{self._name}.con" ) self._console_socket: Optional[socket.socket] = None self._remove_files: List[str] = [] @@ -303,7 +299,11 @@ class QEMUMachine: args = ['-display', 'none', '-vga', 'none'] if self._qmp_set: - if isinstance(self._monitor_address, tuple): + if self._sock_pair: + fd = self._sock_pair[0].fileno() + os.set_inheritable(fd, True) + moncdev = f"socket,id=mon,fd={fd}" + elif isinstance(self._monitor_address, tuple): moncdev = "socket,id=mon,host={},port={}".format( *self._monitor_address ) @@ -337,10 +337,17 @@ class QEMUMachine: self._remove_files.append(self._console_address) if self._qmp_set: + monitor_address = None + sock = None + if self._monitor_address is None: + self._sock_pair = socket.socketpair() + sock = self._sock_pair[1] if isinstance(self._monitor_address, str): self._remove_files.append(self._monitor_address) + monitor_address = self._monitor_address self._qmp_connection = QEMUMonitorProtocol( - self._monitor_address, + address=monitor_address, + sock=sock, server=True, nickname=self._name ) @@ -360,6 +367,8 @@ class QEMUMachine: )) def _post_launch(self) -> None: + if self._sock_pair: + self._sock_pair[0].close() if self._qmp_connection: self._qmp.accept(self._qmp_timer) diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py index 1a1fc6c9b0..1c46138bd0 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -42,7 +42,7 @@ class QEMUQtestProtocol: :raise socket.error: on socket connection errors .. note:: - No conection is estabalished by __init__(), this is done + No connection is established by __init__(), this is done by the connect() or accept() methods. """ def __init__(self, address: SocketAddrT, diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index 1951754455..8b09ee7dbb 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -22,6 +22,7 @@ old interface. # import asyncio +import socket from types import TracebackType from typing import ( Any, @@ -69,22 +70,32 @@ class QEMUMonitorProtocol: :param address: QEMU address, can be either a unix socket path (string) or a tuple in the form ( address, port ) for a TCP - connection + connection or None + :param sock: a socket or None :param server: Act as the socket server. (See 'accept') :param nickname: Optional nickname used for logging. """ - def __init__(self, address: SocketAddrT, + def __init__(self, + address: Optional[SocketAddrT] = None, + sock: Optional[socket.socket] = None, server: bool = False, nickname: Optional[str] = None): + assert address or sock self._qmp = QMPClient(nickname) self._aloop = asyncio.get_event_loop() self._address = address + self._sock = sock self._timeout: Optional[float] = None if server: - self._sync(self._qmp.start_server(self._address)) + if sock: + assert self._sock is not None + self._sync(self._qmp.open_with_socket(self._sock)) + else: + assert self._address is not None + self._sync(self._qmp.start_server(self._address)) _T = TypeVar('_T') @@ -139,6 +150,7 @@ class QEMUMonitorProtocol: :return: QMP greeting dict, or None if negotiate is false :raise ConnectError: on connection errors """ + assert self._address is not None self._qmp.await_greeting = negotiate self._qmp.negotiate = negotiate diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 6ea86650ad..6d3d739daa 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -18,6 +18,7 @@ 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, @@ -298,6 +299,19 @@ class AsyncProtocol(Generic[T]): @upper_half @require(Runstate.IDLE) + async def open_with_socket(self, sock: socket.socket) -> None: + """ + Start connection with given socket. + + :param sock: A socket. + + :raise StateError: When the `Runstate` is not `IDLE`. + """ + self._reader, self._writer = await asyncio.open_connection(sock=sock) + self._set_state(Runstate.CONNECTING) + + @upper_half + @require(Runstate.IDLE) async def start_server(self, address: SocketAddrT, ssl: Optional[SSLContext] = None) -> None: """ @@ -343,11 +357,12 @@ class AsyncProtocol(Generic[T]): 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') + if not self._reader: + 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') @@ -812,7 +827,7 @@ class AsyncProtocol(Generic[T]): @bottom_half async def _bh_close_stream(self, error_pathway: bool = False) -> None: - # NB: Closing the writer also implcitly closes the reader. + # NB: Closing the writer also implicitly closes the reader. if not self._writer: return diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index 5dcda04a75..b5772e7f32 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -197,8 +197,8 @@ class QMPClient(AsyncProtocol[Message], Events): #: Logger object used for debugging messages. logger = logging.getLogger(__name__) - # Read buffer limit; large enough to accept query-qmp-schema - _limit = (256 * 1024) + # Read buffer limit; 10MB like libvirt default + _limit = (10 * 1024 * 1024) # Type alias for pending execute() result items _PendingT = Union[Message, ExecInterruptedError] diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index ce239d8979..8369144723 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -71,7 +71,7 @@ def format_json(msg: str) -> str: due to an decoding error then a simple string manipulation is done to achieve a single line JSON string. - Converting into single line is more asthetically pleasing when looking + Converting into single line is more aesthetically pleasing when looking along with error messages. Eg: @@ -91,7 +91,7 @@ def format_json(msg: str) -> str: [1, true, 3]: QMP message is not a JSON object. - The single line mode is more asthetically pleasing. + The single line mode is more aesthetically pleasing. :param msg: The message to formatted into single line. @@ -498,7 +498,7 @@ class EditorWidget(urwid.Filler): class HistoryBox(urwid.ListBox): """ This widget is modelled using the ListBox widget, contains the list of - all messages both QMP messages and log messsages to be shown in the TUI. + all messages both QMP messages and log messages to be shown in the TUI. The messages are urwid.Text widgets. On every append of a message, the focus is shifted to the last appended message. diff --git a/qapi/audio.json b/qapi/audio.json index 1e0a24bdfc..4e54c00f51 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -408,8 +408,18 @@ # Since: 4.0 ## { 'enum': 'AudiodevDriver', - 'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', 'pa', - 'sdl', 'sndio', 'spice', 'wav' ] } + 'data': [ 'none', + { 'name': 'alsa', 'if': 'CONFIG_AUDIO_ALSA' }, + { 'name': 'coreaudio', 'if': 'CONFIG_AUDIO_COREAUDIO' }, + { 'name': 'dbus', 'if': 'CONFIG_DBUS_DISPLAY' }, + { 'name': 'dsound', 'if': 'CONFIG_AUDIO_DSOUND' }, + { 'name': 'jack', 'if': 'CONFIG_AUDIO_JACK' }, + { 'name': 'oss', 'if': 'CONFIG_AUDIO_OSS' }, + { 'name': 'pa', 'if': 'CONFIG_AUDIO_PA' }, + { 'name': 'sdl', 'if': 'CONFIG_AUDIO_SDL' }, + { 'name': 'sndio', 'if': 'CONFIG_AUDIO_SNDIO' }, + { 'name': 'spice', 'if': 'CONFIG_SPICE' }, + 'wav' ] } ## # @Audiodev: @@ -432,14 +442,37 @@ 'discriminator': 'driver', 'data': { 'none': 'AudiodevGenericOptions', - 'alsa': 'AudiodevAlsaOptions', - 'coreaudio': 'AudiodevCoreaudioOptions', - 'dbus': 'AudiodevGenericOptions', - 'dsound': 'AudiodevDsoundOptions', - 'jack': 'AudiodevJackOptions', - 'oss': 'AudiodevOssOptions', - 'pa': 'AudiodevPaOptions', - 'sdl': 'AudiodevSdlOptions', - 'sndio': 'AudiodevSndioOptions', - 'spice': 'AudiodevGenericOptions', + 'alsa': { 'type': 'AudiodevAlsaOptions', + 'if': 'CONFIG_AUDIO_ALSA' }, + 'coreaudio': { 'type': 'AudiodevCoreaudioOptions', + 'if': 'CONFIG_AUDIO_COREAUDIO' }, + 'dbus': { 'type': 'AudiodevGenericOptions', + 'if': 'CONFIG_DBUS_DISPLAY' }, + 'dsound': { 'type': 'AudiodevDsoundOptions', + 'if': 'CONFIG_AUDIO_DSOUND' }, + 'jack': { 'type': 'AudiodevJackOptions', + 'if': 'CONFIG_AUDIO_JACK' }, + 'oss': { 'type': 'AudiodevOssOptions', + 'if': 'CONFIG_AUDIO_OSS' }, + 'pa': { 'type': 'AudiodevPaOptions', + 'if': 'CONFIG_AUDIO_PA' }, + 'sdl': { 'type': 'AudiodevSdlOptions', + 'if': 'CONFIG_AUDIO_SDL' }, + 'sndio': { 'type': 'AudiodevSndioOptions', + 'if': 'CONFIG_AUDIO_SNDIO' }, + 'spice': { 'type': 'AudiodevGenericOptions', + 'if': 'CONFIG_SPICE' }, 'wav': 'AudiodevWavOptions' } } + +## +# @query-audiodevs: +# +# Returns information about audiodev configuration +# +# Returns: array of @Audiodev +# +# Since: 8.0 +# +## +{ 'command': 'query-audiodevs', + 'returns': ['Audiodev'] } diff --git a/qapi/block-core.json b/qapi/block-core.json index 95ac4fa634..7f331eb8ea 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -124,7 +124,33 @@ 'create-type': 'str', 'cid': 'int', 'parent-cid': 'int', - 'extents': ['ImageInfo'] + 'extents': ['VmdkExtentInfo'] + } } + +## +# @VmdkExtentInfo: +# +# Information about a VMDK extent file +# +# @filename: Name of the extent file +# +# @format: Extent type (e.g. FLAT or SPARSE) +# +# @virtual-size: Number of bytes covered by this extent +# +# @cluster-size: Cluster size in bytes (for non-flat extents) +# +# @compressed: Whether this extent contains compressed data +# +# Since: 8.0 +## +{ 'struct': 'VmdkExtentInfo', + 'data': { + 'filename': 'str', + 'format': 'str', + 'virtual-size': 'int', + '*cluster-size': 'int', + '*compressed': 'bool' } } ## @@ -140,15 +166,28 @@ } } ## +# @ImageInfoSpecificFile: +# +# @extent-size-hint: Extent size hint (if available) +# +# Since: 8.0 +## +{ 'struct': 'ImageInfoSpecificFile', + 'data': { + '*extent-size-hint': 'size' + } } + +## # @ImageInfoSpecificKind: # # @luks: Since 2.7 # @rbd: Since 6.1 +# @file: Since 8.0 # # Since: 1.7 ## { 'enum': 'ImageInfoSpecificKind', - 'data': [ 'qcow2', 'vmdk', 'luks', 'rbd' ] } + 'data': [ 'qcow2', 'vmdk', 'luks', 'rbd', 'file' ] } ## # @ImageInfoSpecificQCow2Wrapper: @@ -186,6 +225,14 @@ 'data': { 'data': 'ImageInfoSpecificRbd' } } ## +# @ImageInfoSpecificFileWrapper: +# +# Since: 8.0 +## +{ 'struct': 'ImageInfoSpecificFileWrapper', + 'data': { 'data': 'ImageInfoSpecificFile' } } + +## # @ImageInfoSpecific: # # A discriminated record of image format specific information structures. @@ -199,11 +246,12 @@ 'qcow2': 'ImageInfoSpecificQCow2Wrapper', 'vmdk': 'ImageInfoSpecificVmdkWrapper', 'luks': 'ImageInfoSpecificLUKSWrapper', - 'rbd': 'ImageInfoSpecificRbdWrapper' + 'rbd': 'ImageInfoSpecificRbdWrapper', + 'file': 'ImageInfoSpecificFileWrapper' } } ## -# @ImageInfo: +# @BlockNodeInfo: # # Information about a QEMU image file # @@ -231,23 +279,70 @@ # # @snapshots: list of VM snapshots # -# @backing-image: info of the backing image (since 1.6) -# # @format-specific: structure supplying additional format-specific # information (since 1.7) # -# Since: 1.3 +# Since: 8.0 ## -{ 'struct': 'ImageInfo', +{ 'struct': 'BlockNodeInfo', 'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool', '*actual-size': 'int', 'virtual-size': 'int', '*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool', '*backing-filename': 'str', '*full-backing-filename': 'str', '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'], - '*backing-image': 'ImageInfo', '*format-specific': 'ImageInfoSpecific' } } ## +# @ImageInfo: +# +# Information about a QEMU image file, and potentially its backing image +# +# @backing-image: info of the backing image +# +# Since: 1.3 +## +{ 'struct': 'ImageInfo', + 'base': 'BlockNodeInfo', + 'data': { + '*backing-image': 'ImageInfo' + } } + +## +# @BlockChildInfo: +# +# Information about all nodes in the block graph starting at some node, +# annotated with information about that node in relation to its parent. +# +# @name: Child name of the root node in the BlockGraphInfo struct, in its role +# as the child of some undescribed parent node +# +# @info: Block graph information starting at this node +# +# Since: 8.0 +## +{ 'struct': 'BlockChildInfo', + 'data': { + 'name': 'str', + 'info': 'BlockGraphInfo' + } } + +## +# @BlockGraphInfo: +# +# Information about all nodes in a block (sub)graph in the form of BlockNodeInfo +# data. +# The base BlockNodeInfo struct contains the information for the (sub)graph's +# root node. +# +# @children: Array of links to this node's child nodes' information +# +# Since: 8.0 +## +{ 'struct': 'BlockGraphInfo', + 'base': 'BlockNodeInfo', + 'data': { 'children': ['BlockChildInfo'] } } + +## # @ImageCheck: # # Information about a QEMU image file check @@ -5732,3 +5827,13 @@ 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, 'returns': 'SnapshotInfo', 'allow-preconfig': true } + +## +# @DummyBlockCoreForceArrays: +# +# Not used by QMP; hack to let us use BlockGraphInfoList internally +# +# Since: 8.0 +## +{ 'struct': 'DummyBlockCoreForceArrays', + 'data': { 'unused-block-graph-info': ['BlockGraphInfo'] } } diff --git a/qemu-img.c b/qemu-img.c index 7e73c5c1da..7c05931866 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -450,6 +450,11 @@ static BlockBackend *img_open(bool image_opts, blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet, force_share); } + + if (blk) { + blk_set_force_allow_inactivate(blk); + } + return blk; } @@ -1120,6 +1125,14 @@ unref_backing: done: qemu_progress_end(); + /* + * Manually inactivate the image first because this way we can know whether + * an error occurred. blk_unref() doesn't tell us about failures. + */ + ret = bdrv_inactivate_all(); + if (ret < 0 && !local_err) { + error_setg_errno(&local_err, -ret, "Error while closing the image"); + } blk_unref(blk); if (local_err) { @@ -2804,13 +2817,13 @@ static void dump_snapshots(BlockDriverState *bs) g_free(sn_tab); } -static void dump_json_image_info_list(ImageInfoList *list) +static void dump_json_block_graph_info_list(BlockGraphInfoList *list) { GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); - visit_type_ImageInfoList(v, NULL, &list, &error_abort); + visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort); visit_complete(v, &obj); str = qobject_to_json_pretty(obj, true); assert(str != NULL); @@ -2820,13 +2833,13 @@ static void dump_json_image_info_list(ImageInfoList *list) g_string_free(str, true); } -static void dump_json_image_info(ImageInfo *info) +static void dump_json_block_graph_info(BlockGraphInfo *info) { GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); - visit_type_ImageInfo(v, NULL, &info, &error_abort); + visit_type_BlockGraphInfo(v, NULL, &info, &error_abort); visit_complete(v, &obj); str = qobject_to_json_pretty(obj, true); assert(str != NULL); @@ -2836,9 +2849,30 @@ static void dump_json_image_info(ImageInfo *info) g_string_free(str, true); } -static void dump_human_image_info_list(ImageInfoList *list) +static void dump_human_image_info(BlockGraphInfo *info, int indentation, + const char *path) +{ + BlockChildInfoList *children_list; + + bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation, + info->children == NULL); + + for (children_list = info->children; children_list; + children_list = children_list->next) + { + BlockChildInfo *child = children_list->value; + g_autofree char *child_path = NULL; + + printf("%*sChild node '%s%s':\n", + indentation * 4, "", path, child->name); + child_path = g_strdup_printf("%s%s/", path, child->name); + dump_human_image_info(child->info, indentation + 1, child_path); + } +} + +static void dump_human_image_info_list(BlockGraphInfoList *list) { - ImageInfoList *elem; + BlockGraphInfoList *elem; bool delim = false; for (elem = list; elem; elem = elem->next) { @@ -2847,7 +2881,7 @@ static void dump_human_image_info_list(ImageInfoList *list) } delim = true; - bdrv_image_info_dump(elem->value); + dump_human_image_info(elem->value, 0, "/"); } } @@ -2857,24 +2891,24 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b) } /** - * Open an image file chain and return an ImageInfoList + * Open an image file chain and return an BlockGraphInfoList * * @filename: topmost image filename * @fmt: topmost image format (may be NULL to autodetect) * @chain: true - enumerate entire backing file chain * false - only topmost image file * - * Returns a list of ImageInfo objects or NULL if there was an error opening an - * image file. If there was an error a message will have been printed to - * stderr. + * Returns a list of BlockNodeInfo objects or NULL if there was an error + * opening an image file. If there was an error a message will have been + * printed to stderr. */ -static ImageInfoList *collect_image_info_list(bool image_opts, - const char *filename, - const char *fmt, - bool chain, bool force_share) +static BlockGraphInfoList *collect_image_info_list(bool image_opts, + const char *filename, + const char *fmt, + bool chain, bool force_share) { - ImageInfoList *head = NULL; - ImageInfoList **tail = &head; + BlockGraphInfoList *head = NULL; + BlockGraphInfoList **tail = &head; GHashTable *filenames; Error *err = NULL; @@ -2883,7 +2917,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts, while (filename) { BlockBackend *blk; BlockDriverState *bs; - ImageInfo *info; + BlockGraphInfo *info; if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) { error_report("Backing file '%s' creates an infinite loop.", @@ -2900,7 +2934,14 @@ static ImageInfoList *collect_image_info_list(bool image_opts, } bs = blk_bs(blk); - bdrv_query_image_info(bs, &info, &err); + /* + * Note that the returned BlockGraphInfo object will not have + * information about this image's backing node, because we have opened + * it with BDRV_O_NO_BACKING. Printing this object will therefore not + * duplicate the backing chain information that we obtain by walking + * the chain manually here. + */ + bdrv_query_block_graph_info(bs, &info, &err); if (err) { error_report_err(err); blk_unref(blk); @@ -2933,7 +2974,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts, return head; err: - qapi_free_ImageInfoList(head); + qapi_free_BlockGraphInfoList(head); g_hash_table_destroy(filenames); return NULL; } @@ -2944,7 +2985,7 @@ static int img_info(int argc, char **argv) OutputFormat output_format = OFORMAT_HUMAN; bool chain = false; const char *filename, *fmt, *output; - ImageInfoList *list; + BlockGraphInfoList *list; bool image_opts = false; bool force_share = false; @@ -3023,14 +3064,14 @@ static int img_info(int argc, char **argv) break; case OFORMAT_JSON: if (chain) { - dump_json_image_info_list(list); + dump_json_block_graph_info_list(list); } else { - dump_json_image_info(list->value); + dump_json_block_graph_info(list->value); } break; } - qapi_free_ImageInfoList(list); + qapi_free_BlockGraphInfoList(list); return 0; } @@ -4633,6 +4674,7 @@ static int img_bitmap(int argc, char **argv) QSIMPLEQ_HEAD(, ImgBitmapAction) actions; ImgBitmapAction *act, *act_next; const char *op; + int inactivate_ret; QSIMPLEQ_INIT(&actions); @@ -4817,6 +4859,16 @@ static int img_bitmap(int argc, char **argv) ret = 0; out: + /* + * Manually inactivate the images first because this way we can know whether + * an error occurred. blk_unref() doesn't tell us about failures. + */ + inactivate_ret = bdrv_inactivate_all(); + if (inactivate_ret < 0) { + error_report("Error while closing the image: %s", strerror(-inactivate_ret)); + ret = 1; + } + blk_unref(src); blk_unref(blk); qemu_opts_del(opts); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 952dc940f1..a061031615 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -572,54 +572,17 @@ static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, return 1; } -typedef struct { - BlockBackend *blk; - int64_t offset; - int64_t bytes; - int64_t *total; - int flags; - int ret; - bool done; -} CoWriteZeroes; - -static void coroutine_fn co_pwrite_zeroes_entry(void *opaque) -{ - CoWriteZeroes *data = opaque; - - data->ret = blk_co_pwrite_zeroes(data->blk, data->offset, data->bytes, - data->flags); - data->done = true; - if (data->ret < 0) { - *data->total = data->ret; - return; - } - - *data->total = data->bytes; -} - -static int do_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, +static int do_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, int flags, int64_t *total) { - Coroutine *co; - CoWriteZeroes data = { - .blk = blk, - .offset = offset, - .bytes = bytes, - .total = total, - .flags = flags, - .done = false, - }; - - co = qemu_coroutine_create(co_pwrite_zeroes_entry, &data); - bdrv_coroutine_enter(blk_bs(blk), co); - while (!data.done) { - aio_poll(blk_get_aio_context(blk), true); - } - if (data.ret < 0) { - return data.ret; - } else { - return 1; + int ret = blk_pwrite_zeroes(blk, offset, bytes, + flags | BDRV_REQ_ZERO_WRITE); + + if (ret < 0) { + return ret; } + *total = bytes; + return 1; } static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset, @@ -1042,7 +1005,7 @@ static void write_help(void) " -C, -- report statistics in a machine parsable format\n" " -q, -- quiet mode, do not show I/O statistics\n" " -u, -- with -z, allow unmapping\n" -" -z, -- write zeroes using blk_co_pwrite_zeroes\n" +" -z, -- write zeroes using blk_pwrite_zeroes\n" "\n"); } @@ -1199,7 +1162,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) if (bflag) { ret = do_save_vmstate(blk, buf, offset, count, &total); } else if (zflag) { - ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total); + ret = do_pwrite_zeroes(blk, offset, count, flags, &total); } else if (cflag) { ret = do_write_compressed(blk, buf, offset, count, &total); } else { @@ -1825,8 +1788,9 @@ static int info_f(BlockBackend *blk, int argc, char **argv) return -EIO; } if (spec_info) { - printf("Format specific information:\n"); - bdrv_image_info_specific_dump(spec_info); + bdrv_image_info_specific_dump(spec_info, + "Format specific information:\n", + 0); qapi_free_ImageInfoSpecific(spec_info); } diff --git a/qemu-options.hx b/qemu-options.hx index d59d19704b..88e93c6103 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4633,10 +4633,11 @@ DEF("semihosting", 0, QEMU_OPTION_semihosting, QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV) SRST ``-semihosting`` - Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). + Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). - Note that this allows guest direct access to the host filesystem, so - should only be used with a trusted guest OS. + .. warning:: + Note that this allows guest direct access to the host filesystem, so + should only be used with a trusted guest OS. See the -semihosting-config option documentation for further information about the facilities this enables. @@ -4648,22 +4649,12 @@ QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV) SRST ``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]`` - Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V + Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). - Note that this allows guest direct access to the host filesystem, so - should only be used with a trusted guest OS. - - On Arm this implements the standard semihosting API, version 2.0. - - On M68K this implements the "ColdFire GDB" interface used by - libgloss. - - Xtensa semihosting provides basic file IO calls, such as - open/read/write/seek/select. Tensilica baremetal libc for ISS and - linux platform "sim" use this interface. - - On RISC-V this implements the standard semihosting API, version 0.2. + .. warning:: + Note that this allows guest direct access to the host filesystem, so + should only be used with a trusted guest OS. ``target=native|gdb|auto`` Defines where the semihosting calls will be addressed, to QEMU diff --git a/qom/qom-hmp-cmds.c b/qom/qom-hmp-cmds.c index 453fbfeabc..6e3a2175a4 100644 --- a/qom/qom-hmp-cmds.c +++ b/qom/qom-hmp-cmds.c @@ -13,7 +13,9 @@ #include "qapi/qapi-commands-qom.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" +#include "qemu/readline.h" #include "qom/object.h" +#include "qom/object_interfaces.h" void hmp_qom_list(Monitor *mon, const QDict *qdict) { @@ -150,3 +152,68 @@ void hmp_info_qom_tree(Monitor *mon, const QDict *dict) } print_qom_composition(mon, obj, 0); } + +void hmp_object_add(Monitor *mon, const QDict *qdict) +{ + const char *options = qdict_get_str(qdict, "object"); + Error *err = NULL; + + user_creatable_add_from_str(options, &err); + hmp_handle_error(mon, err); +} + +void hmp_object_del(Monitor *mon, const QDict *qdict) +{ + const char *id = qdict_get_str(qdict, "id"); + Error *err = NULL; + + user_creatable_del(id, &err); + hmp_handle_error(mon, err); +} + +void object_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + GSList *list, *elt; + size_t len; + + if (nb_args != 2) { + return; + } + + len = strlen(str); + readline_set_completion_index(rs, len); + list = elt = object_class_get_list(TYPE_USER_CREATABLE, false); + while (elt) { + const char *name; + + name = object_class_get_name(OBJECT_CLASS(elt->data)); + if (strcmp(name, TYPE_USER_CREATABLE)) { + readline_add_completion_of(rs, str, name); + } + elt = elt->next; + } + g_slist_free(list); +} + +void object_del_completion(ReadLineState *rs, int nb_args, const char *str) +{ + ObjectPropertyInfoList *list, *start; + size_t len; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_qom_list("/objects", NULL); + while (list) { + ObjectPropertyInfo *info = list->value; + + if (!strncmp(info->type, "child<", 5)) { + readline_add_completion_of(rs, str, info->name); + } + list = list->next; + } + qapi_free_ObjectPropertyInfoList(start); +} diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py index dff3af49f5..e82b648127 100644 --- a/scripts/block-coroutine-wrapper.py +++ b/scripts/block-coroutine-wrapper.py @@ -86,6 +86,16 @@ class FuncDecl: ctx = 'qemu_get_aio_context()' self.ctx = ctx + self.get_result = 's->ret = ' + self.ret = 'return s.ret;' + self.co_ret = 'return ' + self.return_field = self.return_type + " ret;" + if self.return_type == 'void': + self.get_result = '' + self.ret = '' + self.co_ret = '' + self.return_field = '' + def gen_list(self, format: str) -> str: return ', '.join(format.format_map(arg.__dict__) for arg in self.args) @@ -132,7 +142,7 @@ def create_mixed_wrapper(func: FuncDecl) -> str: {{ if (qemu_in_coroutine()) {{ {graph_assume_lock} - return {name}({ func.gen_list('{name}') }); + {func.co_ret}{name}({ func.gen_list('{name}') }); }} else {{ {struct_name} s = {{ .poll_state.ctx = {func.ctx}, @@ -144,7 +154,7 @@ def create_mixed_wrapper(func: FuncDecl) -> str: s.poll_state.co = qemu_coroutine_create({name}_entry, &s); bdrv_poll_co(&s.poll_state); - return s.ret; + {func.ret} }} }}""" @@ -169,7 +179,7 @@ def create_co_wrapper(func: FuncDecl) -> str: s.poll_state.co = qemu_coroutine_create({name}_entry, &s); bdrv_poll_co(&s.poll_state); - return s.ret; + {func.ret} }}""" @@ -196,7 +206,7 @@ def gen_wrapper(func: FuncDecl) -> str: typedef struct {struct_name} {{ BdrvPollCo poll_state; - {func.return_type} ret; + {func.return_field} { func.gen_block(' {decl};') } }} {struct_name}; @@ -205,7 +215,7 @@ static void coroutine_fn {name}_entry(void *opaque) {struct_name} *s = opaque; {graph_lock} - s->ret = {name}({ func.gen_list('s->{name}') }); + {func.get_result}{name}({ func.gen_list('s->{name}') }); {graph_unlock} s->poll_state.in_progress = false; diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/build-environment.yml index b04c2b7cee..58438008ee 100644 --- a/scripts/ci/setup/build-environment.yml +++ b/scripts/ci/setup/build-environment.yml @@ -155,7 +155,6 @@ - nettle-devel - ninja-build - nmap-ncat - - perl-Test-Harness - pixman-devel - python36 - rdma-core-devel diff --git a/scripts/ci/setup/gitlab-runner.yml b/scripts/ci/setup/gitlab-runner.yml index 33128be85d..95d4199c03 100644 --- a/scripts/ci/setup/gitlab-runner.yml +++ b/scripts/ci/setup/gitlab-runner.yml @@ -50,60 +50,30 @@ - name: Download the matching gitlab-runner get_url: - dest: /usr/local/bin/gitlab-runner - url: "https://s3.amazonaws.com/gitlab-runner-downloads/v{{ gitlab_runner_version }}/binaries/gitlab-runner-{{ gitlab_runner_os }}-{{ gitlab_runner_arch }}" - owner: gitlab-runner - group: gitlab-runner - mode: u=rwx,g=rwx,o=rx - - - name: Register the gitlab-runner - command: "/usr/local/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list {{ ansible_facts[\"architecture\"] }},{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" - - - name: Install the gitlab-runner service using its own functionality - command: /usr/local/bin/gitlab-runner install --user gitlab-runner --working-directory /home/gitlab-runner - 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" + dest: "/root/" + url: "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_{{ gitlab_runner_arch }}.deb" - - name: Enable the gitlab-runner service - service: - name: gitlab-runner - state: started - enabled: yes + - name: Install gitlab-runner via package manager + apt: deb="/root/gitlab-runner_{{ gitlab_runner_arch }}.deb" - - 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 the gitlab-runner + command: "/usr/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list {{ ansible_facts[\"architecture\"] }},{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" + # The secondary runner will still run under the single gitlab-runner service - 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\"] }})'" + command: "/usr/bin/gitlab-runner 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' + - ansible_facts['distribution_version'] == '22.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 + - name: Install the gitlab-runner service using its own functionality + command: "/usr/bin/gitlab-runner install --user gitlab-runner --working-directory /home/gitlab-runner" 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 + - name: Enable the gitlab-runner service service: - name: gitlab-runner-arm + name: gitlab-runner state: started enabled: yes - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '20.04' diff --git a/scripts/ci/setup/vars.yml.template b/scripts/ci/setup/vars.yml.template index e48089761f..4b355fb80f 100644 --- a/scripts/ci/setup/vars.yml.template +++ b/scripts/ci/setup/vars.yml.template @@ -1,5 +1,3 @@ -# The version of the gitlab-runner to use -gitlab_runner_version: 13.12.0 # The URL of the gitlab server to use, usually https://gitlab.com unless you're # using a private GitLab instance gitlab_runner_server_url: https://gitlab.com diff --git a/scripts/oss-fuzz/lsan_suppressions.txt b/scripts/oss-fuzz/lsan_suppressions.txt new file mode 100644 index 0000000000..02ec0a6ed5 --- /dev/null +++ b/scripts/oss-fuzz/lsan_suppressions.txt @@ -0,0 +1,2 @@ +# The tcmalloc on Fedora37 confuses things +leak:/lib64/libtcmalloc_minimal.so.4 diff --git a/scripts/shaderinclude.pl b/scripts/shaderinclude.pl deleted file mode 100644 index cd3bb40b12..0000000000 --- a/scripts/shaderinclude.pl +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -my $file = shift; -open FILE, "<", $file or die "open $file: $!"; -my $name = $file; -$name =~ s|.*/||; -$name =~ s/[-.]/_/g; -print "static GLchar ${name}_src[] =\n"; -while (<FILE>) { - chomp; - printf " \"%s\\n\"\n", $_; -} -print " \"\\n\";\n"; -close FILE; diff --git a/scripts/shaderinclude.py b/scripts/shaderinclude.py new file mode 100644 index 0000000000..ab2aade2cd --- /dev/null +++ b/scripts/shaderinclude.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +import os + + +def main(args): + file_path = args[1] + basename = os.path.basename(file_path) + varname = basename.replace('-', '_').replace('.', '_') + + with os.fdopen(sys.stdout.fileno(), "wt", closefd=False, newline='\n') as stdout: + with open(file_path, "r", encoding='utf-8') as file: + print(f'static GLchar {varname}_src[] =', file=stdout) + for line in file: + line = line.rstrip() + print(f' "{line}\\n"', file=stdout) + print(' "\\n";', file=stdout) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 5893c760c5..e89992cf90 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -253,7 +253,7 @@ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, { CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; char *p; - int ret, host_flags; + int ret, host_flags = O_BINARY; ret = validate_lock_user_string(&p, cs, fname, fname_len); if (ret < 0) { @@ -262,11 +262,11 @@ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, } if (gdb_flags & GDB_O_WRONLY) { - host_flags = O_WRONLY; + host_flags |= O_WRONLY; } else if (gdb_flags & GDB_O_RDWR) { - host_flags = O_RDWR; + host_flags |= O_RDWR; } else { - host_flags = O_RDONLY; + host_flags |= O_RDONLY; } if (gdb_flags & GDB_O_CREAT) { host_flags |= O_CREAT; @@ -319,11 +319,11 @@ static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, } ret = RETRY_ON_EINTR(read(gf->hostfd, ptr, len)); if (ret == -1) { - complete(cs, -1, errno); unlock_user(ptr, buf, 0); + complete(cs, -1, errno); } else { - complete(cs, ret, 0); unlock_user(ptr, buf, ret); + complete(cs, ret, 0); } } @@ -339,8 +339,8 @@ static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, return; } ret = write(gf->hostfd, ptr, len); - complete(cs, ret, ret == -1 ? errno : 0); unlock_user(ptr, buf, 0); + complete(cs, ret, ret == -1 ? errno : 0); } static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete, @@ -426,8 +426,8 @@ static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, ret = -1; } } - complete(cs, ret, err); unlock_user(name, fname, 0); + complete(cs, ret, err); } static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, @@ -444,8 +444,8 @@ static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, } ret = remove(p); - complete(cs, ret, ret ? errno : 0); unlock_user(p, fname, 0); + complete(cs, ret, ret ? errno : 0); } static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, @@ -469,9 +469,9 @@ static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, } ret = rename(ostr, nstr); - complete(cs, ret, ret ? errno : 0); unlock_user(ostr, oname, 0); unlock_user(nstr, nname, 0); + complete(cs, ret, ret ? errno : 0); } static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, @@ -488,8 +488,8 @@ static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, } ret = system(p); - complete(cs, ret, ret == -1 ? errno : 0); unlock_user(p, cmd, 0); + complete(cs, ret, ret == -1 ? errno : 0); } static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, @@ -554,8 +554,8 @@ static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, } memcpy(ptr, gf->staticfile.data + gf->staticfile.off, len); gf->staticfile.off += len; - complete(cs, len, 0); unlock_user(ptr, buf, len); + complete(cs, len, 0); } static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete, @@ -608,8 +608,8 @@ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, return; } ret = qemu_semihosting_console_read(cs, ptr, len); - complete(cs, ret, 0); unlock_user(ptr, buf, ret); + complete(cs, ret, 0); } static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, @@ -624,8 +624,8 @@ static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, return; } ret = qemu_semihosting_console_write(ptr, len); - complete(cs, ret ? ret : -1, ret ? 0 : EIO); unlock_user(ptr, buf, 0); + complete(cs, ret ? ret : -1, ret ? 0 : EIO); } static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, diff --git a/softmmu/meson.build b/softmmu/meson.build index 3272af1f31..1828db149c 100644 --- a/softmmu/meson.build +++ b/softmmu/meson.build @@ -24,7 +24,9 @@ softmmu_ss.add(files( 'qdev-monitor.c', 'rtc.c', 'runstate-action.c', + 'runstate-hmp-cmds.c', 'runstate.c', + 'tpm-hmp-cmds.c', 'vl.c', ), sdl, libpmem, libdaxctl) diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 4b0ef65780..b8d2c4dadd 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -973,6 +973,88 @@ void hmp_device_del(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } +void device_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + GSList *list, *elt; + size_t len; + + if (nb_args != 2) { + return; + } + + len = strlen(str); + readline_set_completion_index(rs, len); + list = elt = object_class_get_list(TYPE_DEVICE, false); + while (elt) { + DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data, + TYPE_DEVICE); + + if (dc->user_creatable) { + readline_add_completion_of(rs, str, + object_class_get_name(OBJECT_CLASS(dc))); + } + elt = elt->next; + } + g_slist_free(list); +} + +static int qdev_add_hotpluggable_device(Object *obj, void *opaque) +{ + GSList **list = opaque; + DeviceState *dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE); + + if (dev == NULL) { + return 0; + } + + if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { + *list = g_slist_append(*list, dev); + } + + return 0; +} + +static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) +{ + GSList *list = NULL; + + object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); + + return list; +} + +static void peripheral_device_del_completion(ReadLineState *rs, + const char *str) +{ + Object *peripheral = container_get(qdev_get_machine(), "/peripheral"); + GSList *list, *item; + + list = qdev_build_hotpluggable_device_list(peripheral); + if (!list) { + return; + } + + for (item = list; item; item = g_slist_next(item)) { + DeviceState *dev = item->data; + + if (dev->id) { + readline_add_completion_of(rs, str, dev->id); + } + } + + g_slist_free(list); +} + +void device_del_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args != 2) { + return; + } + + readline_set_completion_index(rs, strlen(str)); + peripheral_device_del_completion(rs, str); +} + BlockBackend *blk_by_qdev_id(const char *id, Error **errp) { DeviceState *dev; diff --git a/softmmu/runstate-hmp-cmds.c b/softmmu/runstate-hmp-cmds.c new file mode 100644 index 0000000000..d55a7d4db8 --- /dev/null +++ b/softmmu/runstate-hmp-cmds.c @@ -0,0 +1,82 @@ +/* + * HMP commands related to run state + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "exec/cpu-common.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-run-state.h" +#include "qapi/qmp/qdict.h" + +void hmp_info_status(Monitor *mon, const QDict *qdict) +{ + StatusInfo *info; + + info = qmp_query_status(NULL); + + monitor_printf(mon, "VM status: %s%s", + info->running ? "running" : "paused", + info->singlestep ? " (single step mode)" : ""); + + if (!info->running && info->status != RUN_STATE_PAUSED) { + monitor_printf(mon, " (%s)", RunState_str(info->status)); + } + + monitor_printf(mon, "\n"); + + qapi_free_StatusInfo(info); +} + +void hmp_singlestep(Monitor *mon, const QDict *qdict) +{ + const char *option = qdict_get_try_str(qdict, "option"); + if (!option || !strcmp(option, "on")) { + singlestep = 1; + } else if (!strcmp(option, "off")) { + singlestep = 0; + } else { + monitor_printf(mon, "unexpected option %s\n", option); + } +} + +void hmp_watchdog_action(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + WatchdogAction action; + char *qapi_value; + + qapi_value = g_ascii_strdown(qdict_get_str(qdict, "action"), -1); + action = qapi_enum_parse(&WatchdogAction_lookup, qapi_value, -1, &err); + g_free(qapi_value); + if (err) { + hmp_handle_error(mon, err); + return; + } + qmp_watchdog_set_action(action, &error_abort); +} + +void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str) +{ + int i; + + if (nb_args != 2) { + return; + } + readline_set_completion_index(rs, strlen(str)); + for (i = 0; i < WATCHDOG_ACTION__MAX; i++) { + readline_add_completion_of(rs, str, WatchdogAction_str(i)); + } +} diff --git a/softmmu/tpm-hmp-cmds.c b/softmmu/tpm-hmp-cmds.c new file mode 100644 index 0000000000..9ed6ad6c4d --- /dev/null +++ b/softmmu/tpm-hmp-cmds.c @@ -0,0 +1,65 @@ +/* + * HMP commands related to TPM + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-commands-tpm.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" +#include "qapi/error.h" + +void hmp_info_tpm(Monitor *mon, const QDict *qdict) +{ +#ifdef CONFIG_TPM + TPMInfoList *info_list, *info; + Error *err = NULL; + unsigned int c = 0; + TPMPassthroughOptions *tpo; + TPMEmulatorOptions *teo; + + info_list = qmp_query_tpm(&err); + if (err) { + monitor_printf(mon, "TPM device not supported\n"); + error_free(err); + return; + } + + if (info_list) { + monitor_printf(mon, "TPM device:\n"); + } + + for (info = info_list; info; info = info->next) { + TPMInfo *ti = info->value; + monitor_printf(mon, " tpm%d: model=%s\n", + c, TpmModel_str(ti->model)); + + monitor_printf(mon, " \\ %s: type=%s", + ti->id, TpmType_str(ti->options->type)); + + switch (ti->options->type) { + case TPM_TYPE_PASSTHROUGH: + tpo = ti->options->u.passthrough.data; + monitor_printf(mon, "%s%s%s%s", + tpo->path ? ",path=" : "", + tpo->path ?: "", + tpo->cancel_path ? ",cancel-path=" : "", + tpo->cancel_path ?: ""); + break; + case TPM_TYPE_EMULATOR: + teo = ti->options->u.emulator.data; + monitor_printf(mon, ",chardev=%s", teo->chardev); + break; + case TPM_TYPE__MAX: + break; + } + monitor_printf(mon, "\n"); + c++; + } + qapi_free_TPMInfoList(info_list); +#else + monitor_printf(mon, "TPM device not supported\n"); +#endif /* CONFIG_TPM */ +} diff --git a/stats/meson.build b/stats/meson.build new file mode 100644 index 0000000000..c4153f979e --- /dev/null +++ b/stats/meson.build @@ -0,0 +1 @@ +softmmu_ss.add(files('stats-hmp-cmds.c', 'stats-qmp-cmds.c')) diff --git a/stats/stats-hmp-cmds.c b/stats/stats-hmp-cmds.c new file mode 100644 index 0000000000..531e35d128 --- /dev/null +++ b/stats/stats-hmp-cmds.c @@ -0,0 +1,247 @@ +/* + * HMP commands related to stats + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-commands-stats.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qemu/cutils.h" +#include "hw/core/cpu.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" + +static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) +{ + const char *unit = NULL; + monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type), + value->has_unit || value->exponent ? ", " : ""); + + if (value->has_unit) { + if (value->unit == STATS_UNIT_SECONDS) { + unit = "s"; + } else if (value->unit == STATS_UNIT_BYTES) { + unit = "B"; + } + } + + if (unit && value->base == 10 && + value->exponent >= -18 && value->exponent <= 18 && + value->exponent % 3 == 0) { + monitor_puts(mon, si_prefix(value->exponent)); + } else if (unit && value->base == 2 && + value->exponent >= 0 && value->exponent <= 60 && + value->exponent % 10 == 0) { + + monitor_puts(mon, iec_binary_prefix(value->exponent)); + } else if (value->exponent) { + /* Use exponential notation and write the unit's English name */ + monitor_printf(mon, "* %d^%d%s", + value->base, value->exponent, + value->has_unit ? " " : ""); + unit = NULL; + } + + if (value->has_unit) { + monitor_puts(mon, unit ? unit : StatsUnit_str(value->unit)); + } + + /* Print bucket size for linear histograms */ + if (value->type == STATS_TYPE_LINEAR_HISTOGRAM && value->has_bucket_size) { + monitor_printf(mon, ", bucket size=%d", value->bucket_size); + } + monitor_printf(mon, ")"); +} + +static StatsSchemaValueList *find_schema_value_list( + StatsSchemaList *list, StatsProvider provider, + StatsTarget target) +{ + StatsSchemaList *node; + + for (node = list; node; node = node->next) { + if (node->value->provider == provider && + node->value->target == target) { + return node->value->stats; + } + } + return NULL; +} + +static void print_stats_results(Monitor *mon, StatsTarget target, + bool show_provider, + StatsResult *result, + StatsSchemaList *schema) +{ + /* Find provider schema */ + StatsSchemaValueList *schema_value_list = + find_schema_value_list(schema, result->provider, target); + StatsList *stats_list; + + if (!schema_value_list) { + monitor_printf(mon, "failed to find schema list for %s\n", + StatsProvider_str(result->provider)); + return; + } + + if (show_provider) { + monitor_printf(mon, "provider: %s\n", + StatsProvider_str(result->provider)); + } + + for (stats_list = result->stats; stats_list; + stats_list = stats_list->next, + schema_value_list = schema_value_list->next) { + + Stats *stats = stats_list->value; + StatsValue *stats_value = stats->value; + StatsSchemaValue *schema_value = schema_value_list->value; + + /* Find schema entry */ + while (!g_str_equal(stats->name, schema_value->name)) { + if (!schema_value_list->next) { + monitor_printf(mon, "failed to find schema entry for %s\n", + stats->name); + return; + } + schema_value_list = schema_value_list->next; + schema_value = schema_value_list->value; + } + + print_stats_schema_value(mon, schema_value); + + if (stats_value->type == QTYPE_QNUM) { + monitor_printf(mon, ": %" PRId64 "\n", stats_value->u.scalar); + } else if (stats_value->type == QTYPE_QBOOL) { + monitor_printf(mon, ": %s\n", stats_value->u.boolean ? "yes" : "no"); + } else if (stats_value->type == QTYPE_QLIST) { + uint64List *list; + int i; + + monitor_printf(mon, ": "); + for (list = stats_value->u.list, i = 1; + list; + list = list->next, i++) { + monitor_printf(mon, "[%d]=%" PRId64 " ", i, list->value); + } + monitor_printf(mon, "\n"); + } + } +} + +/* Create the StatsFilter that is needed for an "info stats" invocation. */ +static StatsFilter *stats_filter(StatsTarget target, const char *names, + int cpu_index, StatsProvider provider) +{ + StatsFilter *filter = g_malloc0(sizeof(*filter)); + StatsProvider provider_idx; + StatsRequestList *request_list = NULL; + + filter->target = target; + switch (target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + { + strList *vcpu_list = NULL; + CPUState *cpu = qemu_get_cpu(cpu_index); + char *canonical_path = object_get_canonical_path(OBJECT(cpu)); + + QAPI_LIST_PREPEND(vcpu_list, canonical_path); + filter->u.vcpu.has_vcpus = true; + filter->u.vcpu.vcpus = vcpu_list; + break; + } + default: + break; + } + + if (!names && provider == STATS_PROVIDER__MAX) { + return filter; + } + + /* + * "info stats" can only query either one or all the providers. Querying + * by name, but not by provider, requires the creation of one filter per + * provider. + */ + for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) { + if (provider == STATS_PROVIDER__MAX || provider == provider_idx) { + StatsRequest *request = g_new0(StatsRequest, 1); + request->provider = provider_idx; + if (names && !g_str_equal(names, "*")) { + request->has_names = true; + request->names = hmp_split_at_comma(names); + } + QAPI_LIST_PREPEND(request_list, request); + } + } + + filter->has_providers = true; + filter->providers = request_list; + return filter; +} + +void hmp_info_stats(Monitor *mon, const QDict *qdict) +{ + const char *target_str = qdict_get_str(qdict, "target"); + const char *provider_str = qdict_get_try_str(qdict, "provider"); + const char *names = qdict_get_try_str(qdict, "names"); + + StatsProvider provider = STATS_PROVIDER__MAX; + StatsTarget target; + Error *err = NULL; + g_autoptr(StatsSchemaList) schema = NULL; + g_autoptr(StatsResultList) stats = NULL; + g_autoptr(StatsFilter) filter = NULL; + StatsResultList *entry; + + target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats target %s\n", target_str); + goto exit_no_print; + } + if (provider_str) { + provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats provider %s\n", provider_str); + goto exit_no_print; + } + } + + schema = qmp_query_stats_schemas(provider_str ? true : false, + provider, &err); + if (err) { + goto exit; + } + + switch (target) { + case STATS_TARGET_VM: + filter = stats_filter(target, names, -1, provider); + break; + case STATS_TARGET_VCPU: {} + int cpu_index = monitor_get_cpu_index(mon); + filter = stats_filter(target, names, cpu_index, provider); + break; + default: + abort(); + } + + stats = qmp_query_stats(filter, &err); + if (err) { + goto exit; + } + for (entry = stats; entry; entry = entry->next) { + print_stats_results(mon, target, provider_str == NULL, entry->value, schema); + } + +exit: + if (err) { + monitor_printf(mon, "%s\n", error_get_pretty(err)); + } +exit_no_print: + error_free(err); +} diff --git a/stats/stats-qmp-cmds.c b/stats/stats-qmp-cmds.c new file mode 100644 index 0000000000..bc973747fb --- /dev/null +++ b/stats/stats-qmp-cmds.c @@ -0,0 +1,162 @@ +/* + * QMP commands related to stats + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "sysemu/stats.h" +#include "qapi/qapi-commands-stats.h" +#include "qemu/queue.h" +#include "qapi/error.h" + +typedef struct StatsCallbacks { + StatsProvider provider; + StatRetrieveFunc *stats_cb; + SchemaRetrieveFunc *schemas_cb; + QTAILQ_ENTRY(StatsCallbacks) next; +} StatsCallbacks; + +static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks = + QTAILQ_HEAD_INITIALIZER(stats_callbacks); + +void add_stats_callbacks(StatsProvider provider, + StatRetrieveFunc *stats_fn, + SchemaRetrieveFunc *schemas_fn) +{ + StatsCallbacks *entry = g_new(StatsCallbacks, 1); + entry->provider = provider; + entry->stats_cb = stats_fn; + entry->schemas_cb = schemas_fn; + + QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next); +} + +static bool invoke_stats_cb(StatsCallbacks *entry, + StatsResultList **stats_results, + StatsFilter *filter, StatsRequest *request, + Error **errp) +{ + ERRP_GUARD(); + strList *targets = NULL; + strList *names = NULL; + + if (request) { + if (request->provider != entry->provider) { + return true; + } + if (request->has_names && !request->names) { + return true; + } + names = request->has_names ? request->names : NULL; + } + + switch (filter->target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + if (filter->u.vcpu.has_vcpus) { + if (!filter->u.vcpu.vcpus) { + /* No targets allowed? Return no statistics. */ + return true; + } + targets = filter->u.vcpu.vcpus; + } + break; + default: + abort(); + } + + entry->stats_cb(stats_results, filter->target, names, targets, errp); + if (*errp) { + qapi_free_StatsResultList(*stats_results); + *stats_results = NULL; + return false; + } + return true; +} + +StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp) +{ + StatsResultList *stats_results = NULL; + StatsCallbacks *entry; + StatsRequestList *request; + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (filter->has_providers) { + for (request = filter->providers; request; request = request->next) { + if (!invoke_stats_cb(entry, &stats_results, filter, + request->value, errp)) { + break; + } + } + } else { + if (!invoke_stats_cb(entry, &stats_results, filter, NULL, errp)) { + break; + } + } + } + + return stats_results; +} + +StatsSchemaList *qmp_query_stats_schemas(bool has_provider, + StatsProvider provider, + Error **errp) +{ + ERRP_GUARD(); + StatsSchemaList *stats_results = NULL; + StatsCallbacks *entry; + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (!has_provider || provider == entry->provider) { + entry->schemas_cb(&stats_results, errp); + if (*errp) { + qapi_free_StatsSchemaList(stats_results); + return NULL; + } + } + } + + return stats_results; +} + +void add_stats_entry(StatsResultList **stats_results, StatsProvider provider, + const char *qom_path, StatsList *stats_list) +{ + StatsResult *entry = g_new0(StatsResult, 1); + + entry->provider = provider; + entry->qom_path = g_strdup(qom_path); + entry->stats = stats_list; + + QAPI_LIST_PREPEND(*stats_results, entry); +} + +void add_stats_schema(StatsSchemaList **schema_results, + StatsProvider provider, StatsTarget target, + StatsSchemaValueList *stats_list) +{ + StatsSchema *entry = g_new0(StatsSchema, 1); + + entry->provider = provider; + entry->target = target; + entry->stats = stats_list; + QAPI_LIST_PREPEND(*schema_results, entry); +} + +bool apply_str_list_filter(const char *string, strList *list) +{ + strList *str_list = NULL; + + if (!list) { + return true; + } + for (str_list = list; str_list; str_list = str_list->next) { + if (g_str_equal(string, str_list->value)) { + return true; + } + } + return false; +} diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index da19498c66..0e9354faa6 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -299,7 +299,7 @@ static void process_options(int argc, char *argv[], bool pre_init_pass) case OPTION_DAEMONIZE: if (os_set_daemonize(true) < 0) { /* - * --daemonize is parsed before monitor_init_globals_core(), so + * --daemonize is parsed before monitor_init_globals(), so * error_report() does not work yet */ fprintf(stderr, "--daemonize not supported in this build\n"); @@ -411,7 +411,7 @@ int main(int argc, char *argv[]) qemu_add_opts(&qemu_trace_opts); qcrypto_init(&error_fatal); bdrv_init(); - monitor_init_globals_core(); + monitor_init_globals(); init_qmp_commands(); if (!trace_init_backends()) { diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 7e78c2c05c..efcf9181b9 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -224,12 +224,487 @@ typedef enum CPAccessResult { * Access fails and results in an exception syndrome 0x0 ("uncategorized"). * Note that this is not a catch-all case -- the set of cases which may * result in this failure is specifically defined by the architecture. + * This trap is always to the usual target EL, never directly to a + * specified target EL. */ CP_ACCESS_TRAP_UNCATEGORIZED = (2 << 2), - CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = CP_ACCESS_TRAP_UNCATEGORIZED | 2, - CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = CP_ACCESS_TRAP_UNCATEGORIZED | 3, } CPAccessResult; +/* Indexes into fgt_read[] */ +#define FGTREG_HFGRTR 0 +#define FGTREG_HDFGRTR 1 +/* Indexes into fgt_write[] */ +#define FGTREG_HFGWTR 0 +#define FGTREG_HDFGWTR 1 +/* Indexes into fgt_exec[] */ +#define FGTREG_HFGITR 0 + +FIELD(HFGRTR_EL2, AFSR0_EL1, 0, 1) +FIELD(HFGRTR_EL2, AFSR1_EL1, 1, 1) +FIELD(HFGRTR_EL2, AIDR_EL1, 2, 1) +FIELD(HFGRTR_EL2, AMAIR_EL1, 3, 1) +FIELD(HFGRTR_EL2, APDAKEY, 4, 1) +FIELD(HFGRTR_EL2, APDBKEY, 5, 1) +FIELD(HFGRTR_EL2, APGAKEY, 6, 1) +FIELD(HFGRTR_EL2, APIAKEY, 7, 1) +FIELD(HFGRTR_EL2, APIBKEY, 8, 1) +FIELD(HFGRTR_EL2, CCSIDR_EL1, 9, 1) +FIELD(HFGRTR_EL2, CLIDR_EL1, 10, 1) +FIELD(HFGRTR_EL2, CONTEXTIDR_EL1, 11, 1) +FIELD(HFGRTR_EL2, CPACR_EL1, 12, 1) +FIELD(HFGRTR_EL2, CSSELR_EL1, 13, 1) +FIELD(HFGRTR_EL2, CTR_EL0, 14, 1) +FIELD(HFGRTR_EL2, DCZID_EL0, 15, 1) +FIELD(HFGRTR_EL2, ESR_EL1, 16, 1) +FIELD(HFGRTR_EL2, FAR_EL1, 17, 1) +FIELD(HFGRTR_EL2, ISR_EL1, 18, 1) +FIELD(HFGRTR_EL2, LORC_EL1, 19, 1) +FIELD(HFGRTR_EL2, LOREA_EL1, 20, 1) +FIELD(HFGRTR_EL2, LORID_EL1, 21, 1) +FIELD(HFGRTR_EL2, LORN_EL1, 22, 1) +FIELD(HFGRTR_EL2, LORSA_EL1, 23, 1) +FIELD(HFGRTR_EL2, MAIR_EL1, 24, 1) +FIELD(HFGRTR_EL2, MIDR_EL1, 25, 1) +FIELD(HFGRTR_EL2, MPIDR_EL1, 26, 1) +FIELD(HFGRTR_EL2, PAR_EL1, 27, 1) +FIELD(HFGRTR_EL2, REVIDR_EL1, 28, 1) +FIELD(HFGRTR_EL2, SCTLR_EL1, 29, 1) +FIELD(HFGRTR_EL2, SCXTNUM_EL1, 30, 1) +FIELD(HFGRTR_EL2, SCXTNUM_EL0, 31, 1) +FIELD(HFGRTR_EL2, TCR_EL1, 32, 1) +FIELD(HFGRTR_EL2, TPIDR_EL1, 33, 1) +FIELD(HFGRTR_EL2, TPIDRRO_EL0, 34, 1) +FIELD(HFGRTR_EL2, TPIDR_EL0, 35, 1) +FIELD(HFGRTR_EL2, TTBR0_EL1, 36, 1) +FIELD(HFGRTR_EL2, TTBR1_EL1, 37, 1) +FIELD(HFGRTR_EL2, VBAR_EL1, 38, 1) +FIELD(HFGRTR_EL2, ICC_IGRPENN_EL1, 39, 1) +FIELD(HFGRTR_EL2, ERRIDR_EL1, 40, 1) +FIELD(HFGRTR_EL2, ERRSELR_EL1, 41, 1) +FIELD(HFGRTR_EL2, ERXFR_EL1, 42, 1) +FIELD(HFGRTR_EL2, ERXCTLR_EL1, 43, 1) +FIELD(HFGRTR_EL2, ERXSTATUS_EL1, 44, 1) +FIELD(HFGRTR_EL2, ERXMISCN_EL1, 45, 1) +FIELD(HFGRTR_EL2, ERXPFGF_EL1, 46, 1) +FIELD(HFGRTR_EL2, ERXPFGCTL_EL1, 47, 1) +FIELD(HFGRTR_EL2, ERXPFGCDN_EL1, 48, 1) +FIELD(HFGRTR_EL2, ERXADDR_EL1, 49, 1) +FIELD(HFGRTR_EL2, NACCDATA_EL1, 50, 1) +/* 51-53: RES0 */ +FIELD(HFGRTR_EL2, NSMPRI_EL1, 54, 1) +FIELD(HFGRTR_EL2, NTPIDR2_EL0, 55, 1) +/* 56-63: RES0 */ + +/* These match HFGRTR but bits for RO registers are RES0 */ +FIELD(HFGWTR_EL2, AFSR0_EL1, 0, 1) +FIELD(HFGWTR_EL2, AFSR1_EL1, 1, 1) +FIELD(HFGWTR_EL2, AMAIR_EL1, 3, 1) +FIELD(HFGWTR_EL2, APDAKEY, 4, 1) +FIELD(HFGWTR_EL2, APDBKEY, 5, 1) +FIELD(HFGWTR_EL2, APGAKEY, 6, 1) +FIELD(HFGWTR_EL2, APIAKEY, 7, 1) +FIELD(HFGWTR_EL2, APIBKEY, 8, 1) +FIELD(HFGWTR_EL2, CONTEXTIDR_EL1, 11, 1) +FIELD(HFGWTR_EL2, CPACR_EL1, 12, 1) +FIELD(HFGWTR_EL2, CSSELR_EL1, 13, 1) +FIELD(HFGWTR_EL2, ESR_EL1, 16, 1) +FIELD(HFGWTR_EL2, FAR_EL1, 17, 1) +FIELD(HFGWTR_EL2, LORC_EL1, 19, 1) +FIELD(HFGWTR_EL2, LOREA_EL1, 20, 1) +FIELD(HFGWTR_EL2, LORN_EL1, 22, 1) +FIELD(HFGWTR_EL2, LORSA_EL1, 23, 1) +FIELD(HFGWTR_EL2, MAIR_EL1, 24, 1) +FIELD(HFGWTR_EL2, PAR_EL1, 27, 1) +FIELD(HFGWTR_EL2, SCTLR_EL1, 29, 1) +FIELD(HFGWTR_EL2, SCXTNUM_EL1, 30, 1) +FIELD(HFGWTR_EL2, SCXTNUM_EL0, 31, 1) +FIELD(HFGWTR_EL2, TCR_EL1, 32, 1) +FIELD(HFGWTR_EL2, TPIDR_EL1, 33, 1) +FIELD(HFGWTR_EL2, TPIDRRO_EL0, 34, 1) +FIELD(HFGWTR_EL2, TPIDR_EL0, 35, 1) +FIELD(HFGWTR_EL2, TTBR0_EL1, 36, 1) +FIELD(HFGWTR_EL2, TTBR1_EL1, 37, 1) +FIELD(HFGWTR_EL2, VBAR_EL1, 38, 1) +FIELD(HFGWTR_EL2, ICC_IGRPENN_EL1, 39, 1) +FIELD(HFGWTR_EL2, ERRSELR_EL1, 41, 1) +FIELD(HFGWTR_EL2, ERXCTLR_EL1, 43, 1) +FIELD(HFGWTR_EL2, ERXSTATUS_EL1, 44, 1) +FIELD(HFGWTR_EL2, ERXMISCN_EL1, 45, 1) +FIELD(HFGWTR_EL2, ERXPFGCTL_EL1, 47, 1) +FIELD(HFGWTR_EL2, ERXPFGCDN_EL1, 48, 1) +FIELD(HFGWTR_EL2, ERXADDR_EL1, 49, 1) +FIELD(HFGWTR_EL2, NACCDATA_EL1, 50, 1) +FIELD(HFGWTR_EL2, NSMPRI_EL1, 54, 1) +FIELD(HFGWTR_EL2, NTPIDR2_EL0, 55, 1) + +FIELD(HFGITR_EL2, ICIALLUIS, 0, 1) +FIELD(HFGITR_EL2, ICIALLU, 1, 1) +FIELD(HFGITR_EL2, ICIVAU, 2, 1) +FIELD(HFGITR_EL2, DCIVAC, 3, 1) +FIELD(HFGITR_EL2, DCISW, 4, 1) +FIELD(HFGITR_EL2, DCCSW, 5, 1) +FIELD(HFGITR_EL2, DCCISW, 6, 1) +FIELD(HFGITR_EL2, DCCVAU, 7, 1) +FIELD(HFGITR_EL2, DCCVAP, 8, 1) +FIELD(HFGITR_EL2, DCCVADP, 9, 1) +FIELD(HFGITR_EL2, DCCIVAC, 10, 1) +FIELD(HFGITR_EL2, DCZVA, 11, 1) +FIELD(HFGITR_EL2, ATS1E1R, 12, 1) +FIELD(HFGITR_EL2, ATS1E1W, 13, 1) +FIELD(HFGITR_EL2, ATS1E0R, 14, 1) +FIELD(HFGITR_EL2, ATS1E0W, 15, 1) +FIELD(HFGITR_EL2, ATS1E1RP, 16, 1) +FIELD(HFGITR_EL2, ATS1E1WP, 17, 1) +FIELD(HFGITR_EL2, TLBIVMALLE1OS, 18, 1) +FIELD(HFGITR_EL2, TLBIVAE1OS, 19, 1) +FIELD(HFGITR_EL2, TLBIASIDE1OS, 20, 1) +FIELD(HFGITR_EL2, TLBIVAAE1OS, 21, 1) +FIELD(HFGITR_EL2, TLBIVALE1OS, 22, 1) +FIELD(HFGITR_EL2, TLBIVAALE1OS, 23, 1) +FIELD(HFGITR_EL2, TLBIRVAE1OS, 24, 1) +FIELD(HFGITR_EL2, TLBIRVAAE1OS, 25, 1) +FIELD(HFGITR_EL2, TLBIRVALE1OS, 26, 1) +FIELD(HFGITR_EL2, TLBIRVAALE1OS, 27, 1) +FIELD(HFGITR_EL2, TLBIVMALLE1IS, 28, 1) +FIELD(HFGITR_EL2, TLBIVAE1IS, 29, 1) +FIELD(HFGITR_EL2, TLBIASIDE1IS, 30, 1) +FIELD(HFGITR_EL2, TLBIVAAE1IS, 31, 1) +FIELD(HFGITR_EL2, TLBIVALE1IS, 32, 1) +FIELD(HFGITR_EL2, TLBIVAALE1IS, 33, 1) +FIELD(HFGITR_EL2, TLBIRVAE1IS, 34, 1) +FIELD(HFGITR_EL2, TLBIRVAAE1IS, 35, 1) +FIELD(HFGITR_EL2, TLBIRVALE1IS, 36, 1) +FIELD(HFGITR_EL2, TLBIRVAALE1IS, 37, 1) +FIELD(HFGITR_EL2, TLBIRVAE1, 38, 1) +FIELD(HFGITR_EL2, TLBIRVAAE1, 39, 1) +FIELD(HFGITR_EL2, TLBIRVALE1, 40, 1) +FIELD(HFGITR_EL2, TLBIRVAALE1, 41, 1) +FIELD(HFGITR_EL2, TLBIVMALLE1, 42, 1) +FIELD(HFGITR_EL2, TLBIVAE1, 43, 1) +FIELD(HFGITR_EL2, TLBIASIDE1, 44, 1) +FIELD(HFGITR_EL2, TLBIVAAE1, 45, 1) +FIELD(HFGITR_EL2, TLBIVALE1, 46, 1) +FIELD(HFGITR_EL2, TLBIVAALE1, 47, 1) +FIELD(HFGITR_EL2, CFPRCTX, 48, 1) +FIELD(HFGITR_EL2, DVPRCTX, 49, 1) +FIELD(HFGITR_EL2, CPPRCTX, 50, 1) +FIELD(HFGITR_EL2, ERET, 51, 1) +FIELD(HFGITR_EL2, SVC_EL0, 52, 1) +FIELD(HFGITR_EL2, SVC_EL1, 53, 1) +FIELD(HFGITR_EL2, DCCVAC, 54, 1) +FIELD(HFGITR_EL2, NBRBINJ, 55, 1) +FIELD(HFGITR_EL2, NBRBIALL, 56, 1) + +FIELD(HDFGRTR_EL2, DBGBCRN_EL1, 0, 1) +FIELD(HDFGRTR_EL2, DBGBVRN_EL1, 1, 1) +FIELD(HDFGRTR_EL2, DBGWCRN_EL1, 2, 1) +FIELD(HDFGRTR_EL2, DBGWVRN_EL1, 3, 1) +FIELD(HDFGRTR_EL2, MDSCR_EL1, 4, 1) +FIELD(HDFGRTR_EL2, DBGCLAIM, 5, 1) +FIELD(HDFGRTR_EL2, DBGAUTHSTATUS_EL1, 6, 1) +FIELD(HDFGRTR_EL2, DBGPRCR_EL1, 7, 1) +/* 8: RES0: OSLAR_EL1 is WO */ +FIELD(HDFGRTR_EL2, OSLSR_EL1, 9, 1) +FIELD(HDFGRTR_EL2, OSECCR_EL1, 10, 1) +FIELD(HDFGRTR_EL2, OSDLR_EL1, 11, 1) +FIELD(HDFGRTR_EL2, PMEVCNTRN_EL0, 12, 1) +FIELD(HDFGRTR_EL2, PMEVTYPERN_EL0, 13, 1) +FIELD(HDFGRTR_EL2, PMCCFILTR_EL0, 14, 1) +FIELD(HDFGRTR_EL2, PMCCNTR_EL0, 15, 1) +FIELD(HDFGRTR_EL2, PMCNTEN, 16, 1) +FIELD(HDFGRTR_EL2, PMINTEN, 17, 1) +FIELD(HDFGRTR_EL2, PMOVS, 18, 1) +FIELD(HDFGRTR_EL2, PMSELR_EL0, 19, 1) +/* 20: RES0: PMSWINC_EL0 is WO */ +/* 21: RES0: PMCR_EL0 is WO */ +FIELD(HDFGRTR_EL2, PMMIR_EL1, 22, 1) +FIELD(HDFGRTR_EL2, PMBLIMITR_EL1, 23, 1) +FIELD(HDFGRTR_EL2, PMBPTR_EL1, 24, 1) +FIELD(HDFGRTR_EL2, PMBSR_EL1, 25, 1) +FIELD(HDFGRTR_EL2, PMSCR_EL1, 26, 1) +FIELD(HDFGRTR_EL2, PMSEVFR_EL1, 27, 1) +FIELD(HDFGRTR_EL2, PMSFCR_EL1, 28, 1) +FIELD(HDFGRTR_EL2, PMSICR_EL1, 29, 1) +FIELD(HDFGRTR_EL2, PMSIDR_EL1, 30, 1) +FIELD(HDFGRTR_EL2, PMSIRR_EL1, 31, 1) +FIELD(HDFGRTR_EL2, PMSLATFR_EL1, 32, 1) +FIELD(HDFGRTR_EL2, TRC, 33, 1) +FIELD(HDFGRTR_EL2, TRCAUTHSTATUS, 34, 1) +FIELD(HDFGRTR_EL2, TRCAUXCTLR, 35, 1) +FIELD(HDFGRTR_EL2, TRCCLAIM, 36, 1) +FIELD(HDFGRTR_EL2, TRCCNTVRn, 37, 1) +/* 38, 39: RES0 */ +FIELD(HDFGRTR_EL2, TRCID, 40, 1) +FIELD(HDFGRTR_EL2, TRCIMSPECN, 41, 1) +/* 42: RES0: TRCOSLAR is WO */ +FIELD(HDFGRTR_EL2, TRCOSLSR, 43, 1) +FIELD(HDFGRTR_EL2, TRCPRGCTLR, 44, 1) +FIELD(HDFGRTR_EL2, TRCSEQSTR, 45, 1) +FIELD(HDFGRTR_EL2, TRCSSCSRN, 46, 1) +FIELD(HDFGRTR_EL2, TRCSTATR, 47, 1) +FIELD(HDFGRTR_EL2, TRCVICTLR, 48, 1) +/* 49: RES0: TRFCR_EL1 is WO */ +FIELD(HDFGRTR_EL2, TRBBASER_EL1, 50, 1) +FIELD(HDFGRTR_EL2, TRBIDR_EL1, 51, 1) +FIELD(HDFGRTR_EL2, TRBLIMITR_EL1, 52, 1) +FIELD(HDFGRTR_EL2, TRBMAR_EL1, 53, 1) +FIELD(HDFGRTR_EL2, TRBPTR_EL1, 54, 1) +FIELD(HDFGRTR_EL2, TRBSR_EL1, 55, 1) +FIELD(HDFGRTR_EL2, TRBTRG_EL1, 56, 1) +FIELD(HDFGRTR_EL2, PMUSERENR_EL0, 57, 1) +FIELD(HDFGRTR_EL2, PMCEIDN_EL0, 58, 1) +FIELD(HDFGRTR_EL2, NBRBIDR, 59, 1) +FIELD(HDFGRTR_EL2, NBRBCTL, 60, 1) +FIELD(HDFGRTR_EL2, NBRBDATA, 61, 1) +FIELD(HDFGRTR_EL2, NPMSNEVFR_EL1, 62, 1) +FIELD(HDFGRTR_EL2, PMBIDR_EL1, 63, 1) + +/* + * These match HDFGRTR_EL2, but bits for RO registers are RES0. + * A few bits are for WO registers, where the HDFGRTR_EL2 bit is RES0. + */ +FIELD(HDFGWTR_EL2, DBGBCRN_EL1, 0, 1) +FIELD(HDFGWTR_EL2, DBGBVRN_EL1, 1, 1) +FIELD(HDFGWTR_EL2, DBGWCRN_EL1, 2, 1) +FIELD(HDFGWTR_EL2, DBGWVRN_EL1, 3, 1) +FIELD(HDFGWTR_EL2, MDSCR_EL1, 4, 1) +FIELD(HDFGWTR_EL2, DBGCLAIM, 5, 1) +FIELD(HDFGWTR_EL2, DBGPRCR_EL1, 7, 1) +FIELD(HDFGWTR_EL2, OSLAR_EL1, 8, 1) +FIELD(HDFGWTR_EL2, OSLSR_EL1, 9, 1) +FIELD(HDFGWTR_EL2, OSECCR_EL1, 10, 1) +FIELD(HDFGWTR_EL2, OSDLR_EL1, 11, 1) +FIELD(HDFGWTR_EL2, PMEVCNTRN_EL0, 12, 1) +FIELD(HDFGWTR_EL2, PMEVTYPERN_EL0, 13, 1) +FIELD(HDFGWTR_EL2, PMCCFILTR_EL0, 14, 1) +FIELD(HDFGWTR_EL2, PMCCNTR_EL0, 15, 1) +FIELD(HDFGWTR_EL2, PMCNTEN, 16, 1) +FIELD(HDFGWTR_EL2, PMINTEN, 17, 1) +FIELD(HDFGWTR_EL2, PMOVS, 18, 1) +FIELD(HDFGWTR_EL2, PMSELR_EL0, 19, 1) +FIELD(HDFGWTR_EL2, PMSWINC_EL0, 20, 1) +FIELD(HDFGWTR_EL2, PMCR_EL0, 21, 1) +FIELD(HDFGWTR_EL2, PMBLIMITR_EL1, 23, 1) +FIELD(HDFGWTR_EL2, PMBPTR_EL1, 24, 1) +FIELD(HDFGWTR_EL2, PMBSR_EL1, 25, 1) +FIELD(HDFGWTR_EL2, PMSCR_EL1, 26, 1) +FIELD(HDFGWTR_EL2, PMSEVFR_EL1, 27, 1) +FIELD(HDFGWTR_EL2, PMSFCR_EL1, 28, 1) +FIELD(HDFGWTR_EL2, PMSICR_EL1, 29, 1) +FIELD(HDFGWTR_EL2, PMSIRR_EL1, 31, 1) +FIELD(HDFGWTR_EL2, PMSLATFR_EL1, 32, 1) +FIELD(HDFGWTR_EL2, TRC, 33, 1) +FIELD(HDFGWTR_EL2, TRCAUXCTLR, 35, 1) +FIELD(HDFGWTR_EL2, TRCCLAIM, 36, 1) +FIELD(HDFGWTR_EL2, TRCCNTVRn, 37, 1) +FIELD(HDFGWTR_EL2, TRCIMSPECN, 41, 1) +FIELD(HDFGWTR_EL2, TRCOSLAR, 42, 1) +FIELD(HDFGWTR_EL2, TRCPRGCTLR, 44, 1) +FIELD(HDFGWTR_EL2, TRCSEQSTR, 45, 1) +FIELD(HDFGWTR_EL2, TRCSSCSRN, 46, 1) +FIELD(HDFGWTR_EL2, TRCVICTLR, 48, 1) +FIELD(HDFGWTR_EL2, TRFCR_EL1, 49, 1) +FIELD(HDFGWTR_EL2, TRBBASER_EL1, 50, 1) +FIELD(HDFGWTR_EL2, TRBLIMITR_EL1, 52, 1) +FIELD(HDFGWTR_EL2, TRBMAR_EL1, 53, 1) +FIELD(HDFGWTR_EL2, TRBPTR_EL1, 54, 1) +FIELD(HDFGWTR_EL2, TRBSR_EL1, 55, 1) +FIELD(HDFGWTR_EL2, TRBTRG_EL1, 56, 1) +FIELD(HDFGWTR_EL2, PMUSERENR_EL0, 57, 1) +FIELD(HDFGWTR_EL2, NBRBCTL, 60, 1) +FIELD(HDFGWTR_EL2, NBRBDATA, 61, 1) +FIELD(HDFGWTR_EL2, NPMSNEVFR_EL1, 62, 1) + +/* Which fine-grained trap bit register to check, if any */ +FIELD(FGT, TYPE, 10, 3) +FIELD(FGT, REV, 9, 1) /* Is bit sense reversed? */ +FIELD(FGT, IDX, 6, 3) /* Index within a uint64_t[] array */ +FIELD(FGT, BITPOS, 0, 6) /* Bit position within the uint64_t */ + +/* + * Macros to define FGT_##bitname enum constants to use in ARMCPRegInfo::fgt + * fields. We assume for brevity's sake that there are no duplicated + * bit names across the various FGT registers. + */ +#define DO_BIT(REG, BITNAME) \ + FGT_##BITNAME = FGT_##REG | R_##REG##_EL2_##BITNAME##_SHIFT + +/* Some bits have reversed sense, so 0 means trap and 1 means not */ +#define DO_REV_BIT(REG, BITNAME) \ + FGT_##BITNAME = FGT_##REG | FGT_REV | R_##REG##_EL2_##BITNAME##_SHIFT + +typedef enum FGTBit { + /* + * These bits tell us which register arrays to use: + * if FGT_R is set then reads are checked against fgt_read[]; + * if FGT_W is set then writes are checked against fgt_write[]; + * if FGT_EXEC is set then all accesses are checked against fgt_exec[]. + * + * For almost all bits in the R/W register pairs, the bit exists in + * both registers for a RW register, in HFGRTR/HDFGRTR for a RO register + * with the corresponding HFGWTR/HDFGTWTR bit being RES0, and vice-versa + * for a WO register. There are unfortunately a couple of exceptions + * (PMCR_EL0, TRFCR_EL1) where the register being trapped is RW but + * the FGT system only allows trapping of writes, not reads. + * + * Note that we arrange these bits so that a 0 FGTBit means "no trap". + */ + FGT_R = 1 << R_FGT_TYPE_SHIFT, + FGT_W = 2 << R_FGT_TYPE_SHIFT, + FGT_EXEC = 4 << R_FGT_TYPE_SHIFT, + FGT_RW = FGT_R | FGT_W, + /* Bit to identify whether trap bit is reversed sense */ + FGT_REV = R_FGT_REV_MASK, + + /* + * If a bit exists in HFGRTR/HDFGRTR then either the register being + * trapped is RO or the bit also exists in HFGWTR/HDFGWTR, so we either + * want to trap for both reads and writes or else it's harmless to mark + * it as trap-on-writes. + * If a bit exists only in HFGWTR/HDFGWTR then either the register being + * trapped is WO, or else it is one of the two oddball special cases + * which are RW but have only a write trap. We mark these as only + * FGT_W so we get the right behaviour for those special cases. + * (If a bit was added in future that provided only a read trap for an + * RW register we'd need to do something special to get the FGT_R bit + * only. But this seems unlikely to happen.) + * + * So for the DO_BIT/DO_REV_BIT macros: use FGT_HFGRTR/FGT_HDFGRTR if + * the bit exists in that register. Otherwise use FGT_HFGWTR/FGT_HDFGWTR. + */ + FGT_HFGRTR = FGT_RW | (FGTREG_HFGRTR << R_FGT_IDX_SHIFT), + FGT_HFGWTR = FGT_W | (FGTREG_HFGWTR << R_FGT_IDX_SHIFT), + FGT_HDFGRTR = FGT_RW | (FGTREG_HDFGRTR << R_FGT_IDX_SHIFT), + FGT_HDFGWTR = FGT_W | (FGTREG_HDFGWTR << R_FGT_IDX_SHIFT), + FGT_HFGITR = FGT_EXEC | (FGTREG_HFGITR << R_FGT_IDX_SHIFT), + + /* Trap bits in HFGRTR_EL2 / HFGWTR_EL2, starting from bit 0. */ + DO_BIT(HFGRTR, AFSR0_EL1), + DO_BIT(HFGRTR, AFSR1_EL1), + DO_BIT(HFGRTR, AIDR_EL1), + DO_BIT(HFGRTR, AMAIR_EL1), + DO_BIT(HFGRTR, APDAKEY), + DO_BIT(HFGRTR, APDBKEY), + DO_BIT(HFGRTR, APGAKEY), + DO_BIT(HFGRTR, APIAKEY), + DO_BIT(HFGRTR, APIBKEY), + DO_BIT(HFGRTR, CCSIDR_EL1), + DO_BIT(HFGRTR, CLIDR_EL1), + DO_BIT(HFGRTR, CONTEXTIDR_EL1), + DO_BIT(HFGRTR, CPACR_EL1), + DO_BIT(HFGRTR, CSSELR_EL1), + DO_BIT(HFGRTR, CTR_EL0), + DO_BIT(HFGRTR, DCZID_EL0), + DO_BIT(HFGRTR, ESR_EL1), + DO_BIT(HFGRTR, FAR_EL1), + DO_BIT(HFGRTR, ISR_EL1), + DO_BIT(HFGRTR, LORC_EL1), + DO_BIT(HFGRTR, LOREA_EL1), + DO_BIT(HFGRTR, LORID_EL1), + DO_BIT(HFGRTR, LORN_EL1), + DO_BIT(HFGRTR, LORSA_EL1), + DO_BIT(HFGRTR, MAIR_EL1), + DO_BIT(HFGRTR, MIDR_EL1), + DO_BIT(HFGRTR, MPIDR_EL1), + DO_BIT(HFGRTR, PAR_EL1), + DO_BIT(HFGRTR, REVIDR_EL1), + DO_BIT(HFGRTR, SCTLR_EL1), + DO_BIT(HFGRTR, SCXTNUM_EL1), + DO_BIT(HFGRTR, SCXTNUM_EL0), + DO_BIT(HFGRTR, TCR_EL1), + DO_BIT(HFGRTR, TPIDR_EL1), + DO_BIT(HFGRTR, TPIDRRO_EL0), + DO_BIT(HFGRTR, TPIDR_EL0), + DO_BIT(HFGRTR, TTBR0_EL1), + DO_BIT(HFGRTR, TTBR1_EL1), + DO_BIT(HFGRTR, VBAR_EL1), + DO_BIT(HFGRTR, ICC_IGRPENN_EL1), + DO_BIT(HFGRTR, ERRIDR_EL1), + DO_REV_BIT(HFGRTR, NSMPRI_EL1), + DO_REV_BIT(HFGRTR, NTPIDR2_EL0), + + /* Trap bits in HDFGRTR_EL2 / HDFGWTR_EL2, starting from bit 0. */ + DO_BIT(HDFGRTR, DBGBCRN_EL1), + DO_BIT(HDFGRTR, DBGBVRN_EL1), + DO_BIT(HDFGRTR, DBGWCRN_EL1), + DO_BIT(HDFGRTR, DBGWVRN_EL1), + DO_BIT(HDFGRTR, MDSCR_EL1), + DO_BIT(HDFGRTR, DBGCLAIM), + DO_BIT(HDFGWTR, OSLAR_EL1), + DO_BIT(HDFGRTR, OSLSR_EL1), + DO_BIT(HDFGRTR, OSECCR_EL1), + DO_BIT(HDFGRTR, OSDLR_EL1), + DO_BIT(HDFGRTR, PMEVCNTRN_EL0), + DO_BIT(HDFGRTR, PMEVTYPERN_EL0), + DO_BIT(HDFGRTR, PMCCFILTR_EL0), + DO_BIT(HDFGRTR, PMCCNTR_EL0), + DO_BIT(HDFGRTR, PMCNTEN), + DO_BIT(HDFGRTR, PMINTEN), + DO_BIT(HDFGRTR, PMOVS), + DO_BIT(HDFGRTR, PMSELR_EL0), + DO_BIT(HDFGWTR, PMSWINC_EL0), + DO_BIT(HDFGWTR, PMCR_EL0), + DO_BIT(HDFGRTR, PMMIR_EL1), + DO_BIT(HDFGRTR, PMCEIDN_EL0), + + /* Trap bits in HFGITR_EL2, starting from bit 0 */ + DO_BIT(HFGITR, ICIALLUIS), + DO_BIT(HFGITR, ICIALLU), + DO_BIT(HFGITR, ICIVAU), + DO_BIT(HFGITR, DCIVAC), + DO_BIT(HFGITR, DCISW), + DO_BIT(HFGITR, DCCSW), + DO_BIT(HFGITR, DCCISW), + DO_BIT(HFGITR, DCCVAU), + DO_BIT(HFGITR, DCCVAP), + DO_BIT(HFGITR, DCCVADP), + DO_BIT(HFGITR, DCCIVAC), + DO_BIT(HFGITR, DCZVA), + DO_BIT(HFGITR, ATS1E1R), + DO_BIT(HFGITR, ATS1E1W), + DO_BIT(HFGITR, ATS1E0R), + DO_BIT(HFGITR, ATS1E0W), + DO_BIT(HFGITR, ATS1E1RP), + DO_BIT(HFGITR, ATS1E1WP), + DO_BIT(HFGITR, TLBIVMALLE1OS), + DO_BIT(HFGITR, TLBIVAE1OS), + DO_BIT(HFGITR, TLBIASIDE1OS), + DO_BIT(HFGITR, TLBIVAAE1OS), + DO_BIT(HFGITR, TLBIVALE1OS), + DO_BIT(HFGITR, TLBIVAALE1OS), + DO_BIT(HFGITR, TLBIRVAE1OS), + DO_BIT(HFGITR, TLBIRVAAE1OS), + DO_BIT(HFGITR, TLBIRVALE1OS), + DO_BIT(HFGITR, TLBIRVAALE1OS), + DO_BIT(HFGITR, TLBIVMALLE1IS), + DO_BIT(HFGITR, TLBIVAE1IS), + DO_BIT(HFGITR, TLBIASIDE1IS), + DO_BIT(HFGITR, TLBIVAAE1IS), + DO_BIT(HFGITR, TLBIVALE1IS), + DO_BIT(HFGITR, TLBIVAALE1IS), + DO_BIT(HFGITR, TLBIRVAE1IS), + DO_BIT(HFGITR, TLBIRVAAE1IS), + DO_BIT(HFGITR, TLBIRVALE1IS), + DO_BIT(HFGITR, TLBIRVAALE1IS), + DO_BIT(HFGITR, TLBIRVAE1), + DO_BIT(HFGITR, TLBIRVAAE1), + DO_BIT(HFGITR, TLBIRVALE1), + DO_BIT(HFGITR, TLBIRVAALE1), + DO_BIT(HFGITR, TLBIVMALLE1), + DO_BIT(HFGITR, TLBIVAE1), + DO_BIT(HFGITR, TLBIASIDE1), + DO_BIT(HFGITR, TLBIVAAE1), + DO_BIT(HFGITR, TLBIVALE1), + DO_BIT(HFGITR, TLBIVAALE1), + DO_BIT(HFGITR, CFPRCTX), + DO_BIT(HFGITR, DVPRCTX), + DO_BIT(HFGITR, CPPRCTX), + DO_BIT(HFGITR, DCCVAC), +} FGTBit; + +#undef DO_BIT +#undef DO_REV_BIT + typedef struct ARMCPRegInfo ARMCPRegInfo; /* @@ -285,6 +760,11 @@ struct ARMCPRegInfo { /* Security state: ARM_CP_SECSTATE_* bits/values */ CPSecureState secure; /* + * Which fine-grained trap register bit to check, if any. This + * value encodes both the trap register and bit within it. + */ + FGTBit fgt; + /* * The opaque pointer passed to define_arm_cp_regs_with_opaque() when * this register was defined: can be used to hand data through to the * register read/write functions, since they are passed the ARMCPRegInfo*. diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 8cf70693be..7bc97fece9 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -529,6 +529,16 @@ typedef struct CPUArchState { uint64_t disr_el1; uint64_t vdisr_el2; uint64_t vsesr_el2; + + /* + * Fine-Grained Trap registers. We store these as arrays so the + * access checking code doesn't have to manually select + * HFGRTR_EL2 vs HFDFGRTR_EL2 etc when looking up the bit to test. + * FEAT_FGT2 will add more elements to these arrays. + */ + uint64_t fgt_read[2]; /* HFGRTR, HDFGRTR */ + uint64_t fgt_write[2]; /* HFGWTR, HDFGWTR */ + uint64_t fgt_exec[1]; /* HFGITR */ } cp15; struct { @@ -3160,6 +3170,8 @@ FIELD(TBFLAG_ANY, FPEXC_EL, 8, 2) /* Memory operations require alignment: SCTLR_ELx.A or CCR.UNALIGN_TRP */ FIELD(TBFLAG_ANY, ALIGN_MEM, 10, 1) FIELD(TBFLAG_ANY, PSTATE__IL, 11, 1) +FIELD(TBFLAG_ANY, FGT_ACTIVE, 12, 1) +FIELD(TBFLAG_ANY, FGT_SVC, 13, 1) /* * Bit usage when in AArch32 state, both A- and M-profile. @@ -3234,6 +3246,7 @@ FIELD(TBFLAG_A64, PSTATE_ZA, 23, 1) FIELD(TBFLAG_A64, SVL, 24, 4) /* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */ FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) +FIELD(TBFLAG_A64, FGT_ERET, 29, 1) /* * Helpers for using the above. @@ -4164,6 +4177,11 @@ static inline bool isar_feature_aa64_tgran64_2(const ARMISARegisters *id) return t >= 2 || (t == 0 && isar_feature_aa64_tgran64(id)); } +static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; +} + static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 0e021960fb..4066950da1 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -1224,6 +1224,7 @@ static void aarch64_max_initfn(Object *obj) 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 */ + t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* FEAT_FGT */ cpu->isar.id_aa64mmfr0 = t; t = cpu->isar.id_aa64mmfr1; diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index cced3f168d..3c671c88c1 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -599,6 +599,33 @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +/* + * Check for traps to Debug Comms Channel registers. If FEAT_FGT + * is implemented then these are controlled by MDCR_EL2.TDCC for + * EL2 and MDCR_EL3.TDCC for EL3. They are also controlled by + * the general debug access trap bits MDCR_EL2.TDA and MDCR_EL3.TDA. + */ +static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdcr_el2_tda = (mdcr_el2 & MDCR_TDA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + bool mdcr_el2_tdcc = cpu_isar_feature(aa64_fgt, env_archcpu(env)) && + (mdcr_el2 & MDCR_TDCC); + bool mdcr_el3_tdcc = cpu_isar_feature(aa64_fgt, env_archcpu(env)) && + (env->cp15.mdcr_el3 & MDCR_TDCC); + + if (el < 2 && (mdcr_el2_tda || mdcr_el2_tdcc)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && ((env->cp15.mdcr_el3 & MDCR_TDA) || mdcr_el3_tdcc)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + static void oslar_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -672,6 +699,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_MDSCR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetvalue = 0 }, /* @@ -680,7 +708,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { */ { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 1, .opc2 = 0, - .access = PL0_R, .accessfn = access_tda, + .access = PL0_R, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, /* * OSDTRRX_EL1/OSDTRTX_EL1 are used for save and restore of DBGDTRRX_EL0. @@ -688,11 +716,11 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { */ { .name = "OSDTRRX_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 2, - .access = PL1_RW, .accessfn = access_tda, + .access = PL1_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "OSDTRTX_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, - .access = PL1_RW, .accessfn = access_tda, + .access = PL1_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, /* * OSECCR_EL1 provides a mechanism for an operating system @@ -702,6 +730,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "OSECCR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_OSECCR_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, /* * DBGDSCRint[15,12,5:2] map to MDSCR_EL1[15,12,5:2]. Map all bits as @@ -717,16 +746,19 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, .access = PL1_W, .type = ARM_CP_NO_RAW, .accessfn = access_tdosa, + .fgt = FGT_OSLAR_EL1, .writefn = oslar_write }, { .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4, .access = PL1_R, .resetvalue = 10, .accessfn = access_tdosa, + .fgt = FGT_OSLSR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) }, /* Dummy OSDLR_EL1: 32-bit Linux will read this */ { .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4, .access = PL1_RW, .accessfn = access_tdosa, + .fgt = FGT_OSDLR_EL1, .writefn = osdlr_write, .fieldoffset = offsetof(CPUARMState, cp15.osdlr_el1) }, /* @@ -752,7 +784,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { */ { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, - .access = PL1_RW, .accessfn = access_tda, + .access = PL1_RW, .accessfn = access_tdcc, .type = ARM_CP_NOP }, /* * Dummy DBGCLAIM registers. @@ -763,10 +795,12 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 6, .type = ARM_CP_ALIAS, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGCLAIM, .writefn = dbgclaimset_write, .readfn = dbgclaimset_read }, { .name = "DBGCLAIMCLR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 6, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGCLAIM, .writefn = dbgclaimclr_write, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.dbgclaim) }, }; @@ -1127,12 +1161,14 @@ void define_debug_regs(ARMCPU *cpu) { .name = dbgbvr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGBVRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), .writefn = dbgbvr_write, .raw_writefn = raw_write }, { .name = dbgbcr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGBCRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), .writefn = dbgbcr_write, .raw_writefn = raw_write }, @@ -1149,12 +1185,14 @@ void define_debug_regs(ARMCPU *cpu) { .name = dbgwvr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGWVRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), .writefn = dbgwvr_write, .raw_writefn = raw_write }, { .name = dbgwcr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGWCRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), .writefn = dbgwcr_write, .raw_writefn = raw_write }, diff --git a/target/arm/helper.c b/target/arm/helper.c index 72b37b7cf1..c62ed05c12 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -633,6 +633,7 @@ static const ARMCPRegInfo cp_reginfo[] = { { .name = "CONTEXTIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_CONTEXTIDR_EL1, .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, @@ -868,6 +869,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, + .fgt = FGT_CPACR_EL1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, }; @@ -1869,6 +1871,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_hcx, cpu)) { valid_mask |= SCR_HXEN; } + if (cpu_isar_feature(aa64_fgt, cpu)) { + valid_mask |= SCR_FGTEN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -2030,21 +2035,25 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenset_write, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .raw_writefn = raw_write }, { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, .type = ARM_CP_IO, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 1, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .resetvalue = 0, .writefn = pmcntenset_write, .raw_writefn = raw_write }, { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, .access = PL0_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .writefn = pmcntenclr_write, .type = ARM_CP_ALIAS | ARM_CP_IO }, { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, @@ -2052,41 +2061,49 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .writefn = pmovsr_write, .raw_writefn = raw_write }, { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsr_write, .raw_writefn = raw_write }, { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, .access = PL0_RW, .type = ARM_CP_ALIAS, + .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr), .accessfn = pmreg_access_selr, .writefn = pmselr_write, .raw_writefn = raw_write}, { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5, .access = PL0_RW, .accessfn = pmreg_access_selr, + .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), .writefn = pmselr_write, .raw_writefn = raw_write, }, { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, + .fgt = FGT_PMCCNTR_EL0, .readfn = pmccntr_read, .writefn = pmccntr_write32, .accessfn = pmreg_access_ccntr }, { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, .access = PL0_RW, .accessfn = pmreg_access_ccntr, + .fgt = FGT_PMCCNTR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt), .readfn = pmccntr_read, .writefn = pmccntr_write, @@ -2094,32 +2111,38 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMCCFILTR", .cp = 15, .opc1 = 0, .crn = 14, .crm = 15, .opc2 = 7, .writefn = pmccfiltr_write_a32, .readfn = pmccfiltr_read_a32, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCCFILTR_EL0, .type = ARM_CP_ALIAS | ARM_CP_IO, .resetvalue = 0, }, { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7, .writefn = pmccfiltr_write, .raw_writefn = raw_write, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCCFILTR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0), .resetvalue = 0, }, { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access_xevcntr, + .fgt = FGT_PMEVCNTRN_EL0, .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access_xevcntr, + .fgt = FGT_PMEVCNTRN_EL0, .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, .access = PL0_R | PL1_RW, .accessfn = access_tpm, @@ -2134,6 +2157,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pminten), .resetvalue = 0, @@ -2141,18 +2165,21 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenset_write, .raw_writefn = raw_write, .resetvalue = 0x0 }, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenclr_write, }, { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenclr_write }, @@ -2160,11 +2187,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_R, .accessfn = access_tid4, + .fgt = FGT_CCSIDR_EL1, .readfn = ccsidr_read, .type = ARM_CP_NO_RAW }, { .name = "CSSELR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_tid4, + .fgt = FGT_CSSELR_EL1, .writefn = csselr_write, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.csselr_s), offsetof(CPUARMState, cp15.csselr_ns) } }, @@ -2176,6 +2205,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid1, + .fgt = FGT_AIDR_EL1, .resetvalue = 0 }, /* * Auxiliary fault status registers: these also are IMPDEF, and we @@ -2184,10 +2214,12 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "AFSR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_AFSR0_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_AFSR1_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, /* * MAIR can just read-as-written because we don't implement caches @@ -2196,6 +2228,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "MAIR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_MAIR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), .resetvalue = 0 }, { .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64, @@ -2225,6 +2258,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .resetfn = arm_cp_reset_ignore }, { .name = "ISR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 1, .opc2 = 0, + .fgt = FGT_ISR_EL1, .type = ARM_CP_NO_RAW, .access = PL1_R, .readfn = isr_read }, /* 32 bit ITLB invalidates */ { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, @@ -2281,6 +2315,7 @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = { /* PMOVSSET is not implemented in v7 before v7ve */ { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsset_write, @@ -2288,6 +2323,7 @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = { { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsset_write, @@ -2338,25 +2374,30 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { { .name = "TPIDR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 2, .crn = 13, .crm = 0, .access = PL0_RW, + .fgt = FGT_TPIDR_EL0, .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[0]), .resetvalue = 0 }, { .name = "TPIDRURW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL0_RW, + .fgt = FGT_TPIDR_EL0, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidrurw_s), offsetoflow32(CPUARMState, cp15.tpidrurw_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "TPIDRRO_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 3, .crn = 13, .crm = 0, .access = PL0_R | PL1_W, + .fgt = FGT_TPIDRRO_EL0, .fieldoffset = offsetof(CPUARMState, cp15.tpidrro_el[0]), .resetvalue = 0}, { .name = "TPIDRURO", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 3, .access = PL0_R | PL1_W, + .fgt = FGT_TPIDRRO_EL0, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidruro_s), offsetoflow32(CPUARMState, cp15.tpidruro_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "TPIDR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .opc2 = 4, .crn = 13, .crm = 0, .access = PL1_RW, + .fgt = FGT_TPIDR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[1]), .resetvalue = 0 }, { .name = "TPIDRPRW", .opc1 = 0, .cp = 15, .crn = 13, .crm = 0, .opc2 = 4, .access = PL1_RW, @@ -3284,9 +3325,9 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, if (arm_current_el(env) == 1) { if (arm_is_secure_below_el3(env)) { if (env->cp15.scr_el3 & SCR_EEL2) { - return CP_ACCESS_TRAP_UNCATEGORIZED_EL2; + return CP_ACCESS_TRAP_EL2; } - return CP_ACCESS_TRAP_UNCATEGORIZED_EL3; + return CP_ACCESS_TRAP_EL3; } return CP_ACCESS_TRAP_UNCATEGORIZED; } @@ -4127,6 +4168,7 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_FAR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .resetvalue = 0, }, }; @@ -4135,22 +4177,26 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "ESR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_ESR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, }, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_TTBR0_EL1, .writefn = vmsa_ttbr_write, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) } }, { .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_TTBR1_EL1, .writefn = vmsa_ttbr_write, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) } }, { .name = "TCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_TCR_EL1, .writefn = vmsa_tcr_el12_write, .raw_writefn = raw_write, .resetvalue = 0, @@ -4389,6 +4435,7 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { { .name = "AMAIR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_AMAIR_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, /* AMAIR1 is mapped to AMAIR_EL1[63:32] */ { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, @@ -5206,6 +5253,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "DCZID_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0, .access = PL0_R, .type = ARM_CP_NO_RAW, + .fgt = FGT_DCZID_EL0, .readfn = aa64_dczid_read }, { .name = "DC_ZVA", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 4, .opc2 = 1, @@ -5213,6 +5261,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { #ifndef CONFIG_USER_ONLY /* Avoid overhead of an access check that always passes in user-mode */ .accessfn = aa64_zva_access, + .fgt = FGT_DCZVA, #endif }, { .name = "CURRENTEL", .state = ARM_CP_STATE_AA64, @@ -5222,88 +5271,110 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "IC_IALLUIS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NOP, + .fgt = FGT_ICIALLUIS, .accessfn = access_ticab }, { .name = "IC_IALLU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NOP, + .fgt = FGT_ICIALLU, .accessfn = access_tocu }, { .name = "IC_IVAU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 5, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, + .fgt = FGT_ICIVAU, .accessfn = access_tocu }, { .name = "DC_IVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 1, .access = PL1_W, .accessfn = aa64_cacheop_poc_access, + .fgt = FGT_DCIVAC, .type = ARM_CP_NOP }, { .name = "DC_ISW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 2, + .fgt = FGT_DCISW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, { .name = "DC_CVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, + .fgt = FGT_DCCVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2, + .fgt = FGT_DCCSW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, { .name = "DC_CVAU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 11, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, + .fgt = FGT_DCCVAU, .accessfn = access_tocu }, { .name = "DC_CIVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, + .fgt = FGT_DCCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CISW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, + .fgt = FGT_DCCISW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, /* TLBI operations */ { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1IS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1IS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, @@ -5342,18 +5413,22 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1R, .writefn = ats_write64 }, { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1W, .writefn = ats_write64 }, { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E0R, .writefn = ats_write64 }, { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E0W, .writefn = ats_write64 }, { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, @@ -5384,6 +5459,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 7, .crm = 4, .opc2 = 0, .access = PL1_RW, .resetvalue = 0, + .fgt = FGT_PAR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.par_el[1]), .writefn = par_write }, #endif @@ -6465,6 +6541,10 @@ static void disr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t val) * ERRSELR_EL1 * may generate UNDEFINED, which is the effect we get by not * listing them at all. + * + * These registers have fine-grained trap bits, but UNDEF-to-EL1 + * is higher priority than FGT-to-EL2 so we do not need to list them + * in order to check for an FGT. */ static const ARMCPRegInfo minimal_ras_reginfo[] = { { .name = "DISR_EL1", .state = ARM_CP_STATE_BOTH, @@ -6474,6 +6554,7 @@ static const ARMCPRegInfo minimal_ras_reginfo[] = { { .name = "ERRIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 3, .opc2 = 0, .access = PL1_R, .accessfn = access_terr, + .fgt = FGT_ERRIDR_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "VDISR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 1, .opc2 = 1, @@ -6796,6 +6877,7 @@ static const ARMCPRegInfo sme_reginfo[] = { { .name = "TPIDR2_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 5, .access = PL0_RW, .accessfn = access_tpidr2, + .fgt = FGT_NTPIDR2_EL0, .fieldoffset = offsetof(CPUARMState, cp15.tpidr2_el0) }, { .name = "SVCR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 4, .crm = 2, .opc2 = 2, @@ -6833,6 +6915,7 @@ static const ARMCPRegInfo sme_reginfo[] = { { .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 4, .access = PL1_RW, .accessfn = access_esm, + .fgt = FGT_NSMPRI_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "SMPRIMAP_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 5, @@ -6852,6 +6935,7 @@ static void define_pmu_regs(ARMCPU *cpu) ARMCPRegInfo pmcr = { .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, .access = PL0_RW, + .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), .accessfn = pmreg_access, .writefn = pmcr_write, @@ -6861,6 +6945,7 @@ static void define_pmu_regs(ARMCPU *cpu) .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), .resetvalue = cpu->isar.reset_pmcr_el0, @@ -6878,23 +6963,27 @@ static void define_pmu_regs(ARMCPU *cpu) { .name = pmevcntr_name, .cp = 15, .crn = 14, .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .fgt = FGT_PMEVCNTRN_EL0, .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, .accessfn = pmreg_access_xevcntr }, { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access_xevcntr, .type = ARM_CP_IO, + .fgt = FGT_PMEVCNTRN_EL0, .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, .raw_readfn = pmevcntr_rawread, .raw_writefn = pmevcntr_rawwrite }, { .name = pmevtyper_name, .cp = 15, .crn = 14, .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .fgt = FGT_PMEVTYPERN_EL0, .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, .accessfn = pmreg_access }, { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 12 | (3 & (i >> 3)), .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, .type = ARM_CP_IO, .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, .raw_writefn = pmevtyper_rawwrite }, @@ -6910,10 +6999,12 @@ static void define_pmu_regs(ARMCPU *cpu) { .name = "PMCEID2", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid0, 32, 32) }, { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid1, 32, 32) }, }; define_arm_cp_regs(cpu, v81_pmu_regs); @@ -6923,6 +7014,7 @@ static void define_pmu_regs(ARMCPU *cpu) .name = "PMMIR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 6, .access = PL1_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMMIR_EL1, .resetvalue = 0 }; define_one_arm_cp_reg(cpu, &v84_pmmir); @@ -6996,22 +7088,27 @@ static const ARMCPRegInfo lor_reginfo[] = { { .name = "LORSA_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 0, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LORSA_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LOREA_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 1, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LOREA_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LORN_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 2, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LORN_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LORC_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 3, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LORC_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LORID_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 7, .access = PL1_R, .accessfn = access_lor_ns, + .fgt = FGT_LORID_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, }; @@ -7038,42 +7135,52 @@ static const ARMCPRegInfo pauth_reginfo[] = { { .name = "APDAKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDAKEY, .fieldoffset = offsetof(CPUARMState, keys.apda.lo) }, { .name = "APDAKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 1, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDAKEY, .fieldoffset = offsetof(CPUARMState, keys.apda.hi) }, { .name = "APDBKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 2, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDBKEY, .fieldoffset = offsetof(CPUARMState, keys.apdb.lo) }, { .name = "APDBKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 3, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDBKEY, .fieldoffset = offsetof(CPUARMState, keys.apdb.hi) }, { .name = "APGAKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 0, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APGAKEY, .fieldoffset = offsetof(CPUARMState, keys.apga.lo) }, { .name = "APGAKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 1, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APGAKEY, .fieldoffset = offsetof(CPUARMState, keys.apga.hi) }, { .name = "APIAKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 0, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIAKEY, .fieldoffset = offsetof(CPUARMState, keys.apia.lo) }, { .name = "APIAKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 1, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIAKEY, .fieldoffset = offsetof(CPUARMState, keys.apia.hi) }, { .name = "APIBKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 2, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIBKEY, .fieldoffset = offsetof(CPUARMState, keys.apib.lo) }, { .name = "APIBKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 3, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIBKEY, .fieldoffset = offsetof(CPUARMState, keys.apib.hi) }, }; @@ -7081,50 +7188,62 @@ static const ARMCPRegInfo tlbirange_reginfo[] = { { .name = "TLBI_RVAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 1, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, @@ -7196,26 +7315,32 @@ static const ARMCPRegInfo tlbios_reginfo[] = { { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1OS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 1, + .fgt = FGT_TLBIVAE1OS, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 2, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1OS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ALLE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 0, @@ -7333,6 +7458,7 @@ static const ARMCPRegInfo dcpop_reg[] = { { .name = "DC_CVAP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, + .fgt = FGT_DCCVAP, .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, }; @@ -7340,6 +7466,7 @@ static const ARMCPRegInfo dcpodp_reg[] = { { .name = "DC_CVADP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, + .fgt = FGT_DCCVADP, .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, }; #endif /*CONFIG_USER_ONLY*/ @@ -7419,28 +7546,36 @@ static const ARMCPRegInfo mte_reginfo[] = { { .name = "DC_IGVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 3, .type = ARM_CP_NOP, .access = PL1_W, + .fgt = FGT_DCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_IGSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 4, + .fgt = FGT_DCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_IGDVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 5, .type = ARM_CP_NOP, .access = PL1_W, + .fgt = FGT_DCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_IGDSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 6, + .fgt = FGT_DCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CGSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 4, + .fgt = FGT_DCCSW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CGDSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 6, + .fgt = FGT_DCCSW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CIGSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 4, + .fgt = FGT_DCCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CIGDSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 6, + .fgt = FGT_DCCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, }; @@ -7454,34 +7589,42 @@ static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { { .name = "DC_CGVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGDVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGVAP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGDVAP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGVADP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVADP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGDVADP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVADP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CIGVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CIGDVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_GVA", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 4, .opc2 = 3, @@ -7489,6 +7632,7 @@ static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { #ifndef CONFIG_USER_ONLY /* Avoid overhead of an access check that always passes in user-mode */ .accessfn = aa64_zva_access, + .fgt = FGT_DCZVA, #endif }, { .name = "DC_GZVA", .state = ARM_CP_STATE_AA64, @@ -7497,6 +7641,7 @@ static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { #ifndef CONFIG_USER_ONLY /* Avoid overhead of an access check that always passes in user-mode */ .accessfn = aa64_zva_access, + .fgt = FGT_DCZVA, #endif }, }; @@ -7532,10 +7677,12 @@ static const ARMCPRegInfo scxtnum_reginfo[] = { { .name = "SCXTNUM_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 7, .access = PL0_RW, .accessfn = access_scxtnum, + .fgt = FGT_SCXTNUM_EL0, .fieldoffset = offsetof(CPUARMState, scxtnum_el[0]) }, { .name = "SCXTNUM_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 7, .access = PL1_RW, .accessfn = access_scxtnum, + .fgt = FGT_SCXTNUM_EL1, .fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) }, { .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 7, @@ -7546,6 +7693,39 @@ static const ARMCPRegInfo scxtnum_reginfo[] = { .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, scxtnum_el[3]) }, }; + +static CPAccessResult access_fgt(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 2 && + arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_FGTEN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo fgt_reginfo[] = { + { .name = "HFGRTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HFGRTR]) }, + { .name = "HFGWTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 5, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HFGWTR]) }, + { .name = "HDFGRTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 4, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HDFGRTR]) }, + { .name = "HDFGWTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 5, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HDFGWTR]) }, + { .name = "HFGITR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 6, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_exec[FGTREG_HFGITR]) }, +}; #endif /* TARGET_AARCH64 */ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, @@ -7570,24 +7750,30 @@ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo predinv_reginfo[] = { { .name = "CFP_RCTX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 4, + .fgt = FGT_CFPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "DVP_RCTX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 5, + .fgt = FGT_DVPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "CPP_RCTX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 7, + .fgt = FGT_CPPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, /* * Note the AArch32 opcodes have a different OPC1. */ { .name = "CFPRCTX", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 4, + .fgt = FGT_CFPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "DVPRCTX", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 5, + .fgt = FGT_DVPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "CPPRCTX", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 7, + .fgt = FGT_CPPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, }; @@ -7734,13 +7920,15 @@ static const ARMCPRegInfo vhe_reginfo[] = { #ifndef CONFIG_USER_ONLY static const ARMCPRegInfo ats1e1_reginfo[] = { - { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, + { .name = "AT_S1E1RP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1RP, .writefn = ats_write64 }, - { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, + { .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1WP, .writefn = ats_write64 }, }; @@ -7904,6 +8092,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_tid4, + .fgt = FGT_CLIDR_EL1, .resetvalue = cpu->clidr }; define_one_arm_cp_reg(cpu, &clidr); @@ -8168,18 +8357,22 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "PMCEID0", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid0, 0, 32) }, { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = cpu->pmceid0 }, { .name = "PMCEID1", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid1, 0, 32) }, { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = cpu->pmceid1 }, }; #ifdef CONFIG_USER_ONLY @@ -8540,6 +8733,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_NO_RAW, .resetvalue = cpu->midr, + .fgt = FGT_MIDR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), .readfn = midr_read }, /* crn = 0 op1 = 0 crm = 0 op2 = 7 : AArch32 aliases of MIDR */ @@ -8550,6 +8744,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6, .access = PL1_R, .accessfn = access_aa64_tid1, + .fgt = FGT_REVIDR_EL1, .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, }; ARMCPRegInfo id_v8_midr_alias_cp_reginfo = { @@ -8566,6 +8761,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0, .access = PL0_R, .accessfn = ctr_el0_access, + .fgt = FGT_CTR_EL0, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, /* TCMTR and TLBTR exist in v8 but have no 64-bit versions */ { .name = "TCMTR", @@ -8720,6 +8916,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo mpidr_cp_reginfo[] = { { .name = "MPIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, + .fgt = FGT_MPIDR_EL1, .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, }; #ifdef CONFIG_USER_ONLY @@ -8806,6 +9003,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "VBAR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, + .fgt = FGT_VBAR_EL1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), offsetof(CPUARMState, cp15.vbar_ns) }, .resetvalue = 0 }, @@ -8819,6 +9017,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .name = "SCTLR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_SCTLR_EL1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s), offsetof(CPUARMState, cp15.sctlr_ns) }, .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, @@ -8933,6 +9132,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_scxtnum, cpu)) { define_arm_cp_regs(cpu, scxtnum_reginfo); } + + if (cpu_isar_feature(aa64_fgt, cpu)) { + define_arm_cp_regs(cpu, fgt_reginfo); + } #endif if (cpu_isar_feature(any_predinv, cpu)) { @@ -11639,6 +11842,20 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env) return arm_mmu_idx_el(env, arm_current_el(env)); } +static inline bool fgt_svc(CPUARMState *env, int el) +{ + /* + * Assuming fine-grained-traps are active, return true if we + * should be trapping on SVC instructions. Only AArch64 can + * trap on an SVC at EL1, but we don't need to special-case this + * because if this is AArch32 EL1 then arm_fgt_active() is false. + * We also know el is 0 or 1. + */ + return el == 0 ? + FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, SVC_EL0) : + FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, SVC_EL1); +} + static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el, ARMMMUIdx mmu_idx, CPUARMTBFlags flags) @@ -11649,6 +11866,7 @@ static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el, if (arm_singlestep_active(env)) { DP_TBFLAG_ANY(flags, SS_ACTIVE, 1); } + return flags; } @@ -11716,11 +11934,18 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_A32(flags, VFPEN, 1); } - if (el < 2 && env->cp15.hstr_el2 && + if (el < 2 && env->cp15.hstr_el2 && arm_is_el2_enabled(env) && (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { DP_TBFLAG_A32(flags, HSTR_ACTIVE, 1); } + if (arm_fgt_active(env, el)) { + DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1); + if (fgt_svc(env, el)) { + DP_TBFLAG_ANY(flags, FGT_SVC, 1); + } + } + if (env->uncached_cpsr & CPSR_IL) { DP_TBFLAG_ANY(flags, PSTATE__IL, 1); } @@ -11855,6 +12080,16 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, DP_TBFLAG_ANY(flags, PSTATE__IL, 1); } + if (arm_fgt_active(env, el)) { + DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1); + if (FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, ERET)) { + DP_TBFLAG_A64(flags, FGT_ERET, 1); + } + if (fgt_svc(env, el)) { + DP_TBFLAG_ANY(flags, FGT_SVC, 1); + } + } + if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { /* * Set MTE_ACTIVE if any access may be Checked, and leave clear diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 060aa0ccf4..ad65603445 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -80,6 +80,33 @@ #define SYSREG_PMCCNTR_EL0 SYSREG(3, 3, 9, 13, 0) #define SYSREG_PMCCFILTR_EL0 SYSREG(3, 3, 14, 15, 7) +#define SYSREG_ICC_AP0R0_EL1 SYSREG(3, 0, 12, 8, 4) +#define SYSREG_ICC_AP0R1_EL1 SYSREG(3, 0, 12, 8, 5) +#define SYSREG_ICC_AP0R2_EL1 SYSREG(3, 0, 12, 8, 6) +#define SYSREG_ICC_AP0R3_EL1 SYSREG(3, 0, 12, 8, 7) +#define SYSREG_ICC_AP1R0_EL1 SYSREG(3, 0, 12, 9, 0) +#define SYSREG_ICC_AP1R1_EL1 SYSREG(3, 0, 12, 9, 1) +#define SYSREG_ICC_AP1R2_EL1 SYSREG(3, 0, 12, 9, 2) +#define SYSREG_ICC_AP1R3_EL1 SYSREG(3, 0, 12, 9, 3) +#define SYSREG_ICC_ASGI1R_EL1 SYSREG(3, 0, 12, 11, 6) +#define SYSREG_ICC_BPR0_EL1 SYSREG(3, 0, 12, 8, 3) +#define SYSREG_ICC_BPR1_EL1 SYSREG(3, 0, 12, 12, 3) +#define SYSREG_ICC_CTLR_EL1 SYSREG(3, 0, 12, 12, 4) +#define SYSREG_ICC_DIR_EL1 SYSREG(3, 0, 12, 11, 1) +#define SYSREG_ICC_EOIR0_EL1 SYSREG(3, 0, 12, 8, 1) +#define SYSREG_ICC_EOIR1_EL1 SYSREG(3, 0, 12, 12, 1) +#define SYSREG_ICC_HPPIR0_EL1 SYSREG(3, 0, 12, 8, 2) +#define SYSREG_ICC_HPPIR1_EL1 SYSREG(3, 0, 12, 12, 2) +#define SYSREG_ICC_IAR0_EL1 SYSREG(3, 0, 12, 8, 0) +#define SYSREG_ICC_IAR1_EL1 SYSREG(3, 0, 12, 12, 0) +#define SYSREG_ICC_IGRPEN0_EL1 SYSREG(3, 0, 12, 12, 6) +#define SYSREG_ICC_IGRPEN1_EL1 SYSREG(3, 0, 12, 12, 7) +#define SYSREG_ICC_PMR_EL1 SYSREG(3, 0, 4, 6, 0) +#define SYSREG_ICC_RPR_EL1 SYSREG(3, 0, 12, 11, 3) +#define SYSREG_ICC_SGI0R_EL1 SYSREG(3, 0, 12, 11, 7) +#define SYSREG_ICC_SGI1R_EL1 SYSREG(3, 0, 12, 11, 5) +#define SYSREG_ICC_SRE_EL1 SYSREG(3, 0, 12, 12, 5) + #define WFX_IS_WFE (1 << 0) #define TMR_CTL_ENABLE (1 << 0) @@ -788,6 +815,43 @@ static bool is_id_sysreg(uint32_t reg) SYSREG_CRM(reg) < 8; } +static uint32_t hvf_reg2cp_reg(uint32_t reg) +{ + return ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, + (reg >> SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK, + (reg >> SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK, + (reg >> SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK, + (reg >> SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK, + (reg >> SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK); +} + +static bool hvf_sysreg_read_cp(CPUState *cpu, uint32_t reg, uint64_t *val) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + const ARMCPRegInfo *ri; + + ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); + if (ri) { + if (ri->accessfn) { + if (ri->accessfn(env, ri, true) != CP_ACCESS_OK) { + return false; + } + } + if (ri->type & ARM_CP_CONST) { + *val = ri->resetvalue; + } else if (ri->readfn) { + *val = ri->readfn(env, ri); + } else { + *val = CPREG_FIELD64(env, ri); + } + trace_hvf_vgic_read(ri->name, *val); + return true; + } + + return false; +} + static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) { ARMCPU *arm_cpu = ARM_CPU(cpu); @@ -839,6 +903,36 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_OSDLR_EL1: /* Dummy register */ break; + case SYSREG_ICC_AP0R0_EL1: + case SYSREG_ICC_AP0R1_EL1: + case SYSREG_ICC_AP0R2_EL1: + case SYSREG_ICC_AP0R3_EL1: + case SYSREG_ICC_AP1R0_EL1: + case SYSREG_ICC_AP1R1_EL1: + case SYSREG_ICC_AP1R2_EL1: + case SYSREG_ICC_AP1R3_EL1: + case SYSREG_ICC_ASGI1R_EL1: + case SYSREG_ICC_BPR0_EL1: + case SYSREG_ICC_BPR1_EL1: + case SYSREG_ICC_DIR_EL1: + case SYSREG_ICC_EOIR0_EL1: + case SYSREG_ICC_EOIR1_EL1: + case SYSREG_ICC_HPPIR0_EL1: + case SYSREG_ICC_HPPIR1_EL1: + case SYSREG_ICC_IAR0_EL1: + case SYSREG_ICC_IAR1_EL1: + case SYSREG_ICC_IGRPEN0_EL1: + case SYSREG_ICC_IGRPEN1_EL1: + case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_SGI0R_EL1: + case SYSREG_ICC_SGI1R_EL1: + case SYSREG_ICC_SRE_EL1: + case SYSREG_ICC_CTLR_EL1: + /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ + if (!hvf_sysreg_read_cp(cpu, reg, &val)) { + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + } + break; default: if (is_id_sysreg(reg)) { /* ID system registers read as RES0 */ @@ -944,6 +1038,33 @@ static void pmswinc_write(CPUARMState *env, uint64_t value) } } +static bool hvf_sysreg_write_cp(CPUState *cpu, uint32_t reg, uint64_t val) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + const ARMCPRegInfo *ri; + + ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); + + if (ri) { + if (ri->accessfn) { + if (ri->accessfn(env, ri, false) != CP_ACCESS_OK) { + return false; + } + } + if (ri->writefn) { + ri->writefn(env, ri, val); + } else { + CPREG_FIELD64(env, ri) = val; + } + + trace_hvf_vgic_write(ri->name, val); + return true; + } + + return false; +} + static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) { ARMCPU *arm_cpu = ARM_CPU(cpu); @@ -1021,6 +1142,36 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_OSDLR_EL1: /* Dummy register */ break; + case SYSREG_ICC_AP0R0_EL1: + case SYSREG_ICC_AP0R1_EL1: + case SYSREG_ICC_AP0R2_EL1: + case SYSREG_ICC_AP0R3_EL1: + case SYSREG_ICC_AP1R0_EL1: + case SYSREG_ICC_AP1R1_EL1: + case SYSREG_ICC_AP1R2_EL1: + case SYSREG_ICC_AP1R3_EL1: + case SYSREG_ICC_ASGI1R_EL1: + case SYSREG_ICC_BPR0_EL1: + case SYSREG_ICC_BPR1_EL1: + case SYSREG_ICC_CTLR_EL1: + case SYSREG_ICC_DIR_EL1: + case SYSREG_ICC_EOIR0_EL1: + case SYSREG_ICC_EOIR1_EL1: + case SYSREG_ICC_HPPIR0_EL1: + case SYSREG_ICC_HPPIR1_EL1: + case SYSREG_ICC_IAR0_EL1: + case SYSREG_ICC_IAR1_EL1: + case SYSREG_ICC_IGRPEN0_EL1: + case SYSREG_ICC_IGRPEN1_EL1: + case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_SGI0R_EL1: + case SYSREG_ICC_SGI1R_EL1: + case SYSREG_ICC_SRE_EL1: + /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ + if (!hvf_sysreg_write_cp(cpu, reg, val)) { + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + } + break; default: cpu_synchronize_state(cpu); trace_hvf_unhandled_sysreg_write(env->pc, reg, diff --git a/target/arm/hvf/trace-events b/target/arm/hvf/trace-events index 820e8e0297..4fbbe4b45e 100644 --- a/target/arm/hvf/trace-events +++ b/target/arm/hvf/trace-events @@ -9,3 +9,5 @@ hvf_unknown_hvc(uint64_t x0) "unknown HVC! 0x%016"PRIx64 hvf_unknown_smc(uint64_t x0) "unknown SMC! 0x%016"PRIx64 hvf_exit(uint64_t syndrome, uint32_t ec, uint64_t pc) "exit: 0x%"PRIx64" [ec=0x%x pc=0x%"PRIx64"]" hvf_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid) "PSCI Call x0=0x%016"PRIx64" x1=0x%016"PRIx64" x2=0x%016"PRIx64" x3=0x%016"PRIx64" cpu=0x%x" +hvf_vgic_write(const char *name, uint64_t val) "vgic write to %s [val=0x%016"PRIx64"]" +hvf_vgic_read(const char *name, uint64_t val) "vgic read from %s [val=0x%016"PRIx64"]" diff --git a/target/arm/internals.h b/target/arm/internals.h index d9555309df..e1e018da46 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1377,4 +1377,24 @@ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) ((1 << (1 - 1)) | (1 << (2 - 1)) | \ (1 << (4 - 1)) | (1 << (8 - 1)) | (1 << (16 - 1))) +/* + * Return true if it is possible to take a fine-grained-trap to EL2. + */ +static inline bool arm_fgt_active(CPUARMState *env, int el) +{ + /* + * The Arm ARM only requires the "{E2H,TGE} != {1,1}" test for traps + * that can affect EL0, but it is harmless to do the test also for + * traps on registers that are only accessible at EL1 because if the test + * returns true then we can't be executing at EL1 anyway. + * FGT traps only happen when EL2 is enabled and EL1 is AArch64; + * traps from AArch32 only happen for the EL0 is AArch32 case. + */ + return cpu_isar_feature(aa64_fgt, env_archcpu(env)) && + el < 2 && arm_is_el2_enabled(env) && + arm_el_is_aa64(env, 1) && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE) && + (!arm_feature(env, ARM_FEATURE_EL3) || (env->cp15.scr_el3 & SCR_FGTEN)); +} + #endif diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 31f89db899..3baf8004f6 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -640,11 +640,30 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, goto fail; } + if (ri->accessfn) { + res = ri->accessfn(env, ri, isread); + } + + /* + * If the access function indicates a trap from EL0 to EL1 then + * that always takes priority over the HSTR_EL2 trap. (If it indicates + * a trap to EL3, then the HSTR_EL2 trap takes priority; if it indicates + * a trap to EL2, then the syndrome is the same either way so we don't + * care whether technically the architecture says that HSTR_EL2 trap or + * the other trap takes priority. So we take the "check HSTR_EL2" path + * for all of those cases.) + */ + if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) == 0) && + arm_current_el(env) == 0) { + goto fail; + } + /* - * Check for an EL2 trap due to HSTR_EL2. We expect EL0 accesses - * to sysregs non accessible at EL0 to have UNDEF-ed already. + * HSTR_EL2 traps from EL1 are checked earlier, in generated code; + * we only need to check here for traps from EL0. */ - if (!is_a64(env) && arm_current_el(env) < 2 && ri->cp == 15 && + if (!is_a64(env) && arm_current_el(env) == 0 && ri->cp == 15 && + arm_is_el2_enabled(env) && (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { uint32_t mask = 1 << ri->crn; @@ -661,9 +680,36 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, } } - if (ri->accessfn) { - res = ri->accessfn(env, ri, isread); + /* + * Fine-grained traps also are lower priority than undef-to-EL1, + * higher priority than trap-to-EL3, and we don't care about priority + * order with other EL2 traps because the syndrome value is the same. + */ + if (arm_fgt_active(env, arm_current_el(env))) { + uint64_t trapword = 0; + unsigned int idx = FIELD_EX32(ri->fgt, FGT, IDX); + unsigned int bitpos = FIELD_EX32(ri->fgt, FGT, BITPOS); + bool rev = FIELD_EX32(ri->fgt, FGT, REV); + bool trapbit; + + if (ri->fgt & FGT_EXEC) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_exec)); + trapword = env->cp15.fgt_exec[idx]; + } else if (isread && (ri->fgt & FGT_R)) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_read)); + trapword = env->cp15.fgt_read[idx]; + } else if (!isread && (ri->fgt & FGT_W)) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_write)); + trapword = env->cp15.fgt_write[idx]; + } + + trapbit = extract64(trapword, bitpos, 1); + if (trapbit != rev) { + res = CP_ACCESS_TRAP_EL2; + goto fail; + } } + if (likely(res == CP_ACCESS_OK)) { return ri; } @@ -673,6 +719,8 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, case CP_ACCESS_TRAP: break; case CP_ACCESS_TRAP_UNCATEGORIZED: + /* Only CP_ACCESS_TRAP traps are direct to a specified EL */ + assert((res & CP_ACCESS_EL_MASK) == 0); if (cpu_isar_feature(aa64_ids, cpu) && isread && arm_cpreg_in_idspace(ri)) { /* diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 57f3615a66..2b125fff44 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -266,7 +266,7 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, if (unlikely(flags & TLB_INVALID_MASK)) { goto fail; } - ptw->out_phys = full->phys_addr; + ptw->out_phys = full->phys_addr | (addr & ~TARGET_PAGE_MASK); ptw->out_rw = full->prot & PAGE_WRITE; pte_attrs = full->pte_attrs; pte_secure = full->attrs.secure; diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 73df5e3793..d27d1bc31f 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -48,6 +48,7 @@ enum arm_exception_class { EC_AA64_SMC = 0x17, EC_SYSTEMREGISTERTRAP = 0x18, EC_SVEACCESSTRAP = 0x19, + EC_ERETTRAP = 0x1a, EC_SMETRAP = 0x1d, EC_INSNABORT = 0x20, EC_INSNABORT_SAME_EL = 0x21, @@ -215,6 +216,15 @@ static inline uint32_t syn_sve_access_trap(void) return EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT; } +/* + * eret_op is bits [1:0] of the ERET instruction, so: + * 0 for ERET, 2 for ERETAA, 3 for ERETAB. + */ +static inline uint32_t syn_erettrap(int eret_op) +{ + return (EC_ERETTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | eret_op; +} + static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) { return (EC_SMETRAP << ARM_EL_EC_SHIFT) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 52b1b8a1f0..bbfadb7c2e 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -1962,7 +1962,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, return; } - if (ri->accessfn) { + if (ri->accessfn || (ri->fgt && s->fgt_active)) { /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. */ @@ -2179,6 +2179,7 @@ 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); + uint32_t syndrome; switch (opc) { case 0: @@ -2189,8 +2190,13 @@ static void disas_exc(DisasContext *s, uint32_t insn) */ switch (op2_ll) { case 1: /* SVC */ + syndrome = syn_aa64_svc(imm16); + if (s->fgt_svc) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); + break; + } gen_ss_advance(s); - gen_exception_insn(s, 4, EXCP_SWI, syn_aa64_svc(imm16)); + gen_exception_insn(s, 4, EXCP_SWI, syndrome); break; case 2: /* HVC */ if (s->current_el == 0) { @@ -2385,6 +2391,10 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) if (op4 != 0) { goto do_unallocated; } + if (s->fgt_eret) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(op3), 2); + return; + } dst = tcg_temp_new_i64(); tcg_gen_ld_i64(dst, cpu_env, offsetof(CPUARMState, elr_el[s->current_el])); @@ -2398,6 +2408,11 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) if (rn != 0x1f || op4 != 0x1f) { goto do_unallocated; } + /* The FGT trap takes precedence over an auth trap. */ + if (s->fgt_eret) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(op3), 2); + return; + } dst = tcg_temp_new_i64(); tcg_gen_ld_i64(dst, cpu_env, offsetof(CPUARMState, elr_el[s->current_el])); @@ -14741,6 +14756,9 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); + dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE); + dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC); + dc->fgt_eret = EX_TBFLAG_A64(tb_flags, FGT_ERET); dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL); dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL); dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16; diff --git a/target/arm/translate.c b/target/arm/translate.c index 365e02fb0b..c23a3462bf 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -4718,6 +4718,73 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); TCGv_ptr tcg_ri = NULL; bool need_exit_tb; + uint32_t syndrome; + + /* + * Note that since we are an implementation which takes an + * exception on a trapped conditional instruction only if the + * instruction passes its condition code check, we can take + * advantage of the clause in the ARM ARM that allows us to set + * the COND field in the instruction to 0xE in all cases. + * We could fish the actual condition out of the insn (ARM) + * or the condexec bits (Thumb) but it isn't necessary. + */ + switch (cpnum) { + case 14: + if (is64) { + syndrome = syn_cp14_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, false); + } else { + syndrome = syn_cp14_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, false); + } + break; + case 15: + if (is64) { + syndrome = syn_cp15_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, false); + } else { + syndrome = syn_cp15_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, false); + } + break; + default: + /* + * ARMv8 defines that only coprocessors 14 and 15 exist, + * so this can only happen if this is an ARMv7 or earlier CPU, + * in which case the syndrome information won't actually be + * guest visible. + */ + assert(!arm_dc_feature(s, ARM_FEATURE_V8)); + syndrome = syn_uncategorized(); + break; + } + + if (s->hstr_active && cpnum == 15 && s->current_el == 1) { + /* + * At EL1, check for a HSTR_EL2 trap, which must take precedence + * over the UNDEF for "no such register" or the UNDEF for "access + * permissions forbid this EL1 access". HSTR_EL2 traps from EL0 + * only happen if the cpreg doesn't UNDEF at EL0, so we do those in + * access_check_cp_reg(), after the checks for whether the access + * configurably trapped to EL1. + */ + uint32_t maskbit = is64 ? crm : crn; + + if (maskbit != 4 && maskbit != 14) { + /* T4 and T14 are RES0 so never cause traps */ + TCGv_i32 t; + DisasLabel over = gen_disas_label(s); + + t = load_cpu_offset(offsetoflow32(CPUARMState, cp15.hstr_el2)); + tcg_gen_andi_i32(t, t, 1u << maskbit); + tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, over.label); + tcg_temp_free_i32(t); + + gen_exception_insn(s, 0, EXCP_UDEF, syndrome); + set_disas_label(s, over); + } + } if (!ri) { /* @@ -4747,7 +4814,8 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, return; } - if (s->hstr_active || ri->accessfn || + if ((s->hstr_active && s->current_el == 0) || ri->accessfn || + (ri->fgt && s->fgt_active) || (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { /* * Emit code to perform further access permissions checks at @@ -4755,48 +4823,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. */ - uint32_t syndrome; - - /* - * Note that since we are an implementation which takes an - * exception on a trapped conditional instruction only if the - * instruction passes its condition code check, we can take - * advantage of the clause in the ARM ARM that allows us to set - * the COND field in the instruction to 0xE in all cases. - * We could fish the actual condition out of the insn (ARM) - * or the condexec bits (Thumb) but it isn't necessary. - */ - switch (cpnum) { - case 14: - if (is64) { - syndrome = syn_cp14_rrt_trap(1, 0xe, opc1, crm, rt, rt2, - isread, false); - } else { - syndrome = syn_cp14_rt_trap(1, 0xe, opc1, opc2, crn, crm, - rt, isread, false); - } - break; - case 15: - if (is64) { - syndrome = syn_cp15_rrt_trap(1, 0xe, opc1, crm, rt, rt2, - isread, false); - } else { - syndrome = syn_cp15_rt_trap(1, 0xe, opc1, opc2, crn, crm, - rt, isread, false); - } - break; - default: - /* - * ARMv8 defines that only coprocessors 14 and 15 exist, - * so this can only happen if this is an ARMv7 or earlier CPU, - * in which case the syndrome information won't actually be - * guest visible. - */ - assert(!arm_dc_feature(s, ARM_FEATURE_V8)); - syndrome = syn_uncategorized(); - break; - } - gen_set_condexec(s); gen_update_pc(s, 0); tcg_ri = tcg_temp_new_ptr(); @@ -8808,9 +8834,14 @@ static bool trans_SVC(DisasContext *s, arg_SVC *a) (a->imm == semihost_imm)) { gen_exception_internal_insn(s, EXCP_SEMIHOST); } else { - gen_update_pc(s, curr_insn_len(s)); - s->svc_imm = a->imm; - s->base.is_jmp = DISAS_SWI; + if (s->fgt_svc) { + uint32_t syndrome = syn_aa32_svc(a->imm, s->thumb); + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); + } else { + gen_update_pc(s, curr_insn_len(s)); + s->svc_imm = a->imm; + s->base.is_jmp = DISAS_SWI; + } } return true; } @@ -9390,6 +9421,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); + dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE); + dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC); if (arm_feature(env, ARM_FEATURE_M)) { dc->vfp_enabled = 1; diff --git a/target/arm/translate.h b/target/arm/translate.h index f17f095cbe..3717824b75 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -130,6 +130,12 @@ typedef struct DisasContext { bool is_nonstreaming; /* True if MVE insns are definitely not predicated by VPR or LTPSIZE */ bool mve_no_pred; + /* True if fine-grained traps are active */ + bool fgt_active; + /* True if fine-grained trap on ERET is enabled */ + bool fgt_eret; + /* True if fine-grained trap on SVC is enabled */ + bool fgt_svc; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. diff --git a/target/i386/hax/hax-all.c b/target/i386/hax/hax-all.c index b7fb5385b2..3e5992a63b 100644 --- a/target/i386/hax/hax-all.c +++ b/target/i386/hax/hax-all.c @@ -357,6 +357,9 @@ static int hax_accel_init(MachineState *ms) fprintf(stdout, "HAX is %s and emulator runs in %s mode.\n", !ret ? "working" : "not working", !ret ? "fast virt" : "emulation"); + fprintf(stdout, + "NOTE: HAX is deprecated and will be removed in a future release.\n" + " Use 'whpx' (on Windows) or 'hvf' (on macOS) instead.\n"); } return ret; } diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c index 858dfcc53a..2e93e77e0d 100644 --- a/target/loongarch/disas.c +++ b/target/loongarch/disas.c @@ -519,10 +519,6 @@ INSN(fsel, fffc) INSN(addu16i_d, rr_i) INSN(lu12i_w, r_i) INSN(lu32i_d, r_i) -INSN(pcaddi, r_i) -INSN(pcalau12i, r_i) -INSN(pcaddu12i, r_i) -INSN(pcaddu18i, r_i) INSN(ll_w, rr_i) INSN(sc_w, rr_i) INSN(ll_d, rr_i) @@ -628,7 +624,7 @@ INSN(beqz, r_offs) INSN(bnez, r_offs) INSN(bceqz, c_offs) INSN(bcnez, c_offs) -INSN(jirl, rr_offs) +INSN(jirl, rr_i) INSN(b, offs) INSN(bl, offs) INSN(beq, rr_offs) @@ -755,3 +751,36 @@ static bool trans_fcmp_cond_##suffix(DisasContext *ctx, \ FCMP_INSN(s) FCMP_INSN(d) + +#define PCADD_INSN(name) \ +static bool trans_##name(DisasContext *ctx, arg_##name *a) \ +{ \ + output(ctx, #name, "r%d, %d # 0x%" PRIx64, \ + a->rd, a->imm, gen_##name(ctx->pc, a->imm)); \ + return true; \ +} + +static uint64_t gen_pcaddi(uint64_t pc, int imm) +{ + return pc + (imm << 2); +} + +static uint64_t gen_pcalau12i(uint64_t pc, int imm) +{ + return (pc + (imm << 12)) & ~0xfff; +} + +static uint64_t gen_pcaddu12i(uint64_t pc, int imm) +{ + return pc + (imm << 12); +} + +static uint64_t gen_pcaddu18i(uint64_t pc, int imm) +{ + return pc + ((uint64_t)(imm) << 18); +} + +PCADD_INSN(pcaddi) +PCADD_INSN(pcalau12i) +PCADD_INSN(pcaddu12i) +PCADD_INSN(pcaddu18i) diff --git a/target/loongarch/insn_trans/trans_branch.c.inc b/target/loongarch/insn_trans/trans_branch.c.inc index 65dbdff41e..a860f7e733 100644 --- a/target/loongarch/insn_trans/trans_branch.c.inc +++ b/target/loongarch/insn_trans/trans_branch.c.inc @@ -23,7 +23,7 @@ static bool trans_jirl(DisasContext *ctx, arg_jirl *a) TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - tcg_gen_addi_tl(cpu_pc, src1, a->offs); + tcg_gen_addi_tl(cpu_pc, src1, a->imm); tcg_gen_movi_tl(dest, ctx->base.pc_next + 4); gen_set_gpr(a->rd, dest, EXT_NONE); tcg_gen_lookup_and_goto_ptr(); diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode index 3fdc6e148c..de7b8f0f3c 100644 --- a/target/loongarch/insns.decode +++ b/target/loongarch/insns.decode @@ -67,6 +67,7 @@ @rr_ui12 .... ...... imm:12 rj:5 rd:5 &rr_i @rr_i14s2 .... .... .............. rj:5 rd:5 &rr_i imm=%i14s2 @rr_i16 .... .. imm:s16 rj:5 rd:5 &rr_i +@rr_i16s2 .... .. ................ rj:5 rd:5 &rr_i imm=%offs16 @hint_r_i12 .... ...... imm:s12 rj:5 hint:5 &hint_r_i @rrr_sa2p1 .... ........ ... .. rk:5 rj:5 rd:5 &rrr_sa sa=%sa2p1 @rrr_sa2 .... ........ ... sa:2 rk:5 rj:5 rd:5 &rrr_sa @@ -444,7 +445,7 @@ beqz 0100 00 ................ ..... ..... @r_offs21 bnez 0100 01 ................ ..... ..... @r_offs21 bceqz 0100 10 ................ 00 ... ..... @c_offs21 bcnez 0100 10 ................ 01 ... ..... @c_offs21 -jirl 0100 11 ................ ..... ..... @rr_offs16 +jirl 0100 11 ................ ..... ..... @rr_i16s2 b 0101 00 .......................... @offs26 bl 0101 01 .......................... @offs26 beq 0101 10 ................ ..... ..... @rr_offs16 diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build index 6376f9e84b..690633969f 100644 --- a/target/loongarch/meson.build +++ b/target/loongarch/meson.build @@ -3,7 +3,6 @@ gen = decodetree.process('insns.decode') loongarch_ss = ss.source_set() loongarch_ss.add(files( 'cpu.c', - 'disas.c', )) loongarch_tcg_ss = ss.source_set() loongarch_tcg_ss.add(gen) @@ -24,6 +23,8 @@ loongarch_softmmu_ss.add(files( 'iocsr_helper.c', )) +common_ss.add(when: 'CONFIG_LOONGARCH_DIS', if_true: [files('disas.c'), gen]) + loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss]) target_arch += {'loongarch': loongarch_ss} diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 18418312b1..31178c3b1d 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -2774,7 +2774,11 @@ DISAS_INSN(swap) DISAS_INSN(bkpt) { +#if defined(CONFIG_SOFTMMU) + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); +#else gen_exception(s, s->base.pc_next, EXCP_DEBUG); +#endif } DISAS_INSN(pea) diff --git a/tcg/arm/tcg-target-con-set.h b/tcg/arm/tcg-target-con-set.h index 3685e1786a..b8849b2478 100644 --- a/tcg/arm/tcg-target-con-set.h +++ b/tcg/arm/tcg-target-con-set.h @@ -15,8 +15,9 @@ C_O0_I2(r, rIN) C_O0_I2(s, s) C_O0_I2(w, r) C_O0_I3(s, s, s) +C_O0_I3(S, p, s) C_O0_I4(r, r, rI, rI) -C_O0_I4(s, s, s, s) +C_O0_I4(S, p, s, s) C_O1_I1(r, l) C_O1_I1(r, r) C_O1_I1(w, r) @@ -38,8 +39,8 @@ C_O1_I2(w, w, wZ) C_O1_I3(w, w, w, w) C_O1_I4(r, r, r, rI, rI) C_O1_I4(r, r, rIN, rIK, 0) -C_O2_I1(r, r, l) -C_O2_I2(r, r, l, l) +C_O2_I1(e, p, l) +C_O2_I2(e, p, l, l) C_O2_I2(r, r, r, r) C_O2_I4(r, r, r, r, rIN, rIK) C_O2_I4(r, r, rI, rI, rIN, rIK) diff --git a/tcg/arm/tcg-target-con-str.h b/tcg/arm/tcg-target-con-str.h index 8f501149e1..24b4b59feb 100644 --- a/tcg/arm/tcg-target-con-str.h +++ b/tcg/arm/tcg-target-con-str.h @@ -8,9 +8,11 @@ * Define constraint letters for register sets: * REGS(letter, register_mask) */ +REGS('e', ALL_GENERAL_REGS & 0x5555) /* even regs */ REGS('r', ALL_GENERAL_REGS) REGS('l', ALL_QLOAD_REGS) REGS('s', ALL_QSTORE_REGS) +REGS('S', ALL_QSTORE_REGS & 0x5555) /* even qstore */ REGS('w', ALL_VECTOR_REGS) /* diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 6abe94137e..0f5f9f4925 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1694,9 +1694,11 @@ static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, tcg_out_ld32_r(s, COND_AL, datalo, addrlo, addend); break; case MO_UQ: + /* We used pair allocation for datalo, so already should be aligned. */ + tcg_debug_assert((datalo & 1) == 0); + tcg_debug_assert(datahi == datalo + 1); /* LDRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { + if (get_alignment_bits(opc) >= MO_64) { /* * Rm (the second address op) must not overlap Rt or Rt + 1. * Since datalo is aligned, we can simplify the test via alignment. @@ -1750,9 +1752,11 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, tcg_out_ld32_12(s, COND_AL, datalo, addrlo, 0); break; case MO_UQ: + /* We used pair allocation for datalo, so already should be aligned. */ + tcg_debug_assert((datalo & 1) == 0); + tcg_debug_assert(datahi == datalo + 1); /* LDRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { + if (get_alignment_bits(opc) >= MO_64) { tcg_out_ldrd_8(s, COND_AL, datalo, addrlo, 0); } else if (datalo == addrlo) { tcg_out_ld32_12(s, COND_AL, datahi, addrlo, 4); @@ -1834,9 +1838,11 @@ static void tcg_out_qemu_st_index(TCGContext *s, ARMCond cond, MemOp opc, tcg_out_st32_r(s, cond, datalo, addrlo, addend); break; case MO_64: + /* We used pair allocation for datalo, so already should be aligned. */ + tcg_debug_assert((datalo & 1) == 0); + tcg_debug_assert(datahi == datalo + 1); /* STRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { + if (get_alignment_bits(opc) >= MO_64) { tcg_out_strd_r(s, cond, datalo, addrlo, addend); } else if (scratch_addend) { tcg_out_st32_rwb(s, cond, datalo, addend, addrlo); @@ -1871,9 +1877,11 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); break; case MO_64: + /* We used pair allocation for datalo, so already should be aligned. */ + tcg_debug_assert((datalo & 1) == 0); + tcg_debug_assert(datahi == datalo + 1); /* STRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { + if (get_alignment_bits(opc) >= MO_64) { tcg_out_strd_8(s, COND_AL, datalo, addrlo, 0); } else { tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); @@ -2372,11 +2380,11 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_qemu_ld_i32: return TARGET_LONG_BITS == 32 ? C_O1_I1(r, l) : C_O1_I2(r, l, l); case INDEX_op_qemu_ld_i64: - return TARGET_LONG_BITS == 32 ? C_O2_I1(r, r, l) : C_O2_I2(r, r, l, l); + return TARGET_LONG_BITS == 32 ? C_O2_I1(e, p, l) : C_O2_I2(e, p, l, l); case INDEX_op_qemu_st_i32: return TARGET_LONG_BITS == 32 ? C_O0_I2(s, s) : C_O0_I3(s, s, s); case INDEX_op_qemu_st_i64: - return TARGET_LONG_BITS == 32 ? C_O0_I3(s, s, s) : C_O0_I4(s, s, s, s); + return TARGET_LONG_BITS == 32 ? C_O0_I3(S, p, s) : C_O0_I4(S, p, s, s); case INDEX_op_st_vec: return C_O0_I2(w, r); diff --git a/tcg/loongarch64/tcg-insn-defs.c.inc b/tcg/loongarch64/tcg-insn-defs.c.inc index d162571856..b5bb0c5e73 100644 --- a/tcg/loongarch64/tcg-insn-defs.c.inc +++ b/tcg/loongarch64/tcg-insn-defs.c.inc @@ -4,7 +4,7 @@ * * This file is auto-generated by genqemutcgdefs from * https://github.com/loongson-community/loongarch-opcodes, - * from commit 961f0c60f5b63e574d785995600c71ad5413fdc4. + * from commit 25ca7effe9d88101c1cf96c4005423643386d81f. * DO NOT EDIT. */ @@ -74,6 +74,7 @@ typedef enum { OPC_ANDI = 0x03400000, OPC_ORI = 0x03800000, OPC_XORI = 0x03c00000, + OPC_ADDU16I_D = 0x10000000, OPC_LU12I_W = 0x14000000, OPC_CU32I_D = 0x16000000, OPC_PCADDU2I = 0x18000000, @@ -710,6 +711,13 @@ tcg_out_opc_xori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) tcg_out32(s, encode_djuk12_insn(OPC_XORI, d, j, uk12)); } +/* Emits the `addu16i.d d, j, sk16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_addu16i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +{ + tcg_out32(s, encode_djsk16_insn(OPC_ADDU16I_D, d, j, sk16)); +} + /* Emits the `lu12i.w d, sj20` instruction. */ static void __attribute__((unused)) tcg_out_opc_lu12i_w(TCGContext *s, TCGReg d, int32_t sj20) diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index 349c672687..172c107289 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -23,9 +23,12 @@ C_O1_I1(r, L) C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) +C_O1_I2(r, r, rJ) C_O1_I2(r, r, rU) C_O1_I2(r, r, rW) C_O1_I2(r, r, rZ) C_O1_I2(r, 0, rZ) -C_O1_I2(r, rZ, rN) +C_O1_I2(r, rZ, ri) +C_O1_I2(r, rZ, rJ) C_O1_I2(r, rZ, rZ) +C_O1_I4(r, rZ, rJ, rZ, rZ) diff --git a/tcg/loongarch64/tcg-target-con-str.h b/tcg/loongarch64/tcg-target-con-str.h index c3986a4fd4..541ff47fa9 100644 --- a/tcg/loongarch64/tcg-target-con-str.h +++ b/tcg/loongarch64/tcg-target-con-str.h @@ -21,7 +21,7 @@ REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) * CONST(letter, TCG_CT_CONST_* bit set) */ CONST('I', TCG_CT_CONST_S12) -CONST('N', TCG_CT_CONST_N12) +CONST('J', TCG_CT_CONST_S32) CONST('U', TCG_CT_CONST_U12) CONST('Z', TCG_CT_CONST_ZERO) CONST('C', TCG_CT_CONST_C12) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 3174557ce3..ce4a153887 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -126,7 +126,7 @@ static const int tcg_target_call_oarg_regs[] = { #define TCG_CT_CONST_ZERO 0x100 #define TCG_CT_CONST_S12 0x200 -#define TCG_CT_CONST_N12 0x400 +#define TCG_CT_CONST_S32 0x400 #define TCG_CT_CONST_U12 0x800 #define TCG_CT_CONST_C12 0x1000 #define TCG_CT_CONST_WSZ 0x2000 @@ -161,7 +161,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) if ((ct & TCG_CT_CONST_S12) && val == sextreg(val, 0, 12)) { return true; } - if ((ct & TCG_CT_CONST_N12) && -val == sextreg(-val, 0, 12)) { + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { return true; } if ((ct & TCG_CT_CONST_U12) && val >= 0 && val <= 0xfff) { @@ -274,16 +274,6 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) return true; } -static bool imm_part_needs_loading(bool high_bits_are_ones, - tcg_target_long part) -{ - if (high_bits_are_ones) { - return part != -1; - } else { - return part != 0; - } -} - /* Loads a 32-bit immediate into rd, sign-extended. */ static void tcg_out_movi_i32(TCGContext *s, TCGReg rd, int32_t val) { @@ -291,16 +281,16 @@ static void tcg_out_movi_i32(TCGContext *s, TCGReg rd, int32_t val) tcg_target_long hi12 = sextreg(val, 12, 20); /* Single-instruction cases. */ - if (lo == val) { - /* val fits in simm12: addi.w rd, zero, val */ - tcg_out_opc_addi_w(s, rd, TCG_REG_ZERO, val); - return; - } - if (0x800 <= val && val <= 0xfff) { + if (hi12 == 0) { /* val fits in uimm12: ori rd, zero, val */ tcg_out_opc_ori(s, rd, TCG_REG_ZERO, val); return; } + if (hi12 == sextreg(lo, 12, 20)) { + /* val fits in simm12: addi.w rd, zero, val */ + tcg_out_opc_addi_w(s, rd, TCG_REG_ZERO, val); + return; + } /* High bits must be set; load with lu12i.w + optional ori. */ tcg_out_opc_lu12i_w(s, rd, hi12); @@ -334,8 +324,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, intptr_t pc_offset; tcg_target_long val_lo, val_hi, pc_hi, offset_hi; - tcg_target_long hi32, hi52; - bool rd_high_bits_are_ones; + tcg_target_long hi12, hi32, hi52; /* Value fits in signed i32. */ if (type == TCG_TYPE_I32 || val == (int32_t)val) { @@ -366,29 +355,68 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, return; } + hi12 = sextreg(val, 12, 20); hi32 = sextreg(val, 32, 20); hi52 = sextreg(val, 52, 12); /* Single cu52i.d case. */ - if (ctz64(val) >= 52) { + if ((hi52 != 0) && (ctz64(val) >= 52)) { tcg_out_opc_cu52i_d(s, rd, TCG_REG_ZERO, hi52); return; } /* Slow path. Initialize the low 32 bits, then concat high bits. */ tcg_out_movi_i32(s, rd, val); - rd_high_bits_are_ones = (int32_t)val < 0; - if (imm_part_needs_loading(rd_high_bits_are_ones, hi32)) { + /* Load hi32 and hi52 explicitly when they are unexpected values. */ + if (hi32 != sextreg(hi12, 20, 20)) { tcg_out_opc_cu32i_d(s, rd, hi32); - rd_high_bits_are_ones = hi32 < 0; } - if (imm_part_needs_loading(rd_high_bits_are_ones, hi52)) { + if (hi52 != sextreg(hi32, 20, 12)) { tcg_out_opc_cu52i_d(s, rd, rd, hi52); } } +static void tcg_out_addi(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rs, tcg_target_long imm) +{ + tcg_target_long lo12 = sextreg(imm, 0, 12); + tcg_target_long hi16 = sextreg(imm - lo12, 16, 16); + + /* + * Note that there's a hole in between hi16 and lo12: + * + * 3 2 1 0 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * ...+-------------------------------+-------+-----------------------+ + * | hi16 | | lo12 | + * ...+-------------------------------+-------+-----------------------+ + * + * For bits within that hole, it's more efficient to use LU12I and ADD. + */ + if (imm == (hi16 << 16) + lo12) { + if (hi16) { + tcg_out_opc_addu16i_d(s, rd, rs, hi16); + rs = rd; + } + if (type == TCG_TYPE_I32) { + tcg_out_opc_addi_w(s, rd, rs, lo12); + } else if (lo12) { + tcg_out_opc_addi_d(s, rd, rs, lo12); + } else { + tcg_out_mov(s, type, rd, rs); + } + } else { + tcg_out_movi(s, type, TCG_REG_TMP0, imm); + if (type == TCG_TYPE_I32) { + tcg_out_opc_add_w(s, rd, rs, TCG_REG_TMP0); + } else { + tcg_out_opc_add_d(s, rd, rs, TCG_REG_TMP0); + } + } +} + static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) { tcg_out_opc_andi(s, ret, arg, 0xff); @@ -441,64 +469,155 @@ static void tcg_out_clzctz(TCGContext *s, LoongArchInsn opc, tcg_out_opc_or(s, a0, TCG_REG_TMP0, a0); } -static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, TCGReg arg2, bool c2) +#define SETCOND_INV TCG_TARGET_NB_REGS +#define SETCOND_NEZ (SETCOND_INV << 1) +#define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) + +static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, tcg_target_long arg2, bool c2) { - TCGReg tmp; + int flags = 0; - if (c2) { - tcg_debug_assert(arg2 == 0); + switch (cond) { + case TCG_COND_EQ: /* -> NE */ + case TCG_COND_GE: /* -> LT */ + case TCG_COND_GEU: /* -> LTU */ + case TCG_COND_GT: /* -> LE */ + case TCG_COND_GTU: /* -> LEU */ + cond = tcg_invert_cond(cond); + flags ^= SETCOND_INV; + break; + default: + break; } switch (cond) { - case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LEU: + /* + * If we have a constant input, the most efficient way to implement + * LE is by adding 1 and using LT. Watch out for wrap around for LEU. + * We don't need to care for this for LE because the constant input + * is still constrained to int32_t, and INT32_MAX+1 is representable + * in the 64-bit temporary register. + */ if (c2) { - tmp = arg1; + if (cond == TCG_COND_LEU) { + /* unsigned <= -1 is true */ + if (arg2 == -1) { + tcg_out_movi(s, TCG_TYPE_REG, ret, !(flags & SETCOND_INV)); + return ret; + } + cond = TCG_COND_LTU; + } else { + cond = TCG_COND_LT; + } + arg2 += 1; } else { - tcg_out_opc_sub_d(s, ret, arg1, arg2); - tmp = ret; + TCGReg tmp = arg2; + arg2 = arg1; + arg1 = tmp; + cond = tcg_swap_cond(cond); /* LE -> GE */ + cond = tcg_invert_cond(cond); /* GE -> LT */ + flags ^= SETCOND_INV; } - tcg_out_opc_sltui(s, ret, tmp, 1); break; + default: + break; + } + + switch (cond) { case TCG_COND_NE: - if (c2) { - tmp = arg1; + flags |= SETCOND_NEZ; + if (!c2) { + tcg_out_opc_xor(s, ret, arg1, arg2); + } else if (arg2 == 0) { + ret = arg1; + } else if (arg2 >= 0 && arg2 <= 0xfff) { + tcg_out_opc_xori(s, ret, arg1, arg2); } else { - tcg_out_opc_sub_d(s, ret, arg1, arg2); - tmp = ret; + tcg_out_addi(s, TCG_TYPE_REG, ret, arg1, -arg2); } - tcg_out_opc_sltu(s, ret, TCG_REG_ZERO, tmp); break; + case TCG_COND_LT: - tcg_out_opc_slt(s, ret, arg1, arg2); - break; - case TCG_COND_GE: - tcg_out_opc_slt(s, ret, arg1, arg2); - tcg_out_opc_xori(s, ret, ret, 1); - break; - case TCG_COND_LE: - tcg_out_setcond(s, TCG_COND_GE, ret, arg2, arg1, false); - break; - case TCG_COND_GT: - tcg_out_setcond(s, TCG_COND_LT, ret, arg2, arg1, false); - break; case TCG_COND_LTU: - tcg_out_opc_sltu(s, ret, arg1, arg2); - break; - case TCG_COND_GEU: - tcg_out_opc_sltu(s, ret, arg1, arg2); - tcg_out_opc_xori(s, ret, ret, 1); - break; - case TCG_COND_LEU: - tcg_out_setcond(s, TCG_COND_GEU, ret, arg2, arg1, false); - break; - case TCG_COND_GTU: - tcg_out_setcond(s, TCG_COND_LTU, ret, arg2, arg1, false); + if (c2) { + if (arg2 >= -0x800 && arg2 <= 0x7ff) { + if (cond == TCG_COND_LT) { + tcg_out_opc_slti(s, ret, arg1, arg2); + } else { + tcg_out_opc_sltui(s, ret, arg1, arg2); + } + break; + } + tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP0, arg2); + arg2 = TCG_REG_TMP0; + } + if (cond == TCG_COND_LT) { + tcg_out_opc_slt(s, ret, arg1, arg2); + } else { + tcg_out_opc_sltu(s, ret, arg1, arg2); + } break; + default: g_assert_not_reached(); break; } + + return ret | flags; +} + +static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, tcg_target_long arg2, bool c2) +{ + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2, c2); + + if (tmpflags != ret) { + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + + switch (tmpflags & SETCOND_FLAGS) { + case SETCOND_INV: + /* Intermediate result is boolean: simply invert. */ + tcg_out_opc_xori(s, ret, tmp, 1); + break; + case SETCOND_NEZ: + /* Intermediate result is zero/non-zero: test != 0. */ + tcg_out_opc_sltu(s, ret, TCG_REG_ZERO, tmp); + break; + case SETCOND_NEZ | SETCOND_INV: + /* Intermediate result is zero/non-zero: test == 0. */ + tcg_out_opc_sltui(s, ret, tmp, 1); + break; + default: + g_assert_not_reached(); + } + } +} + +static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, tcg_target_long c2, bool const2, + TCGReg v1, TCGReg v2) +{ + int tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, c1, c2, const2); + TCGReg t; + + /* Standardize the test below to t != 0. */ + if (tmpflags & SETCOND_INV) { + t = v1, v1 = v2, v2 = t; + } + + t = tmpflags & ~SETCOND_FLAGS; + if (v1 == TCG_REG_ZERO) { + tcg_out_opc_masknez(s, ret, v2, t); + } else if (v2 == TCG_REG_ZERO) { + tcg_out_opc_maskeqz(s, ret, v1, t); + } else { + tcg_out_opc_masknez(s, TCG_REG_TMP2, v2, t); /* t ? 0 : v2 */ + tcg_out_opc_maskeqz(s, TCG_REG_TMP1, v1, t); /* t ? v1 : 0 */ + tcg_out_opc_or(s, ret, TCG_REG_TMP1, TCG_REG_TMP2); + } } /* @@ -583,7 +702,7 @@ static void tcg_out_ldst(TCGContext *s, LoongArchInsn opc, TCGReg data, intptr_t imm12 = sextreg(offset, 0, 12); if (offset != imm12) { - intptr_t diff = offset - (uintptr_t)s->code_ptr; + intptr_t diff = tcg_pcrel_diff(s, (void *)offset); if (addr == TCG_REG_ZERO && diff == (int32_t)diff) { imm12 = sextreg(diff, 0, 12); @@ -1032,37 +1151,6 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) #endif } -/* LoongArch uses `andi zero, zero, 0` as NOP. */ -#define NOP OPC_ANDI -static void tcg_out_nop(TCGContext *s) -{ - tcg_out32(s, NOP); -} - -void tb_target_set_jmp_target(const TranslationBlock *tb, int n, - uintptr_t jmp_rx, uintptr_t jmp_rw) -{ - tcg_insn_unit i1, i2; - ptrdiff_t upper, lower; - uintptr_t addr = tb->jmp_target_addr[n]; - ptrdiff_t offset = (ptrdiff_t)(addr - jmp_rx) >> 2; - - if (offset == sextreg(offset, 0, 26)) { - i1 = encode_sd10k16_insn(OPC_B, offset); - i2 = NOP; - } else { - tcg_debug_assert(offset == sextreg(offset, 0, 36)); - lower = (int16_t)offset; - upper = (offset - lower) >> 16; - - i1 = encode_dsj20_insn(OPC_PCADDU18I, TCG_REG_TMP0, upper); - i2 = encode_djsk16_insn(OPC_JIRL, TCG_REG_ZERO, TCG_REG_TMP0, lower); - } - uint64_t pair = ((uint64_t)i2 << 32) | i1; - qatomic_set((uint64_t *)jmp_rw, pair); - flush_idcache_range(jmp_rx, jmp_rw, 8); -} - /* * Entry-points */ @@ -1083,22 +1171,43 @@ static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) static void tcg_out_goto_tb(TCGContext *s, int which) { /* - * Ensure that patch area is 8-byte aligned so that an - * atomic write can be used to patch the target address. + * Direct branch, or load indirect address, to be patched + * by tb_target_set_jmp_target. Check indirect load offset + * in range early, regardless of direct branch distance, + * via assert within tcg_out_opc_pcaddu2i. */ - if ((uintptr_t)s->code_ptr & 7) { - tcg_out_nop(s); - } + uintptr_t i_addr = get_jmp_target_addr(s, which); + intptr_t i_disp = tcg_pcrel_diff(s, (void *)i_addr); + set_jmp_insn_offset(s, which); - /* - * actual branch destination will be patched by - * tb_target_set_jmp_target later - */ - tcg_out_opc_pcaddu18i(s, TCG_REG_TMP0, 0); + tcg_out_opc_pcaddu2i(s, TCG_REG_TMP0, i_disp >> 2); + + /* Finish the load and indirect branch. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_REG_TMP0, 0); tcg_out_opc_jirl(s, TCG_REG_ZERO, TCG_REG_TMP0, 0); set_jmp_reset_offset(s, which); } +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + uintptr_t d_addr = tb->jmp_target_addr[n]; + ptrdiff_t d_disp = (ptrdiff_t)(d_addr - jmp_rx) >> 2; + tcg_insn_unit insn; + + /* Either directly branch, or load slot address for indirect branch. */ + if (d_disp == sextreg(d_disp, 0, 26)) { + insn = encode_sd10k16_insn(OPC_B, d_disp); + } else { + uintptr_t i_addr = (uintptr_t)&tb->jmp_target_addr[n]; + intptr_t i_disp = i_addr - jmp_rx; + insn = encode_dsj20_insn(OPC_PCADDU2I, TCG_REG_TMP0, i_disp >> 2); + } + + qatomic_set((tcg_insn_unit *)jmp_rw, insn); + flush_idcache_range(jmp_rx, jmp_rw, 4); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1361,14 +1470,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_add_i32: if (c2) { - tcg_out_opc_addi_w(s, a0, a1, a2); + tcg_out_addi(s, TCG_TYPE_I32, a0, a1, a2); } else { tcg_out_opc_add_w(s, a0, a1, a2); } break; case INDEX_op_add_i64: if (c2) { - tcg_out_opc_addi_d(s, a0, a1, a2); + tcg_out_addi(s, TCG_TYPE_I64, a0, a1, a2); } else { tcg_out_opc_add_d(s, a0, a1, a2); } @@ -1376,14 +1485,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_sub_i32: if (c2) { - tcg_out_opc_addi_w(s, a0, a1, -a2); + tcg_out_addi(s, TCG_TYPE_I32, a0, a1, -a2); } else { tcg_out_opc_sub_w(s, a0, a1, a2); } break; case INDEX_op_sub_i64: if (c2) { - tcg_out_opc_addi_d(s, a0, a1, -a2); + tcg_out_addi(s, TCG_TYPE_I64, a0, a1, -a2); } else { tcg_out_opc_sub_d(s, a0, a1, a2); } @@ -1443,6 +1552,11 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_setcond(s, args[3], a0, a1, a2, c2); break; + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + tcg_out_movcond(s, args[5], a0, a1, a2, c2, args[3], args[4]); + break; + case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); @@ -1597,8 +1711,9 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I2(r, r, ri); case INDEX_op_add_i32: + return C_O1_I2(r, r, ri); case INDEX_op_add_i64: - return C_O1_I2(r, r, rI); + return C_O1_I2(r, r, rJ); case INDEX_op_and_i32: case INDEX_op_and_i64: @@ -1617,18 +1732,17 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ctz_i64: return C_O1_I2(r, r, rW); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - return C_O1_I2(r, r, rZ); - case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: /* Must deposit into the same register as input */ return C_O1_I2(r, 0, rZ); case INDEX_op_sub_i32: + case INDEX_op_setcond_i32: + return C_O1_I2(r, rZ, ri); case INDEX_op_sub_i64: - return C_O1_I2(r, rZ, rN); + case INDEX_op_setcond_i64: + return C_O1_I2(r, rZ, rJ); case INDEX_op_mul_i32: case INDEX_op_mul_i64: @@ -1646,6 +1760,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_remu_i64: return C_O1_I2(r, rZ, rZ); + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + return C_O1_I4(r, rZ, rJ, rZ, rZ); + default: g_assert_not_reached(); } diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 1c3e48d662..8b151e7f6f 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -42,11 +42,8 @@ #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 32 -/* - * PCADDU18I + JIRL sequence can give 20 + 16 + 2 = 38 bits - * signed offset, which is +/- 128 GiB. - */ -#define MAX_CODE_GEN_BUFFER_SIZE (128 * GiB) + +#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) typedef enum { TCG_REG_ZERO, @@ -97,7 +94,7 @@ typedef enum { #define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL /* optional instructions */ -#define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 @@ -133,7 +130,7 @@ typedef enum { #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ -#define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 9fa9f1b0fd..326a9180ef 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -874,7 +874,7 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_op3_i32(INDEX_op_muluh_i32, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); - } else { + } else if (TCG_TARGET_REG_BITS == 64) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(t0, arg1); @@ -883,6 +883,8 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_extr_i64_i32(rl, rh, t0); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); + } else { + qemu_build_not_reached(); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index d502327be2..fd557d55d3 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1674,8 +1674,10 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args) op = tcg_op_alloc(INDEX_op_call, total_args); #ifdef CONFIG_PLUGIN - /* detect non-plugin helpers */ - if (tcg_ctx->plugin_insn && unlikely(strncmp(info->name, "plugin_", 7))) { + /* Flag helpers that may affect guest state */ + if (tcg_ctx->plugin_insn && + !(info->flags & TCG_CALL_PLUGIN) && + !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) { tcg_ctx->plugin_insn->calls_helpers = true; } #endif diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index 910f3ba1ea..25a546842f 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -306,7 +306,7 @@ class QemuSystemTest(QemuBaseTest): self.cancel('no support for user networking') def _new_vm(self, name, *args): - self._sd = tempfile.TemporaryDirectory(prefix="avo_qemu_sock_") + self._sd = tempfile.TemporaryDirectory(prefix="qemu_") vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, sock_dir=self._sd.name, log_dir=self.logdir) self.log.debug('QEMUMachine "%s" created', name) diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/pc/DSDT index b688686dc3..0b475fb5a9 100644 --- a/tests/data/acpi/pc/DSDT +++ b/tests/data/acpi/pc/DSDT Binary files differdiff --git a/tests/data/acpi/pc/DSDT.acpierst b/tests/data/acpi/pc/DSDT.acpierst index 86259be9d1..17ef7caeb6 100644 --- a/tests/data/acpi/pc/DSDT.acpierst +++ b/tests/data/acpi/pc/DSDT.acpierst Binary files differdiff --git a/tests/data/acpi/pc/DSDT.acpihmat b/tests/data/acpi/pc/DSDT.acpihmat index e2cc2a6fc9..675b674eaa 100644 --- a/tests/data/acpi/pc/DSDT.acpihmat +++ b/tests/data/acpi/pc/DSDT.acpihmat Binary files differdiff --git a/tests/data/acpi/pc/DSDT.bridge b/tests/data/acpi/pc/DSDT.bridge index 75016fd4b7..c1ce061366 100644 --- a/tests/data/acpi/pc/DSDT.bridge +++ b/tests/data/acpi/pc/DSDT.bridge Binary files differdiff --git a/tests/data/acpi/pc/DSDT.cphp b/tests/data/acpi/pc/DSDT.cphp index 53eb0dd7d4..754ab854dc 100644 --- a/tests/data/acpi/pc/DSDT.cphp +++ b/tests/data/acpi/pc/DSDT.cphp Binary files differdiff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm index 9089d994e0..170503336b 100644 --- a/tests/data/acpi/pc/DSDT.dimmpxm +++ b/tests/data/acpi/pc/DSDT.dimmpxm Binary files differdiff --git a/tests/data/acpi/pc/DSDT.hpbridge b/tests/data/acpi/pc/DSDT.hpbridge index 86259be9d1..834c27002e 100644 --- a/tests/data/acpi/pc/DSDT.hpbridge +++ b/tests/data/acpi/pc/DSDT.hpbridge Binary files differdiff --git a/tests/data/acpi/pc/DSDT.hpbrroot b/tests/data/acpi/pc/DSDT.hpbrroot index 578468f4f0..a71ed4fbaa 100644 --- a/tests/data/acpi/pc/DSDT.hpbrroot +++ b/tests/data/acpi/pc/DSDT.hpbrroot Binary files differdiff --git a/tests/data/acpi/pc/DSDT.ipmikcs b/tests/data/acpi/pc/DSDT.ipmikcs index 39427103aa..dd71356027 100644 --- a/tests/data/acpi/pc/DSDT.ipmikcs +++ b/tests/data/acpi/pc/DSDT.ipmikcs Binary files differdiff --git a/tests/data/acpi/pc/DSDT.memhp b/tests/data/acpi/pc/DSDT.memhp index 987a263339..2f895e9b38 100644 --- a/tests/data/acpi/pc/DSDT.memhp +++ b/tests/data/acpi/pc/DSDT.memhp Binary files differdiff --git a/tests/data/acpi/pc/DSDT.nohpet b/tests/data/acpi/pc/DSDT.nohpet index fc7598b762..c012b63ace 100644 --- a/tests/data/acpi/pc/DSDT.nohpet +++ b/tests/data/acpi/pc/DSDT.nohpet Binary files differdiff --git a/tests/data/acpi/pc/DSDT.numamem b/tests/data/acpi/pc/DSDT.numamem index 85af400cdb..f2ef4b9729 100644 --- a/tests/data/acpi/pc/DSDT.numamem +++ b/tests/data/acpi/pc/DSDT.numamem Binary files differdiff --git a/tests/data/acpi/pc/DSDT.roothp b/tests/data/acpi/pc/DSDT.roothp index 545512adfa..657c8263f0 100644 --- a/tests/data/acpi/pc/DSDT.roothp +++ b/tests/data/acpi/pc/DSDT.roothp Binary files differdiff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT index 2771bcea89..d68c472b46 100644 --- a/tests/data/acpi/q35/DSDT +++ b/tests/data/acpi/q35/DSDT Binary files differdiff --git a/tests/data/acpi/q35/DSDT.acpierst b/tests/data/acpi/q35/DSDT.acpierst index b45abca7c2..de7ae27125 100644 --- a/tests/data/acpi/q35/DSDT.acpierst +++ b/tests/data/acpi/q35/DSDT.acpierst Binary files differdiff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat index d90fd4723a..48e2862257 100644 --- a/tests/data/acpi/q35/DSDT.acpihmat +++ b/tests/data/acpi/q35/DSDT.acpihmat Binary files differdiff --git a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator index 279fafa821..30a4aa2ec8 100644 --- a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator +++ b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator Binary files differdiff --git a/tests/data/acpi/q35/DSDT.applesmc b/tests/data/acpi/q35/DSDT.applesmc index fdf6d14428..84e2b5cbc4 100644 --- a/tests/data/acpi/q35/DSDT.applesmc +++ b/tests/data/acpi/q35/DSDT.applesmc Binary files differdiff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge index b41a4dddc0..e411d40fd1 100644 --- a/tests/data/acpi/q35/DSDT.bridge +++ b/tests/data/acpi/q35/DSDT.bridge Binary files differdiff --git a/tests/data/acpi/q35/DSDT.core-count2 b/tests/data/acpi/q35/DSDT.core-count2 index 375aceed6b..0603db8cc6 100644 --- a/tests/data/acpi/q35/DSDT.core-count2 +++ b/tests/data/acpi/q35/DSDT.core-count2 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp index a0ecafc36c..beeb83c33b 100644 --- a/tests/data/acpi/q35/DSDT.cphp +++ b/tests/data/acpi/q35/DSDT.cphp Binary files differdiff --git a/tests/data/acpi/q35/DSDT.cxl b/tests/data/acpi/q35/DSDT.cxl index f9c6dd4ee0..3d18b9672d 100644 --- a/tests/data/acpi/q35/DSDT.cxl +++ b/tests/data/acpi/q35/DSDT.cxl Binary files differdiff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm index f0659716e3..99a93e12a7 100644 --- a/tests/data/acpi/q35/DSDT.dimmpxm +++ b/tests/data/acpi/q35/DSDT.dimmpxm Binary files differdiff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt index 9c52529919..7f7601dbff 100644 --- a/tests/data/acpi/q35/DSDT.ipmibt +++ b/tests/data/acpi/q35/DSDT.ipmibt Binary files differdiff --git a/tests/data/acpi/q35/DSDT.ipmismbus b/tests/data/acpi/q35/DSDT.ipmismbus index 3f32dffdbf..6c5d1afe44 100644 --- a/tests/data/acpi/q35/DSDT.ipmismbus +++ b/tests/data/acpi/q35/DSDT.ipmismbus Binary files differdiff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/q35/DSDT.ivrs index b45abca7c2..de7ae27125 100644 --- a/tests/data/acpi/q35/DSDT.ivrs +++ b/tests/data/acpi/q35/DSDT.ivrs Binary files differdiff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp index 28a192c69a..79bce5c8f0 100644 --- a/tests/data/acpi/q35/DSDT.memhp +++ b/tests/data/acpi/q35/DSDT.memhp Binary files differdiff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64 index 8fda921296..c249929add 100644 --- a/tests/data/acpi/q35/DSDT.mmio64 +++ b/tests/data/acpi/q35/DSDT.mmio64 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge index 3dba4d8436..66b39be294 100644 --- a/tests/data/acpi/q35/DSDT.multi-bridge +++ b/tests/data/acpi/q35/DSDT.multi-bridge Binary files differdiff --git a/tests/data/acpi/q35/DSDT.nohpet b/tests/data/acpi/q35/DSDT.nohpet index b116947dac..9ff9983a80 100644 --- a/tests/data/acpi/q35/DSDT.nohpet +++ b/tests/data/acpi/q35/DSDT.nohpet Binary files differdiff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem index 5eb6159d5f..1e7c45ef3c 100644 --- a/tests/data/acpi/q35/DSDT.numamem +++ b/tests/data/acpi/q35/DSDT.numamem Binary files differdiff --git a/tests/data/acpi/q35/DSDT.pvpanic-isa b/tests/data/acpi/q35/DSDT.pvpanic-isa index 908e7b6606..ed47451c44 100644 --- a/tests/data/acpi/q35/DSDT.pvpanic-isa +++ b/tests/data/acpi/q35/DSDT.pvpanic-isa Binary files differdiff --git a/tests/data/acpi/q35/DSDT.tis.tpm12 b/tests/data/acpi/q35/DSDT.tis.tpm12 index ce2c2c29c2..efc2efc19f 100644 --- a/tests/data/acpi/q35/DSDT.tis.tpm12 +++ b/tests/data/acpi/q35/DSDT.tis.tpm12 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.tis.tpm2 b/tests/data/acpi/q35/DSDT.tis.tpm2 index e9e4b7f6ed..675339715f 100644 --- a/tests/data/acpi/q35/DSDT.tis.tpm2 +++ b/tests/data/acpi/q35/DSDT.tis.tpm2 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.viot b/tests/data/acpi/q35/DSDT.viot index 6b436f9cd9..eeb40b360f 100644 --- a/tests/data/acpi/q35/DSDT.viot +++ b/tests/data/acpi/q35/DSDT.viot Binary files differdiff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/q35/DSDT.xapic index f47f091222..3aa86f0724 100644 --- a/tests/data/acpi/q35/DSDT.xapic +++ b/tests/data/acpi/q35/DSDT.xapic Binary files differdiff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 665ddde518..bfb0dcac21 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -128,7 +128,6 @@ DOCKER_PARTIAL_IMAGES += debian-mips-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 diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 094f66f4eb..4a569d82f6 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -77,7 +77,6 @@ RUN apk update && \ numactl-dev \ openssh-client \ pcre-dev \ - perl \ pixman-dev \ pkgconf \ pulseaudio-dev \ @@ -100,7 +99,6 @@ RUN apk update && \ spice-protocol \ tar \ tesseract-ocr \ - texinfo \ usbredir-dev \ util-linux \ vde2-dev \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index 1f70d41aeb..fbc953c6dc 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -91,7 +91,6 @@ RUN dnf distro-sync -y && \ openssh-clients \ pam-devel \ pcre-static \ - perl \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ @@ -111,7 +110,6 @@ RUN dnf distro-sync -y && \ systemd-devel \ systemtap-sdt-devel \ tar \ - texinfo \ usbredir-devel \ util-linux \ virglrenderer-devel \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 5e57309361..5175095a85 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index bfeab01ee3..b61f664ea2 100644 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ b/tests/docker/dockerfiles/debian-amd64.docker @@ -108,7 +108,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ nettle-dev \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -126,7 +125,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo \ xfslibs-dev \ zlib1g-dev && \ eatmydata apt-get autoremove -y && \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 98885bd0ee..b69958c69f 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index d5c08714e4..96b524fab6 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 471444fcf4..08a75cebdb 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 15b0224b76..5930e6fa5d 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index a5d3ca6e2f..c65d9830e7 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index d2954e61f6..2ae56c978e 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 9715791e0b..3daf93968a 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -42,6 +42,7 @@ RUN apt update && \ apt install -y --no-install-recommends \ gcc-riscv64-linux-gnu \ libc6-dev-riscv64-cross \ + libfdt-dev:riscv64 \ libffi-dev:riscv64 \ libglib2.0-dev:riscv64 \ libpixman-1-dev:riscv64 diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index d43ce16317..0db86a0fcd 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -40,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -56,8 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ sparse \ tar \ tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + tesseract-ocr-eng && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ diff --git a/tests/docker/dockerfiles/debian-toolchain.docker b/tests/docker/dockerfiles/debian-toolchain.docker index d3d4d3344e..6c73408b34 100644 --- a/tests/docker/dockerfiles/debian-toolchain.docker +++ b/tests/docker/dockerfiles/debian-toolchain.docker @@ -21,7 +21,6 @@ RUN apt update && \ libmpc-dev \ libmpfr-dev \ rsync \ - texinfo \ wget && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt build-dep -yy --arch-only gcc glibc diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index b573b9ded2..5ae58efa09 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -20,6 +20,7 @@ RUN apt update && \ bzip2 \ ca-certificates \ ccache \ + flex \ g++ \ gcc \ git \ @@ -28,7 +29,6 @@ RUN apt update && \ locales \ make \ ninja-build \ - perl-base \ pkgconf \ python3-pip \ python3-setuptools \ diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker index 7eec648d2d..f58b64dc3e 100644 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -9,6 +9,7 @@ ENV PACKAGES \ findutils \ gcc \ git \ + libfdt-devel.i686 \ libffi-devel.i686 \ libselinux-devel.i686 \ libtasn1-devel.i686 \ diff --git a/tests/docker/dockerfiles/fedora-win32-cross.docker b/tests/docker/dockerfiles/fedora-win32-cross.docker index 75383ba185..b659c0b8a8 100644 --- a/tests/docker/dockerfiles/fedora-win32-cross.docker +++ b/tests/docker/dockerfiles/fedora-win32-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross mingw32 fedora-35 qemu +# $ lcitool dockerfile --layers all --cross mingw32 fedora-37 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:35 +FROM registry.fedoraproject.org/fedora:37 RUN dnf install -y nosync && \ echo -e '#!/bin/sh\n\ @@ -42,7 +42,6 @@ exec "$@"' > /usr/bin/nosync && \ nmap-ncat \ openssh-clients \ pcre-static \ - perl-base \ python3 \ python3-PyYAML \ python3-numpy \ @@ -58,7 +57,6 @@ exec "$@"' > /usr/bin/nosync && \ tar \ tesseract \ tesseract-langpack-eng \ - texinfo \ util-linux \ which && \ nosync dnf autoremove -y && \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 98c03dc13b..0a404c15bf 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross mingw64 fedora-35 qemu +# $ lcitool dockerfile --layers all --cross mingw64 fedora-37 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:35 +FROM registry.fedoraproject.org/fedora:37 RUN dnf install -y nosync && \ echo -e '#!/bin/sh\n\ @@ -42,7 +42,6 @@ exec "$@"' > /usr/bin/nosync && \ nmap-ncat \ openssh-clients \ pcre-static \ - perl-base \ python3 \ python3-PyYAML \ python3-numpy \ @@ -58,7 +57,6 @@ exec "$@"' > /usr/bin/nosync && \ tar \ tesseract \ tesseract-langpack-eng \ - texinfo \ util-linux \ which && \ nosync dnf autoremove -y && \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index d200c7fc10..5d60a96141 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all fedora-35 qemu +# $ lcitool dockerfile --layers all fedora-37 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:35 +FROM registry.fedoraproject.org/fedora:37 RUN dnf install -y nosync && \ echo -e '#!/bin/sh\n\ @@ -98,7 +98,6 @@ exec "$@"' > /usr/bin/nosync && \ openssh-clients \ pam-devel \ pcre-static \ - perl-base \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ @@ -122,7 +121,6 @@ exec "$@"' > /usr/bin/nosync && \ tar \ tesseract \ tesseract-langpack-eng \ - texinfo \ usbredir-devel \ util-linux \ virglrenderer-devel \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 4361b01464..4b2c02d6ab 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -88,7 +88,6 @@ RUN zypper update -y && \ openssh \ pam-devel \ pcre-devel-static \ - perl-base \ pkgconfig \ python3-Pillow \ python3-PyYAML \ @@ -112,7 +111,6 @@ RUN zypper update -y && \ tar \ tesseract-ocr \ tesseract-ocr-traineddata-english \ - texinfo \ usbredir-devel \ util-linux \ virglrenderer-devel \ diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker index 9417bca2fa..13ab0b6887 100644 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ b/tests/docker/dockerfiles/ubuntu2004.docker @@ -105,7 +105,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ nettle-dev \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -125,7 +124,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo \ xfslibs-dev \ zlib1g-dev && \ eatmydata apt-get autoremove -y && \ diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci -Subproject e3eb28cf2e17fbcf7fe7e19505ee432b8ec5bbb +Subproject 319a534c220f53fc8670254cac25d6f662c8211 diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index c62dbc00f9..6467bcf08a 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -83,7 +83,6 @@ packages: - ncursesw - pam - pcre-static - - perl - pixman - pkg-config - pulseaudio @@ -110,7 +109,6 @@ packages: - tar - tesseract - tesseract-eng - - texinfo - usbredir - virglrenderer - vte diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index fa966e4009..a5ea0efc3b 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -111,7 +111,7 @@ try: generate_dockerfile("centos8", "centos-stream-8") generate_dockerfile("debian-amd64", "debian-11", trailer="".join(debian11_extras)) - generate_dockerfile("fedora", "fedora-35") + generate_dockerfile("fedora", "fedora-37") generate_dockerfile("opensuse-leap", "opensuse-leap-153") generate_dockerfile("ubuntu2004", "ubuntu-2004", trailer="".join(ubuntu2004_tsanhack)) @@ -161,12 +161,12 @@ try: trailer=cross_build("s390x-linux-gnu-", "s390x-softmmu,s390x-linux-user")) - generate_dockerfile("fedora-win32-cross", "fedora-35", + generate_dockerfile("fedora-win32-cross", "fedora-37", cross="mingw32", trailer=cross_build("i686-w64-mingw32-", "i386-softmmu")) - generate_dockerfile("fedora-win64-cross", "fedora-35", + generate_dockerfile("fedora-win64-cross", "fedora-37", cross="mingw64", trailer=cross_build("x86_64-w64-mingw32-", "x86_64-softmmu")) diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index 9dfe98bc9a..d85b14f28c 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -259,22 +259,23 @@ if build_docs # Fix possible inconsistency in line endings in generated output and # in the golden reference (which could otherwise cause test failures # on Windows hosts). Unfortunately diff --strip-trailing-cr - # is GNU-diff only. The odd-looking perl is because we must avoid + # is GNU-diff only. The odd-looking python is because we must avoid # using an explicit '\' character in the command arguments to # a custom_target(), as Meson will unhelpfully replace it with a '/' # (https://github.com/mesonbuild/meson/issues/1564) + remove_cr = [python, '-c', 'import sys;[sys.stdout.write(line.replace(chr(13), "")) for line in sys.stdin]'] qapi_doc_out_nocr = custom_target('QAPI rST doc newline-sanitized', output: ['doc-good.txt.nocr'], input: qapi_doc_out[0], build_by_default: true, - command: ['perl', '-pe', '$x = chr 13; s/$x$//', '@INPUT@'], + command: [remove_cr, '@INPUT@'], capture: true) qapi_doc_ref_nocr = custom_target('QAPI rST doc reference newline-sanitized', output: ['doc-good.ref.nocr'], input: files('doc-good.txt'), build_by_default: true, - command: ['perl', '-pe', '$x = chr 13; s/$x$//', '@INPUT@'], + command: [remove_cr, '@INPUT@'], capture: true) test('QAPI rST doc', diff, args: ['-u', qapi_doc_ref_nocr[0], qapi_doc_out_nocr[0]], diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index b724c89c7c..b76701c71e 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -56,7 +56,7 @@ class TestQemuImgInfo(TestImageInfoSpecific): def test_human(self): data = qemu_img('info', '--output=human', test_img).stdout.split('\n') data = data[(data.index('Format specific information:') + 1) - :data.index('')] + :data.index("Child node '/file':")] for field in data: self.assertTrue(re.match('^ {4}[^ ]', field) is not None) data = [line.strip() for line in data] diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106 index 9d6adb542d..ae0fc46691 100755 --- a/tests/qemu-iotests/106 +++ b/tests/qemu-iotests/106 @@ -66,7 +66,7 @@ for create_mode in off falloc full; do expected_size=$((expected_size + $GROWTH_SIZE)) fi - actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size') + actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size' | head -n 1) actual_size=$(echo "$actual_size" | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/') # The actual size may exceed the expected size, depending on the file @@ -105,7 +105,7 @@ for growth_mode in falloc full; do _make_test_img -o "extent_size_hint=0" 2G $QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K - actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size') + actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size' | head -n 1) actual_size=$(echo "$actual_size" | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/') if [ $actual_size -lt $GROWTH_SIZE ]; then diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 index c66e246ba2..55ffcd7f44 100755 --- a/tests/qemu-iotests/214 +++ b/tests/qemu-iotests/214 @@ -102,7 +102,8 @@ let data_size="8 * $cluster_size" $QEMU_IO -c "write -P 0xaa 0 $data_size" "$TEST_IMG" \ 2>&1 | _filter_qemu_io | _filter_testdir sizeA=$($QEMU_IMG info --output=json "$TEST_IMG" | - sed -n '/"actual-size":/ s/[^0-9]//gp') + sed -n '/"actual-size":/ s/[^0-9]//gp' | + head -n 1) _make_test_img 2M -o cluster_size=$cluster_size echo "Write compressed data:" @@ -124,7 +125,8 @@ $QEMU_IO -c "write -P 0xcc $offset $data_size" "json:{\ _filter_qemu_io | _filter_testdir sizeB=$($QEMU_IMG info --output=json "$TEST_IMG" | - sed -n '/"actual-size":/ s/[^0-9]//gp') + sed -n '/"actual-size":/ s/[^0-9]//gp' | + head -n 1) if [ $sizeA -lt $sizeB ] then diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262 index 2294fd5ecb..a4a92de45a 100755 --- a/tests/qemu-iotests/262 +++ b/tests/qemu-iotests/262 @@ -25,7 +25,8 @@ import iotests import os iotests.script_initialize(supported_fmts=['qcow2'], - supported_platforms=['linux']) + supported_platforms=['linux'], + required_fmts=['blkverify']) with iotests.FilePath('img') as img_path, \ iotests.FilePath('mig_fifo') as fifo, \ diff --git a/tests/qemu-iotests/302.out b/tests/qemu-iotests/302.out index 3e7c281b91..7b5014cdd8 100644 --- a/tests/qemu-iotests/302.out +++ b/tests/qemu-iotests/302.out @@ -4,6 +4,11 @@ image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock file format: raw virtual size: 448 KiB (458752 bytes) disk size: unavailable +Child node '/file': + filename: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock + protocol type: nbd + file length: 448 KiB (458752 bytes) + disk size: unavailable === Converted image info === image: TEST_IMG diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index bde4aac2fa..09275e9a10 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -217,12 +217,12 @@ echo echo '=== Remove export ===' # Double-check that $EXT_MP appears as a non-empty file (the raw image) -$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' +$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1 fuse_export_del 'export-mp' # See that the file appears empty again -$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' +$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1 echo echo '=== Writable export ===' diff --git a/tests/qemu-iotests/312 b/tests/qemu-iotests/312 index 4139745f0e..0d9ea09a31 100755 --- a/tests/qemu-iotests/312 +++ b/tests/qemu-iotests/312 @@ -52,6 +52,7 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux _unsupported_imgopts cluster_size data_file +_require_drivers quorum echo echo '### Create all images' # three source (quorum), one destination diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index cc9f1a5891..6b32c7fbfa 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -223,6 +223,7 @@ _filter_img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' + regex_json_child_start='^ *"children": \[' gsed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ @@ -251,20 +252,25 @@ _filter_img_info() -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \ -e "s/uuid: [-a-f0-9]\\+/uuid: 00000000-0000-0000-0000-000000000000/" | \ while IFS='' read -r line; do - if [[ $format_specific == 1 ]]; then - discard=0 - elif [[ $line == "Format specific information:" ]]; then - discard=1 - elif [[ $line =~ $regex_json_spec_start ]]; then - discard=2 - regex_json_spec_end="^${line%%[^ ]*}\\},? *$" + if [[ $discard == 0 ]]; then + if [[ $format_specific == 0 && $line == "Format specific information:" ]]; then + discard=1 + elif [[ $line =~ "Child node '/" ]]; then + discard=1 + elif [[ $line =~ $regex_json_spec_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\},? *$" + elif [[ $line =~ $regex_json_child_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\],? *$" + fi fi if [[ $discard == 0 ]]; then echo "$line" elif [[ $discard == 1 && ! $line ]]; then echo discard=0 - elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then + elif [[ $discard == 2 && $line =~ $regex_json_end ]]; then discard=0 fi done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index db757025cb..f4476b62f7 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -711,6 +711,7 @@ _img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' + regex_json_child_start='^ *"children": \[' $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ @@ -721,20 +722,25 @@ _img_info() -e "/^disk size:/ D" \ -e "/actual-size/ D" | \ while IFS='' read -r line; do - if [[ $format_specific == 1 ]]; then - discard=0 - elif [[ $line == "Format specific information:" ]]; then - discard=1 - elif [[ $line =~ $regex_json_spec_start ]]; then - discard=2 - regex_json_spec_end="^${line%%[^ ]*}\\},? *$" + if [[ $discard == 0 ]]; then + if [[ $format_specific == 0 && $line == "Format specific information:" ]]; then + discard=1 + elif [[ $line =~ "Child node '/" ]]; then + discard=1 + elif [[ $format_specific == 0 && $line =~ $regex_json_spec_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\},? *$" + elif [[ $line =~ $regex_json_child_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\],? *$" + fi fi if [[ $discard == 0 ]]; then echo "$line" elif [[ $discard == 1 && ! $line ]]; then echo discard=0 - elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then + elif [[ $discard == 2 && $line =~ $regex_json_end ]]; then discard=0 fi done diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index da7d6637e1..94aeb3f3b2 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -329,7 +329,7 @@ def qemu_img_log(*args: str, check: bool = True def img_info_log(filename: str, filter_path: Optional[str] = None, use_image_opts: bool = False, extra_args: Sequence[str] = (), - check: bool = True, + check: bool = True, drop_child_info: bool = True, ) -> None: args = ['info'] if use_image_opts: @@ -342,7 +342,7 @@ def img_info_log(filename: str, filter_path: Optional[str] = None, output = qemu_img(*args, check=check).stdout if not filter_path: filter_path = filename - log(filter_img_info(output, filter_path)) + log(filter_img_info(output, filter_path, drop_child_info)) def qemu_io_wrap_args(args: Sequence[str]) -> List[str]: if '-f' in args or '--image-opts' in args: @@ -642,11 +642,23 @@ def filter_qmp_virtio_scsi(qmsg): def filter_generated_node_ids(msg): return re.sub("#block[0-9]+", "NODE_NAME", msg) -def filter_img_info(output, filename): +def filter_img_info(output: str, filename: str, + drop_child_info: bool = True) -> str: lines = [] + drop_indented = False for line in output.split('\n'): if 'disk size' in line or 'actual-size' in line: continue + + # Drop child node info + if drop_indented: + if line.startswith(' '): + continue + drop_indented = False + if drop_child_info and "Child node '/" in line: + drop_indented = True + continue + line = line.replace(filename, 'TEST_IMG') line = filter_testfiles(line) line = line.replace(imgfmt, 'IMGFMT') diff --git a/tests/qemu-iotests/tests/qemu-img-close-errors b/tests/qemu-iotests/tests/qemu-img-close-errors new file mode 100755 index 0000000000..50bfb6cfa2 --- /dev/null +++ b/tests/qemu-iotests/tests/qemu-img-close-errors @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# Check that errors while closing the image, in particular writing back dirty +# bitmaps, is correctly reported with a failing qemu-img exit code. +# +# Copyright (C) 2023 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 <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +size=1G + +# The error we are going to use is ENOSPC. Depending on how many bitmaps we +# create in the backing file (and therefore increase the used up space), we get +# failures in different places. With a low number, only merging the bitmap +# fails, whereas with a higher number, already 'qemu-img commit' fails. +for max_bitmap in 6 7; do + echo + echo "=== Test with $max_bitmap bitmaps ===" + + TEST_IMG="$TEST_IMG.base" _make_test_img -q $size + for i in $(seq 1 $max_bitmap); do + $QEMU_IMG bitmap --add "$TEST_IMG.base" "stale-bitmap-$i" + done + + # Simulate a block device of 128 MB by resizing the image file accordingly + # and then enforcing the size with the raw driver + $QEMU_IO -f raw -c "truncate 128M" "$TEST_IMG.base" + BASE_JSON='json:{ + "driver": "qcow2", + "file": { + "driver": "raw", + "size": 134217728, + "file": { + "driver": "file", + "filename":"'"$TEST_IMG.base"'" + } + } + }' + + _make_test_img -q -b "$BASE_JSON" -F $IMGFMT + $QEMU_IMG bitmap --add "$TEST_IMG" "good-bitmap" + + $QEMU_IO -c 'write 0 126m' "$TEST_IMG" | _filter_qemu_io + + $QEMU_IMG commit -d "$TEST_IMG" 2>&1 | _filter_generated_node_ids + echo "qemu-img commit exit code: ${PIPESTATUS[0]}" + + $QEMU_IMG bitmap --add "$BASE_JSON" "good-bitmap" + echo "qemu-img bitmap --add exit code: $?" + + $QEMU_IMG bitmap --merge "good-bitmap" -b "$TEST_IMG" "$BASE_JSON" \ + "good-bitmap" 2>&1 | _filter_generated_node_ids + echo "qemu-img bitmap --merge exit code: ${PIPESTATUS[0]}" +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 + diff --git a/tests/qemu-iotests/tests/qemu-img-close-errors.out b/tests/qemu-iotests/tests/qemu-img-close-errors.out new file mode 100644 index 0000000000..1bfe88f176 --- /dev/null +++ b/tests/qemu-iotests/tests/qemu-img-close-errors.out @@ -0,0 +1,23 @@ +QA output created by qemu-img-close-errors + +=== Test with 6 bitmaps === +wrote 132120576/132120576 bytes at offset 0 +126 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +qemu-img commit exit code: 0 +qemu-img bitmap --add exit code: 0 +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'good-bitmap' to file: No space left on device +qemu-img: Error while closing the image: Invalid argument +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'good-bitmap' to file: No space left on device +qemu-img bitmap --merge exit code: 1 + +=== Test with 7 bitmaps === +wrote 132120576/132120576 bytes at offset 0 +126 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'stale-bitmap-7' to file: No space left on device +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'stale-bitmap-7' to file: No space left on device +qemu-img: Error while closing the image: Invalid argument +qemu-img commit exit code: 1 +qemu-img bitmap --add exit code: 0 +qemu-img bitmap --merge exit code: 0 +*** done diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 8608408213..d8c8cda58e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -24,7 +24,7 @@ * You will also notice that tests/qtest/bios-tables-test-allowed-diff.h lists * a bunch of files. This is your hint that you need to do the below: * 4. Run - * make check V=1 + * make check V=2 * this will produce a bunch of warnings about differences * beween actual and expected ACPI tables. If you have IASL installed, * they will also be disassembled so you can look at the disassembled @@ -108,6 +108,8 @@ static const char *iasl = CONFIG_IASL; static const char *iasl; #endif +static int verbosity_level; + static bool compare_signature(const AcpiSdtTable *sdt, const char *signature) { return !memcmp(sdt->aml, signature, 4); @@ -368,7 +370,7 @@ static GArray *load_expected_aml(test_data *data) gsize aml_len; GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); - if (getenv("V")) { + if (verbosity_level >= 2) { fputc('\n', stderr); } for (i = 0; i < data->tables->len; ++i) { @@ -383,7 +385,7 @@ static GArray *load_expected_aml(test_data *data) try_again: aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, sdt->aml, ext); - if (getenv("V")) { + if (verbosity_level >= 2) { fprintf(stderr, "Looking for expected file '%s'\n", aml_file); } if (g_file_test(aml_file, G_FILE_TEST_EXISTS)) { @@ -395,7 +397,7 @@ try_again: goto try_again; } g_assert(exp_sdt.aml_file); - if (getenv("V")) { + if (verbosity_level >= 2) { fprintf(stderr, "Using expected file '%s'\n", aml_file); } ret = g_file_get_contents(aml_file, (gchar **)&exp_sdt.aml, @@ -503,7 +505,7 @@ static void test_acpi_asl(test_data *data) exp_sdt->aml, sdt->asl_file, sdt->aml_file, exp_sdt->asl_file, exp_sdt->aml_file); fflush(stderr); - if (getenv("V")) { + if (verbosity_level >= 1) { const char *diff_env = getenv("DIFF"); const char *diff_cmd = diff_env ? diff_env : "diff -U 16"; char *diff = g_strdup_printf("%s %s %s", diff_cmd, @@ -748,9 +750,9 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) } } -static void test_acpi_load_tables(test_data *data, bool use_uefi) +static void test_acpi_load_tables(test_data *data) { - if (use_uefi) { + if (data->uefi_fl1 && data->uefi_fl2) { /* use UEFI */ g_assert(data->scan_len); data->rsdp_addr = acpi_find_rsdp_address_uefi(data->qts, data->ram_start, data->scan_len); @@ -766,12 +768,11 @@ static void test_acpi_load_tables(test_data *data, bool use_uefi) test_acpi_fadt_table(data); } -static char *test_acpi_create_args(test_data *data, const char *params, - bool use_uefi) +static char *test_acpi_create_args(test_data *data, const char *params) { char *args; - if (use_uefi) { + if (data->uefi_fl1 && data->uefi_fl2) { /* use UEFI */ /* * TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3) * when arm/virt boad starts to support it. @@ -806,14 +807,16 @@ static char *test_acpi_create_args(test_data *data, const char *params, return args; } -static void test_acpi_one(const char *params, test_data *data) +static void test_vm_prepare(const char *params, test_data *data) { - char *args; - bool use_uefi = data->uefi_fl1 && data->uefi_fl2; - - args = test_acpi_create_args(data, params, use_uefi); + char *args = test_acpi_create_args(data, params); data->qts = qtest_init(args); - test_acpi_load_tables(data, use_uefi); + g_free(args); +} + +static void process_acpi_tables_noexit(test_data *data) +{ + test_acpi_load_tables(data); if (getenv(ACPI_REBUILD_EXPECTED_AML)) { dump_aml_files(data, true); @@ -826,13 +829,22 @@ static void test_acpi_one(const char *params, test_data *data) * Bug on uefi-test-tools to provide entry point: * https://bugs.launchpad.net/qemu/+bug/1821884 */ - if (!use_uefi) { + if (!(data->uefi_fl1 && data->uefi_fl2)) { SmbiosEntryPointType ep_type = test_smbios_entry_point(data); test_smbios_structs(data, ep_type); } +} +static void process_acpi_tables(test_data *data) +{ + process_acpi_tables_noexit(data); qtest_quit(data->qts); - g_free(args); +} + +static void test_acpi_one(const char *params, test_data *data) +{ + test_vm_prepare(params, data); + process_acpi_tables(data); } static uint8_t base_required_struct_types[] = { @@ -863,7 +875,32 @@ static void test_acpi_piix4_tcg_bridge(void) data.variant = ".bridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one("-device pci-bridge,chassis_nr=1", &data); + test_vm_prepare("-S" + " -device pci-bridge,chassis_nr=1" + " -device pci-bridge,bus=pci.1,addr=1.0,chassis_nr=2" + " -device pci-testdev,bus=pci.0,addr=5.0" + " -device pci-testdev,bus=pci.1", &data); + + /* hotplugged bridges section */ + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr", + "{'bus': 'pci.1', 'addr': '2.0', 'chassis_nr': 3 }"); + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr_multifunc", + "{'bus': 'pci.1', 'addr': '0xf.1', 'chassis_nr': 4 }"); + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbrhost", + "{'bus': 'pci.0', 'addr': '4.0', 'chassis_nr': 5 }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "d1", "{'bus': 'pci.0' }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "d2", "{'bus': 'pci.1' }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "d3", "{'bus': 'hpbr', " + "'addr': '1.0' }"); + qtest_qmp_send(data.qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(data.qts, "RESUME"); + + process_acpi_tables_noexit(&data); + free_test_data(&data); + + /* check that reboot/reset doesn't change any ACPI tables */ + qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + process_acpi_tables(&data); free_test_data(&data); } @@ -877,7 +914,10 @@ static void test_acpi_piix4_no_root_hotplug(void) data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one("-global PIIX4_PM.acpi-root-pci-hotplug=off " - "-device pci-bridge,chassis_nr=1", &data); + "-device pci-bridge,chassis_nr=1 " + "-device pci-bridge,bus=pci.1,addr=1.0,chassis_nr=2 " + "-device pci-testdev,bus=pci.0 " + "-device pci-testdev,bus=pci.1", &data); free_test_data(&data); } @@ -891,7 +931,10 @@ static void test_acpi_piix4_no_bridge_hotplug(void) data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one("-global PIIX4_PM.acpi-pci-hotplug-with-bridge-support=off " - "-device pci-bridge,chassis_nr=1", &data); + "-device pci-bridge,chassis_nr=1 " + "-device pci-bridge,bus=pci.1,addr=1.0,chassis_nr=2 " + "-device pci-testdev,bus=pci.0 " + "-device pci-testdev,bus=pci.1,addr=2.0", &data); free_test_data(&data); } @@ -906,7 +949,9 @@ static void test_acpi_piix4_no_acpi_pci_hotplug(void) data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one("-global PIIX4_PM.acpi-root-pci-hotplug=off " "-global PIIX4_PM.acpi-pci-hotplug-with-bridge-support=off " - "-device pci-bridge,chassis_nr=1", &data); + "-device pci-bridge,chassis_nr=1 " + "-device pci-testdev,bus=pci.0 " + "-device pci-testdev,bus=pci.1", &data); free_test_data(&data); } @@ -951,8 +996,9 @@ static void test_acpi_q35_tcg_bridge(void) data.variant = ".bridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one("-device pci-bridge,chassis_nr=1", - &data); + test_acpi_one("-device pci-bridge,chassis_nr=1,id=br1" + " -device pci-testdev,bus=pcie.0" + " -device pci-testdev,bus=br1", &data); free_test_data(&data); } @@ -962,14 +1008,41 @@ static void test_acpi_q35_multif_bridge(void) .machine = MACHINE_Q35, .variant = ".multi-bridge", }; - test_acpi_one("-device pcie-root-port,id=pcie-root-port-0," - "multifunction=on," - "port=0x0,chassis=1,addr=0x2,bus=pcie.0 " - "-device pcie-root-port,id=pcie-root-port-1," - "port=0x1,chassis=2,addr=0x3.0x1,bus=pcie.0 " - "-device virtio-balloon,id=balloon0," - "bus=pcie.0,addr=0x4.0x2", - &data); + test_vm_prepare("-S" + " -device virtio-balloon,id=balloon0,addr=0x4.0x2" + " -device pcie-root-port,id=rp0,multifunction=on," + "port=0x0,chassis=1,addr=0x2" + " -device pcie-root-port,id=rp1,port=0x1,chassis=2,addr=0x3.0x1" + " -device pcie-root-port,id=rp2,port=0x0,chassis=3,bus=rp1,addr=0.0" + " -device pci-bridge,bus=rp2,chassis_nr=4,id=br1" + " -device pcie-root-port,id=rphptgt1,port=0x0,chassis=5,addr=2.1" + " -device pcie-root-port,id=rphptgt2,port=0x0,chassis=6,addr=2.2" + " -device pcie-root-port,id=rphptgt3,port=0x0,chassis=7,addr=2.3" + " -device pci-testdev,bus=pcie.0,addr=2.4" + " -device pci-testdev,bus=pcie.0,addr=5.0" + " -device pci-testdev,bus=rp0,addr=0.0" + " -device pci-testdev,bus=br1", &data); + + /* hotplugged bridges section */ + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr1", + "{'bus': 'br1', 'addr': '6.0', 'chassis_nr': 128 }"); + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr2-multiif", + "{ 'bus': 'br1', 'addr': '2.2', 'chassis_nr': 129 }"); + qtest_qmp_device_add(data.qts, "pcie-pci-bridge", "hpbr3", + "{'bus': 'rphptgt1', 'addr': '0.0' }"); + qtest_qmp_device_add(data.qts, "pcie-root-port", "hprp", + "{'bus': 'rphptgt2', 'addr': '0.0' }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "hpnic", + "{'bus': 'rphptgt3', 'addr': '0.0' }"); + qtest_qmp_send(data.qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(data.qts, "RESUME"); + + process_acpi_tables_noexit(&data); + free_test_data(&data); + + /* check that reboot/reset doesn't change any ACPI tables */ + qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + process_acpi_tables(&data); free_test_data(&data); } @@ -1898,10 +1971,9 @@ static void test_acpi_piix4_oem_fields(void) data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - args = test_acpi_create_args(&data, - OEM_TEST_ARGS, false); + args = test_acpi_create_args(&data, OEM_TEST_ARGS); data.qts = qtest_init(args); - test_acpi_load_tables(&data, false); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); @@ -1918,10 +1990,9 @@ static void test_acpi_q35_oem_fields(void) data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - args = test_acpi_create_args(&data, - OEM_TEST_ARGS, false); + args = test_acpi_create_args(&data, OEM_TEST_ARGS); data.qts = qtest_init(args); - test_acpi_load_tables(&data, false); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); @@ -1936,9 +2007,9 @@ static void test_acpi_microvm_oem_fields(void) test_acpi_microvm_prepare(&data); args = test_acpi_create_args(&data, - OEM_TEST_ARGS",acpi=on", false); + OEM_TEST_ARGS",acpi=on"); data.qts = qtest_init(args); - test_acpi_load_tables(&data, false); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); @@ -1958,10 +2029,9 @@ static void test_acpi_virt_oem_fields(void) }; char *args; - args = test_acpi_create_args(&data, - "-cpu cortex-a57 "OEM_TEST_ARGS, true); + args = test_acpi_create_args(&data, "-cpu cortex-a57 "OEM_TEST_ARGS); data.qts = qtest_init(args); - test_acpi_load_tables(&data, true); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); @@ -1974,8 +2044,13 @@ int main(int argc, char *argv[]) const char *arch = qtest_get_arch(); const bool has_kvm = qtest_has_accel("kvm"); const bool has_tcg = qtest_has_accel("tcg"); + char *v_env = getenv("V"); int ret; + if (v_env) { + verbosity_level = atoi(v_env); + } + g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { diff --git a/tests/qtest/boot-sector.c b/tests/qtest/boot-sector.c index 44a109abd8..679ee17e2a 100644 --- a/tests/qtest/boot-sector.c +++ b/tests/qtest/boot-sector.c @@ -153,6 +153,8 @@ void boot_sector_test(QTestState *qts) signature_high = qtest_readb(qts, SIGNATURE_ADDR + 1); signature = (signature_high << 8) | signature_low; if (signature == SIGNATURE) { + /* wipe signature */ + qtest_writeb(qts, SIGNATURE_ADDR, 0x00); break; } @@ -160,7 +162,9 @@ void boot_sector_test(QTestState *qts) qrsp = qtest_qmp(qts, "{ 'execute': 'query-status' }"); qret = qdict_get_qdict(qrsp, "return"); g_assert_nonnull(qret); - g_assert_cmpstr(qdict_get_try_str(qret, "status"), ==, "running"); + if (qdict_get_try_str(qret, "status")) { + g_assert_cmpstr(qdict_get_try_str(qret, "status"), ==, "running"); + } qobject_unref(qrsp); g_usleep(TEST_DELAY); diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index b216519b62..3aef3a97a9 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -139,7 +139,7 @@ typedef struct testdef { const uint8_t *bios; /* Set in case we use our own mini bios */ } testdef_t; -static testdef_t tests[] = { +static const testdef_t tests[] = { { "alpha", "clipper", "", "PCI:" }, { "avr", "arduino-duemilanove", "", "T", sizeof(bios_avr), NULL, bios_avr }, { "avr", "arduino-mega-2560-v3", "", "T", sizeof(bios_avr), NULL, bios_avr}, diff --git a/tests/qtest/display-vga-test.c b/tests/qtest/display-vga-test.c index ace3bb28e0..75b341a9c6 100644 --- a/tests/qtest/display-vga-test.c +++ b/tests/qtest/display-vga-test.c @@ -8,61 +8,46 @@ */ #include "qemu/osdep.h" -#include "libqtest-single.h" - -static void pci_cirrus(void) -{ - qtest_start("-vga none -device cirrus-vga"); - qtest_end(); -} - -static void pci_stdvga(void) -{ - qtest_start("-vga none -device VGA"); - qtest_end(); -} - -static void pci_secondary(void) -{ - qtest_start("-vga none -device secondary-vga"); - qtest_end(); -} +#include "libqtest.h" static void pci_multihead(void) { - qtest_start("-vga none -device VGA -device secondary-vga"); - qtest_end(); -} + QTestState *qts; -static void pci_virtio_gpu(void) -{ - qtest_start("-vga none -device virtio-gpu-pci"); - qtest_end(); + qts = qtest_init("-vga none -device VGA -device secondary-vga"); + qtest_quit(qts); } -static void pci_virtio_vga(void) +static void test_vga(gconstpointer data) { - qtest_start("-vga none -device virtio-vga"); - qtest_end(); + QTestState *qts; + + qts = qtest_initf("-vga none -device %s", (const char *)data); + qtest_quit(qts); } int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); + static const char *devices[] = { + "cirrus-vga", + "VGA", + "secondary-vga", + "virtio-gpu-pci", + "virtio-vga" + }; g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "alpha") == 0 || strcmp(arch, "i386") == 0 || - strcmp(arch, "mips") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("/display/pci/cirrus", pci_cirrus); + for (int i = 0; i < ARRAY_SIZE(devices); i++) { + if (qtest_has_device(devices[i])) { + char *testpath = g_strdup_printf("/display/pci/%s", devices[i]); + qtest_add_data_func(testpath, devices[i], test_vga); + g_free(testpath); + } } - qtest_add_func("/display/pci/stdvga", pci_stdvga); - qtest_add_func("/display/pci/secondary", pci_secondary); - qtest_add_func("/display/pci/multihead", pci_multihead); - qtest_add_func("/display/pci/virtio-gpu", pci_virtio_gpu); - if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || - g_str_equal(arch, "hppa") || g_str_equal(arch, "ppc64")) { - qtest_add_func("/display/pci/virtio-vga", pci_virtio_vga); + + if (qtest_has_device("secondary-vga")) { + qtest_add_func("/display/pci/multihead", pci_multihead); } return g_test_run(); diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 6b2216cb20..d658222a19 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1435,6 +1435,10 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, resp = qtest_qmp(qts, "{'execute': 'device_add', 'arguments': %p}", args); g_assert(resp); g_assert(!qdict_haskey(resp, "event")); /* We don't expect any events */ + if (qdict_haskey(resp, "error")) { + fprintf(stderr, "error: %s\n", + qdict_get_str(qdict_get_qdict(resp, "error"), "desc")); + } g_assert(!qdict_haskey(resp, "error")); qobject_unref(resp); } diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 1af63f8bd2..e97616d327 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -21,6 +21,7 @@ qtests_generic = [ 'test-hmp', 'qos-test', 'readconfig-test', + 'netdev-socket', ] if config_host.has_key('CONFIG_MODULES') qtests_generic += [ 'modules-test' ] @@ -298,6 +299,7 @@ qtests = { 'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), + 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), } gvnc = dependency('gvnc-1.0', required: false) diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c new file mode 100644 index 0000000000..6ba256e173 --- /dev/null +++ b/tests/qtest/netdev-socket.c @@ -0,0 +1,448 @@ +/* + * QTest testcase for netdev stream and dgram + * + * Copyright (c) 2022 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/sockets.h" +#include <glib/gstdio.h> +#include "../unit/socket-helpers.h" +#include "libqtest.h" + +#define CONNECTION_TIMEOUT 5 + +#define EXPECT_STATE(q, e, t) \ +do { \ + char *resp = NULL; \ + g_test_timer_start(); \ + do { \ + g_free(resp); \ + resp = qtest_hmp(q, "info network"); \ + if (t) { \ + strrchr(resp, t)[0] = 0; \ + } \ + if (g_str_equal(resp, e)) { \ + break; \ + } \ + } while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \ + g_assert_cmpstr(resp, ==, e); \ + g_free(resp); \ +} while (0) + +static gchar *tmpdir; + +static int inet_get_free_port_socket_ipv4(int sock) +{ + struct sockaddr_in addr; + socklen_t len; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = 0; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return -1; + } + + len = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) { + return -1; + } + + return ntohs(addr.sin_port); +} + +static int inet_get_free_port_socket_ipv6(int sock) +{ + struct sockaddr_in6 addr; + socklen_t len; + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = 0; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return -1; + } + + len = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) { + return -1; + } + + return ntohs(addr.sin6_port); +} + +static int inet_get_free_port_multiple(int nb, int *port, bool ipv6) +{ + int sock[nb]; + int i; + + for (i = 0; i < nb; i++) { + sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + if (sock[i] < 0) { + break; + } + port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) : + inet_get_free_port_socket_ipv4(sock[i]); + if (port[i] == -1) { + break; + } + } + + nb = i; + for (i = 0; i < nb; i++) { + closesocket(sock[i]); + } + + return nb; +} + +static int inet_get_free_port(bool ipv6) +{ + int nb, port; + + nb = inet_get_free_port_multiple(1, &port, ipv6); + g_assert_cmpint(nb, ==, 1); + + return port; +} + +static void test_stream_inet_ipv4(void) +{ + QTestState *qts0, *qts1; + char *expect; + int port; + + port = inet_get_free_port(false); + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=inet," + "addr.ipv4=on,addr.ipv6=off," + "addr.host=127.0.0.1,addr.port=%d", port); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,server=false,id=st0,addr.type=inet," + "addr.ipv4=on,addr.ipv6=off," + "addr.host=127.0.0.1,addr.port=%d", port); + + expect = g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n", + port); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + /* the port is unknown, check only the address */ + EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':'); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +static void test_stream_inet_ipv6(void) +{ + QTestState *qts0, *qts1; + char *expect; + int port; + + port = inet_get_free_port(true); + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=inet," + "addr.ipv4=off,addr.ipv6=on," + "addr.host=::1,addr.port=%d", port); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,server=false,id=st0,addr.type=inet," + "addr.ipv4=off,addr.ipv6=on," + "addr.host=::1,addr.port=%d", port); + + expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n", + port); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + /* the port is unknown, check only the address */ + EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':'); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +static void test_stream_unix(void) +{ + QTestState *qts0, *qts1; + char *expect; + gchar *path; + + path = g_strconcat(tmpdir, "/stream_unix", NULL); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true," + "addr.type=unix,addr.path=%s,", + path); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=false," + "addr.type=unix,addr.path=%s", + path); + + expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path); + EXPECT_STATE(qts1, expect, 0); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + g_free(path); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +#ifdef CONFIG_LINUX +static void test_stream_unix_abstract(void) +{ + QTestState *qts0, *qts1; + char *expect; + gchar *path; + + path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true," + "addr.type=unix,addr.path=%s," + "addr.abstract=on", + path); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=false," + "addr.type=unix,addr.path=%s,addr.abstract=on", + path); + + expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path); + EXPECT_STATE(qts1, expect, 0); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + g_free(path); + + qtest_quit(qts1); + qtest_quit(qts0); +} +#endif + +#ifndef _WIN32 +static void test_stream_fd(void) +{ + QTestState *qts0, *qts1; + int sock[2]; + int ret; + + ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock); + g_assert_true(ret == 0); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,addr.type=fd,addr.str=%d", + sock[0]); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,addr.type=fd,addr.str=%d", + sock[1]); + + EXPECT_STATE(qts1, "st0: index=0,type=stream,unix:\r\n", 0); + EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0); + + qtest_quit(qts1); + qtest_quit(qts0); + + closesocket(sock[0]); + closesocket(sock[1]); +} +#endif + +static void test_dgram_inet(void) +{ + QTestState *qts0, *qts1; + char *expect; + int port[2]; + int nb; + + nb = inet_get_free_port_multiple(2, port, false); + g_assert_cmpint(nb, ==, 2); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0," + "local.type=inet,local.host=127.0.0.1,local.port=%d," + "remote.type=inet,remote.host=127.0.0.1,remote.port=%d", + port[0], port[1]); + + expect = g_strdup_printf("st0: index=0,type=dgram," + "udp=127.0.0.1:%d/127.0.0.1:%d\r\n", + port[0], port[1]); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0," + "local.type=inet,local.host=127.0.0.1,local.port=%d," + "remote.type=inet,remote.host=127.0.0.1,remote.port=%d", + port[1], port[0]); + + expect = g_strdup_printf("st0: index=0,type=dgram," + "udp=127.0.0.1:%d/127.0.0.1:%d\r\n", + port[1], port[0]); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +#ifndef _WIN32 +static void test_dgram_mcast(void) +{ + QTestState *qts; + + qts = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0," + "remote.type=inet,remote.host=230.0.0.1,remote.port=1234"); + + EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0); + + qtest_quit(qts); +} + +static void test_dgram_unix(void) +{ + QTestState *qts0, *qts1; + char *expect; + gchar *path0, *path1; + + path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL); + path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=unix,local.path=%s," + "remote.type=unix,remote.path=%s", + path0, path1); + + expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n", + path0, path1); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=unix,local.path=%s," + "remote.type=unix,remote.path=%s", + path1, path0); + + + expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n", + path1, path0); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + unlink(path0); + g_free(path0); + unlink(path1); + g_free(path1); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +static void test_dgram_fd(void) +{ + QTestState *qts0, *qts1; + char *expect; + int ret; + int sv[2]; + + ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv); + g_assert_cmpint(ret, !=, -1); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=fd,local.str=%d", + sv[0]); + + expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=fd,local.str=%d", + sv[1]); + + + expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + qtest_quit(qts1); + qtest_quit(qts0); + + closesocket(sv[0]); + closesocket(sv[1]); +} +#endif + +int main(int argc, char **argv) +{ + int ret; + bool has_ipv4, has_ipv6, has_afunix; + g_autoptr(GError) err = NULL; + + socket_init(); + g_test_init(&argc, &argv, NULL); + + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + g_error("socket_check_protocol_support() failed\n"); + } + + tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err); + if (tmpdir == NULL) { + g_error("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); + } + + if (has_ipv4) { + qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4); + qtest_add_func("/netdev/dgram/inet", test_dgram_inet); +#ifndef _WIN32 + qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast); +#endif + } + if (has_ipv6) { + qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6); + } + + socket_check_afunix_support(&has_afunix); + if (has_afunix) { +#ifndef _WIN32 + qtest_add_func("/netdev/dgram/unix", test_dgram_unix); +#endif + qtest_add_func("/netdev/stream/unix", test_stream_unix); +#ifdef CONFIG_LINUX + qtest_add_func("/netdev/stream/unix/abstract", + test_stream_unix_abstract); +#endif +#ifndef _WIN32 + qtest_add_func("/netdev/stream/fd", test_stream_fd); + qtest_add_func("/netdev/dgram/fd", test_dgram_fd); +#endif + } + + ret = g_test_run(); + + g_rmdir(tmpdir); + g_free(tmpdir); + + return ret; +} diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index d380261f8f..d677f87c8e 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -14,7 +14,7 @@ #include "qemu/cutils.h" #include "libqtest.h" -static bool verbose; +static int verbosity_level; static void test_properties(QTestState *qts, const char *path, bool recurse) { @@ -24,7 +24,9 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) QListEntry *entry; GSList *children = NULL, *links = NULL; - g_test_message("Obtaining properties of %s", path); + if (verbosity_level >= 2) { + g_test_message("Obtaining properties of %s", path); + } response = qtest_qmp(qts, "{ 'execute': 'qom-list'," " 'arguments': { 'path': %s } }", path); g_assert(response); @@ -51,7 +53,7 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) } } else { const char *prop = qdict_get_str(tuple, "name"); - if (verbose) { + if (verbosity_level >= 3) { g_test_message("-> %s", prop); } tmp = qtest_qmp(qts, @@ -109,8 +111,8 @@ int main(int argc, char **argv) { char *v_env = getenv("V"); - if (v_env && atoi(v_env) >= 2) { - verbose = true; + if (v_env) { + verbosity_level = atoi(v_env); } g_test_init(&argc, &argv, NULL); diff --git a/tests/qtest/vnc-display-test.c b/tests/qtest/vnc-display-test.c index e2a9d682bb..e52a4326ec 100644 --- a/tests/qtest/vnc-display-test.c +++ b/tests/qtest/vnc-display-test.c @@ -19,6 +19,8 @@ typedef struct Test { GMainLoop *loop; } Test; +#if !defined(WIN32) && !defined(CONFIG_DARWIN) + static void on_vnc_error(VncConnection* self, const char* msg) { @@ -31,16 +33,21 @@ static void on_vnc_auth_failure(VncConnection *self, g_error("vnc-auth-failure: %s", msg); } +#endif + static bool test_setup(Test *test) { #ifdef WIN32 g_test_skip("Not supported on Windows yet"); return false; +#elif defined(CONFIG_DARWIN) + g_test_skip("Broken on Darwin"); + return false; #else int pair[2]; - test->qts = qtest_init("-vnc none -name vnc-test"); + test->qts = qtest_init("-M none -vnc none -name vnc-test"); g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 14bc013181..a3b0aaf8af 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -54,10 +54,10 @@ cc-option = if $(call cc-test, $1); then \ # $1 = test name, $2 = cmd, $3 = desc ifeq ($(filter %-softmmu, $(TARGET)),) -run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2 > $1.out, \ +run-test = $(call quiet-command, timeout -s KILL --foreground $(TIMEOUT) $2 > $1.out, \ TEST,$(or $3, $*, $<) on $(TARGET_NAME)) else -run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2, \ +run-test = $(call quiet-command, timeout -s KILL --foreground $(TIMEOUT) $2, \ TEST,$(or $3, $*, $<) on $(TARGET_NAME)) endif diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index a1368905f5..df9747bae8 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -36,6 +36,13 @@ config-cc.mak: Makefile memory: CFLAGS+=-DCHECK_UNALIGNED=1 +memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3 -fno-tree-loop-distribute-patterns + +TESTS+=memory-sve + # Running QEMU_BASE_MACHINE=-M virt -cpu max -display none QEMU_OPTS+=$(QEMU_BASE_MACHINE) -semihosting-config enable=on,target=native,chardev=output -kernel diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S index e190b1efa6..f136363d2a 100644 --- a/tests/tcg/aarch64/system/boot.S +++ b/tests/tcg/aarch64/system/boot.S @@ -179,12 +179,13 @@ __start: isb /* - * Enable FP registers. The standard C pre-amble will be + * Enable FP/SVE registers. The standard C pre-amble will be * saving these and A-profile compilers will use AdvSIMD * registers unless we tell it not to. */ mrs x0, cpacr_el1 orr x0, x0, #(3 << 20) + orr x0, x0, #(3 << 16) msr cpacr_el1, x0 /* Setup some stack space and enter the test code. diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index e7213af492..ae8b3d7268 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -42,6 +42,15 @@ munmap-pthread: LDFLAGS+=-pthread vma-pthread: CFLAGS+=-pthread vma-pthread: LDFLAGS+=-pthread +# The vma-pthread seems very sensitive on gitlab and we currently +# don't know if its exposing a real bug or the test is flaky. +ifneq ($(GITLAB_CI),) +run-vma-pthread: vma-pthread + $(call skip-test, $<, "flaky on CI?") +run-plugin-vma-pthread-with-%: vma-pthread + $(call skip-test, $<, "flaky on CI?") +endif + # We define the runner for test-mmap after the individual # architectures have defined their supported pages sizes. If no # additional page sizes are defined we only run the default test. diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index ff5147f619..6dfac6468a 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -832,7 +832,10 @@ static void test_attach_second_node(void) qdict_put_str(options, "driver", "raw"); qdict_put_str(options, "file", "base"); + aio_context_acquire(ctx); filter = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); + aio_context_release(ctx); + g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(bs) == ctx); g_assert(bdrv_get_aio_context(filter) == ctx); diff --git a/tests/unit/test-io-channel-command.c b/tests/unit/test-io-channel-command.c index 19f72eab96..425e2f5594 100644 --- a/tests/unit/test-io-channel-command.c +++ b/tests/unit/test-io-channel-command.c @@ -20,6 +20,8 @@ #include "qemu/osdep.h" #include <glib/gstdio.h> +#include <sys/types.h> +#include <sys/stat.h> #include "io/channel-command.h" #include "io-channel-helpers.h" #include "qapi/error.h" @@ -29,6 +31,7 @@ static char *socat = NULL; +#ifndef _WIN32 static void test_io_channel_command_fifo(bool async) { g_autofree gchar *tmpdir = g_dir_make_tmp("qemu-test-io-channel.XXXXXX", NULL); @@ -40,12 +43,13 @@ static void test_io_channel_command_fifo(bool async) QIOChannel *src, *dst; QIOChannelTest *test; + if (mkfifo(fifo, 0600)) { + g_error("mkfifo: %s", strerror(errno)); + } + src = QIO_CHANNEL(qio_channel_command_new_spawn((const char **) srcargv, O_WRONLY, &error_abort)); - /* try to avoid a race to create the socket */ - g_usleep(1000); - dst = QIO_CHANNEL(qio_channel_command_new_spawn((const char **) dstargv, O_RDONLY, &error_abort)); @@ -60,7 +64,6 @@ static void test_io_channel_command_fifo(bool async) g_rmdir(tmpdir); } - static void test_io_channel_command_fifo_async(void) { if (!socat) { @@ -80,6 +83,7 @@ static void test_io_channel_command_fifo_sync(void) test_io_channel_command_fifo(false); } +#endif static void test_io_channel_command_echo(bool async) @@ -124,10 +128,12 @@ int main(int argc, char **argv) socat = g_find_program_in_path("socat"); +#ifndef _WIN32 g_test_add_func("/io/channel/command/fifo/sync", test_io_channel_command_fifo_sync); g_test_add_func("/io/channel/command/fifo/async", test_io_channel_command_fifo_async); +#endif g_test_add_func("/io/channel/command/echo/sync", test_io_channel_command_echo_sync); g_test_add_func("/io/channel/command/echo/async", diff --git a/tests/vm/centos.aarch64 b/tests/vm/centos.aarch64 index 2de7ef6992..3f58de1e64 100755 --- a/tests/vm/centos.aarch64 +++ b/tests/vm/centos.aarch64 @@ -28,7 +28,7 @@ DEFAULT_CONFIG = { "dnf config-manager --set-enabled powertools, " "dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo, " "dnf install -y make ninja-build git python38 gcc gcc-c++ flex bison "\ - "glib2-devel perl pixman-devel zlib-devel docker-ce.aarch64, " + "glib2-devel pixman-devel zlib-devel docker-ce.aarch64, " "systemctl enable docker, " ), # We increase beyond the default time since during boot diff --git a/trace/meson.build b/trace/meson.build index 26b54714d5..d565948b09 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,3 +1,4 @@ +softmmu_ss.add(files('trace-hmp-cmds.c')) specific_ss.add(files('control-target.c')) diff --git a/trace/trace-hmp-cmds.c b/trace/trace-hmp-cmds.c new file mode 100644 index 0000000000..792876c34a --- /dev/null +++ b/trace/trace-hmp-cmds.c @@ -0,0 +1,148 @@ +/* + * HMP commands related to tracing + * + * Copyright (c) 2003-2004 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. + */ + +#include "qemu/osdep.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-trace.h" +#include "qapi/qmp/qdict.h" +#include "trace/control.h" +#ifdef CONFIG_TRACE_SIMPLE +#include "trace/simple.h" +#endif + +void hmp_trace_event(Monitor *mon, const QDict *qdict) +{ + const char *tp_name = qdict_get_str(qdict, "name"); + bool new_state = qdict_get_bool(qdict, "option"); + bool has_vcpu = qdict_haskey(qdict, "vcpu"); + int vcpu = qdict_get_try_int(qdict, "vcpu", 0); + Error *local_err = NULL; + + if (vcpu < 0) { + monitor_printf(mon, "argument vcpu must be positive"); + return; + } + + qmp_trace_event_set_state(tp_name, new_state, true, true, has_vcpu, vcpu, &local_err); + if (local_err) { + error_report_err(local_err); + } +} + +#ifdef CONFIG_TRACE_SIMPLE +void hmp_trace_file(Monitor *mon, const QDict *qdict) +{ + const char *op = qdict_get_try_str(qdict, "op"); + const char *arg = qdict_get_try_str(qdict, "arg"); + + if (!op) { + st_print_trace_file_status(); + } else if (!strcmp(op, "on")) { + st_set_trace_file_enabled(true); + } else if (!strcmp(op, "off")) { + st_set_trace_file_enabled(false); + } else if (!strcmp(op, "flush")) { + st_flush_trace_buffer(); + } else if (!strcmp(op, "set")) { + if (arg) { + st_set_trace_file(arg); + } + } else { + monitor_printf(mon, "unexpected argument \"%s\"\n", op); + hmp_help_cmd(mon, "trace-file"); + } +} +#endif + +void hmp_info_trace_events(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_try_str(qdict, "name"); + bool has_vcpu = qdict_haskey(qdict, "vcpu"); + int vcpu = qdict_get_try_int(qdict, "vcpu", 0); + TraceEventInfoList *events; + TraceEventInfoList *elem; + Error *local_err = NULL; + + if (name == NULL) { + name = "*"; + } + if (vcpu < 0) { + monitor_printf(mon, "argument vcpu must be positive"); + return; + } + + events = qmp_trace_event_get_state(name, has_vcpu, vcpu, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + for (elem = events; elem != NULL; elem = elem->next) { + monitor_printf(mon, "%s : state %u\n", + elem->value->name, + elem->value->state == TRACE_EVENT_STATE_ENABLED ? 1 : 0); + } + qapi_free_TraceEventInfoList(events); +} + +void info_trace_events_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + TraceEventIter iter; + TraceEvent *ev; + char *pattern = g_strdup_printf("%s*", str); + trace_event_iter_init_pattern(&iter, pattern); + while ((ev = trace_event_iter_next(&iter)) != NULL) { + readline_add_completion(rs, trace_event_get_name(ev)); + } + g_free(pattern); + } +} + +void trace_event_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + TraceEventIter iter; + TraceEvent *ev; + char *pattern = g_strdup_printf("%s*", str); + trace_event_iter_init_pattern(&iter, pattern); + while ((ev = trace_event_iter_next(&iter)) != NULL) { + readline_add_completion(rs, trace_event_get_name(ev)); + } + g_free(pattern); + } else if (nb_args == 3) { + readline_add_completion_of(rs, str, "on"); + readline_add_completion_of(rs, str, "off"); + } +} diff --git a/util/qht.c b/util/qht.c index 065fc501f4..92c6b78759 100644 --- a/util/qht.c +++ b/util/qht.c @@ -151,6 +151,22 @@ struct qht_bucket { QEMU_BUILD_BUG_ON(sizeof(struct qht_bucket) > QHT_BUCKET_ALIGN); +/* + * Under TSAN, we use striped locks instead of one lock per bucket chain. + * This avoids crashing under TSAN, since TSAN aborts the program if more than + * 64 locks are held (this is a hardcoded limit in TSAN). + * When resizing a QHT we grab all the buckets' locks, which can easily + * go over TSAN's limit. By using striped locks, we avoid this problem. + * + * Note: this number must be a power of two for easy index computation. + */ +#define QHT_TSAN_BUCKET_LOCKS_BITS 4 +#define QHT_TSAN_BUCKET_LOCKS (1 << QHT_TSAN_BUCKET_LOCKS_BITS) + +struct qht_tsan_lock { + QemuSpin lock; +} QEMU_ALIGNED(QHT_BUCKET_ALIGN); + /** * struct qht_map - structure to track an array of buckets * @rcu: used by RCU. Keep it as the top field in the struct to help valgrind @@ -160,6 +176,7 @@ QEMU_BUILD_BUG_ON(sizeof(struct qht_bucket) > QHT_BUCKET_ALIGN); * @n_added_buckets: number of added (i.e. "non-head") buckets * @n_added_buckets_threshold: threshold to trigger an upward resize once the * number of added buckets surpasses it. + * @tsan_bucket_locks: Array of striped locks to be used only under TSAN. * * Buckets are tracked in what we call a "map", i.e. this structure. */ @@ -169,6 +186,9 @@ struct qht_map { size_t n_buckets; size_t n_added_buckets; size_t n_added_buckets_threshold; +#ifdef CONFIG_TSAN + struct qht_tsan_lock tsan_bucket_locks[QHT_TSAN_BUCKET_LOCKS]; +#endif }; /* trigger a resize when n_added_buckets > n_buckets / div */ @@ -229,10 +249,56 @@ static inline size_t qht_elems_to_buckets(size_t n_elems) return pow2ceil(n_elems / QHT_BUCKET_ENTRIES); } -static inline void qht_head_init(struct qht_bucket *b) +/* + * When using striped locks (i.e. under TSAN), we have to be careful not + * to operate on the same lock twice (e.g. when iterating through all buckets). + * We achieve this by operating only on each stripe's first matching lock. + */ +static inline void qht_do_if_first_in_stripe(struct qht_map *map, + struct qht_bucket *b, + void (*func)(QemuSpin *spin)) +{ +#ifdef CONFIG_TSAN + unsigned long bucket_idx = b - map->buckets; + bool is_first_in_stripe = (bucket_idx >> QHT_TSAN_BUCKET_LOCKS_BITS) == 0; + if (is_first_in_stripe) { + unsigned long lock_idx = bucket_idx & (QHT_TSAN_BUCKET_LOCKS - 1); + func(&map->tsan_bucket_locks[lock_idx].lock); + } +#else + func(&b->lock); +#endif +} + +static inline void qht_bucket_lock_do(struct qht_map *map, + struct qht_bucket *b, + void (*func)(QemuSpin *lock)) +{ +#ifdef CONFIG_TSAN + unsigned long bucket_idx = b - map->buckets; + unsigned long lock_idx = bucket_idx & (QHT_TSAN_BUCKET_LOCKS - 1); + func(&map->tsan_bucket_locks[lock_idx].lock); +#else + func(&b->lock); +#endif +} + +static inline void qht_bucket_lock(struct qht_map *map, + struct qht_bucket *b) +{ + qht_bucket_lock_do(map, b, qemu_spin_lock); +} + +static inline void qht_bucket_unlock(struct qht_map *map, + struct qht_bucket *b) +{ + qht_bucket_lock_do(map, b, qemu_spin_unlock); +} + +static inline void qht_head_init(struct qht_map *map, struct qht_bucket *b) { memset(b, 0, sizeof(*b)); - qemu_spin_init(&b->lock); + qht_do_if_first_in_stripe(map, b, qemu_spin_init); seqlock_init(&b->sequence); } @@ -250,7 +316,7 @@ static void qht_map_lock_buckets(struct qht_map *map) for (i = 0; i < map->n_buckets; i++) { struct qht_bucket *b = &map->buckets[i]; - qemu_spin_lock(&b->lock); + qht_do_if_first_in_stripe(map, b, qemu_spin_lock); } } @@ -261,7 +327,7 @@ static void qht_map_unlock_buckets(struct qht_map *map) for (i = 0; i < map->n_buckets; i++) { struct qht_bucket *b = &map->buckets[i]; - qemu_spin_unlock(&b->lock); + qht_do_if_first_in_stripe(map, b, qemu_spin_unlock); } } @@ -308,7 +374,7 @@ void qht_map_lock_buckets__no_stale(struct qht *ht, struct qht_map **pmap) * Get a head bucket and lock it, making sure its parent map is not stale. * @pmap is filled with a pointer to the bucket's parent map. * - * Unlock with qemu_spin_unlock(&b->lock). + * Unlock with qht_bucket_unlock. * * Note: callers cannot have ht->lock held. */ @@ -322,18 +388,18 @@ struct qht_bucket *qht_bucket_lock__no_stale(struct qht *ht, uint32_t hash, map = qatomic_rcu_read(&ht->map); b = qht_map_to_bucket(map, hash); - qemu_spin_lock(&b->lock); + qht_bucket_lock(map, b); if (likely(!qht_map_is_stale__locked(ht, map))) { *pmap = map; return b; } - qemu_spin_unlock(&b->lock); + qht_bucket_unlock(map, b); /* we raced with a resize; acquire ht->lock to see the updated ht->map */ qht_lock(ht); map = ht->map; b = qht_map_to_bucket(map, hash); - qemu_spin_lock(&b->lock); + qht_bucket_lock(map, b); qht_unlock(ht); *pmap = map; return b; @@ -345,12 +411,13 @@ static inline bool qht_map_needs_resize(const struct qht_map *map) map->n_added_buckets_threshold; } -static inline void qht_chain_destroy(const struct qht_bucket *head) +static inline void qht_chain_destroy(struct qht_map *map, + struct qht_bucket *head) { struct qht_bucket *curr = head->next; struct qht_bucket *prev; - qemu_spin_destroy(&head->lock); + qht_do_if_first_in_stripe(map, head, qemu_spin_destroy); while (curr) { prev = curr; curr = curr->next; @@ -364,7 +431,7 @@ static void qht_map_destroy(struct qht_map *map) size_t i; for (i = 0; i < map->n_buckets; i++) { - qht_chain_destroy(&map->buckets[i]); + qht_chain_destroy(map, &map->buckets[i]); } qemu_vfree(map->buckets); g_free(map); @@ -390,7 +457,7 @@ static struct qht_map *qht_map_create(size_t n_buckets) map->buckets = qemu_memalign(QHT_BUCKET_ALIGN, sizeof(*map->buckets) * n_buckets); for (i = 0; i < n_buckets; i++) { - qht_head_init(&map->buckets[i]); + qht_head_init(map, &map->buckets[i]); } return map; } @@ -638,7 +705,7 @@ bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing) b = qht_bucket_lock__no_stale(ht, hash, &map); prev = qht_insert__locked(ht, map, b, p, hash, &needs_resize); qht_bucket_debug__locked(b); - qemu_spin_unlock(&b->lock); + qht_bucket_unlock(map, b); if (unlikely(needs_resize) && ht->mode & QHT_MODE_AUTO_RESIZE) { qht_grow_maybe(ht); @@ -688,7 +755,7 @@ static inline void qht_bucket_remove_entry(struct qht_bucket *orig, int pos) int i; if (qht_entry_is_last(orig, pos)) { - orig->hashes[pos] = 0; + qatomic_set(&orig->hashes[pos], 0); qatomic_set(&orig->pointers[pos], NULL); return; } @@ -749,7 +816,7 @@ bool qht_remove(struct qht *ht, const void *p, uint32_t hash) b = qht_bucket_lock__no_stale(ht, hash, &map); ret = qht_remove__locked(b, p, hash); qht_bucket_debug__locked(b); - qemu_spin_unlock(&b->lock); + qht_bucket_unlock(map, b); return ret; } diff --git a/util/readline.c b/util/readline.c index f1ac6e4769..494a3d924e 100644 --- a/util/readline.c +++ b/util/readline.c @@ -286,6 +286,14 @@ void readline_add_completion(ReadLineState *rs, const char *str) } } +void readline_add_completion_of(ReadLineState *rs, + const char *pfx, const char *str) +{ + if (!strncmp(str, pfx, strlen(pfx))) { + readline_add_completion(rs, str); + } +} + void readline_set_completion_index(ReadLineState *rs, int index) { rs->completion_index = index; |