diff options
256 files changed, 3719 insertions, 2234 deletions
@@ -44,6 +44,7 @@ Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com> Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> <arikalo@wavecomp.com> Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> +Leif Lindholm <leif@nuviainc.com> <leif.lindholm@linaro.org> Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com> Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com> Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org> diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000000..8355dbc634 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,20 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# We want all the document formats +formats: all + +# For consistency, we require that QEMU's Sphinx extensions +# run with at least the same minimum version of Python that +# we require for other Python in our codebase (our conf.py +# enforces this, and some code needs it.) +python: + version: 3.5 diff --git a/.shippable.yml b/.shippable.yml index 83aae08bb4..2cce7b5689 100644 --- a/.shippable.yml +++ b/.shippable.yml @@ -37,5 +37,5 @@ build: - unset CC - mkdir build - cd build - - ../configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} + - ../configure --disable-docs ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) diff --git a/.travis.yml b/.travis.yml index 1ae645e9fc..5887055951 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,6 +51,8 @@ addons: - sparse - uuid-dev - gcovr + # Tests dependencies + - genisoimage # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu @@ -69,6 +71,7 @@ env: - SRC_DIR=".." - BUILD_DIR="build" - BASE_CONFIG="--disable-docs --disable-tools" + - TEST_BUILD_CMD="" - TEST_CMD="make check V=1" # This is broadly a list of "mainline" softmmu targets which have support across the major distros - MAIN_SOFTMMU_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" @@ -80,80 +83,110 @@ git: # we want to do this ourselves submodules: false +# Common first phase for all steps +before_install: + - if command -v ccache ; then ccache --zero-stats ; fi + - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - echo "=== Using ${JOBS} simultaneous jobs ===" +# Configure step - may be overridden before_script: - - if command -v ccache ; then ccache --zero-stats ; fi - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } + +# Main build & test - rarely overridden - controlled by TEST_CMD script: - - BUILD_RC=0 && make -j3 || BUILD_RC=$? - - if [ "$BUILD_RC" -eq 0 ] ; then travis_retry ${TEST_CMD} ; else $(exit $BUILD_RC); fi + - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? + - | + if [ "$BUILD_RC" -eq 0 ] && [ -n "$TEST_BUILD_CMD" ]; then + ${TEST_BUILD_CMD} || BUILD_RC=$? + else + $(exit $BUILD_RC); + fi + - | + if [ "$BUILD_RC" -eq 0 ] ; then + ${TEST_CMD} ; + else + $(exit $BUILD_RC); + fi after_script: - if command -v ccache ; then ccache --show-stats ; fi matrix: include: - - env: + - name: "GCC static (user)" + env: - CONFIG="--disable-system --static" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" # we split the system builds as it takes a while to build them all - - env: + - name: "GCC (main-softmmu)" + env: - CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - - env: - - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" + - name: "GCC (other-softmmu)" + env: + - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" # Just build tools and run minimal unit and softfloat checks - - env: + - name: "GCC check-softfloat (user)" + env: - BASE_CONFIG="--enable-tools" - CONFIG="--disable-user --disable-system" - - TEST_CMD="make check-unit check-softfloat -j3" + - TEST_CMD="make check-unit check-softfloat -j${JOBS}" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" # --enable-debug implies --enable-debug-tcg, also runs quite a bit slower - - env: + - name: "GCC debug (main-softmmu)" + env: - CONFIG="--enable-debug --target-list=${MAIN_SOFTMMU_TARGETS}" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug" # TCG debug can be run just on its own and is mostly agnostic to user/softmmu distinctions - - env: + - name: "GCC debug (user)" + env: - CONFIG="--enable-debug-tcg --disable-system" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" - - env: + - name: "GCC some libs disabled (main-softmmu)" + env: - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}" # Module builds are mostly of interest to major distros - - env: + - name: "GCC modules (main-softmmu)" + env: - CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" # Alternate coroutines implementations are only really of interest to KVM users # However we can't test against KVM on Travis so we can only run unit tests - - env: + - name: "check-unit coroutine=ucontext" + env: - CONFIG="--with-coroutine=ucontext --disable-tcg" - - TEST_CMD="make check-unit -j3 V=1" + - TEST_CMD="make check-unit -j${JOBS} V=1" - - env: + - name: "check-unit coroutine=sigaltstack" + env: - CONFIG="--with-coroutine=sigaltstack --disable-tcg" - - TEST_CMD="make check-unit -j3 V=1" + - TEST_CMD="make check-unit -j${JOBS} V=1" # Check we can build docs and tools (out of tree) - - env: + - name: "tools and docs (bionic)" + dist: bionic + env: - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.." - BASE_CONFIG="--enable-tools --enable-docs" - CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user" @@ -161,19 +194,21 @@ matrix: addons: apt: packages: - - python-sphinx + - python3-sphinx - texinfo - perl # Test with Clang for compile portability (Travis uses clang-5.0) - - env: + - name: "Clang (user)" + env: - CONFIG="--disable-system" - CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default" compiler: clang - - env: + - name: "Clang (main-softmmu)" + env: - CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} " - CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-sanitize" compiler: clang @@ -182,52 +217,60 @@ matrix: - ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; } - - env: + - name: "Clang (other-softmmu)" + env: - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" - CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default" compiler: clang # gprof/gcov are GCC features - - env: + - name: "GCC gprof/gcov" + env: - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}" after_success: - ${SRC_DIR}/scripts/travis/coverage-summary.sh # We manually include builds which we disable "make check" for - - env: + - name: "GCC without-default-devices (softmmu)" + env: - CONFIG="--without-default-devices --disable-user" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - TEST_CMD="" # Check the TCG interpreter (TCI) - - env: + - name: "GCC TCI" + env: - CONFIG="--enable-debug-tcg --enable-tcg-interpreter --disable-kvm --disable-containers --target-list=alpha-softmmu,arm-softmmu,hppa-softmmu,m68k-softmmu,microblaze-softmmu,moxie-softmmu,ppc-softmmu,s390x-softmmu,x86_64-softmmu" - TEST_CMD="make check-qtest check-tcg V=1" # We don't need to exercise every backend with every front-end - - env: + - name: "GCC trace log,simple,syslog (user)" + env: - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" - TEST_CMD="" - - env: + - name: "GCC trace ftrace (x86_64-softmmu)" + env: - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" - TEST_CMD="" - - env: + - name: "GCC trace ust (x86_64-softmmu)" + env: - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" - TEST_CMD="" # MacOSX builds - cirrus.yml also tests some MacOS builds including latest Xcode - - env: + - name: "OSX Xcode 10.3" + env: - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" os: osx osx_image: xcode10.3 @@ -244,13 +287,13 @@ matrix: before_script: - brew link --overwrite python - export PATH="/usr/local/opt/ccache/libexec:$PATH" - - if command -v ccache ; then ccache --zero-stats ; fi - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } # Python builds - - env: + - name: "GCC Python 3.5 (x86_64-softmmu)" + env: - CONFIG="--target-list=x86_64-softmmu" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" language: python @@ -258,7 +301,8 @@ matrix: - "3.5" - - env: + - name: "GCC Python 3.6 (x86_64-softmmu)" + env: - CONFIG="--target-list=x86_64-softmmu" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" language: python @@ -267,8 +311,9 @@ matrix: # Acceptance (Functional) tests - - env: - - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,sparc-softmmu" + - name: "GCC check-acceptance" + env: + - CONFIG="--target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu" - TEST_CMD="make check-acceptance" after_script: - python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP")]' | xargs cat @@ -278,12 +323,14 @@ matrix: - python3-pil - python3-pip - python3.5-venv + - rpm2cpio - tesseract-ocr - tesseract-ocr-eng # Using newer GCC with sanitizers - - addons: + - name: "GCC9 with sanitizers (softmmu)" + addons: apt: update: true sources: @@ -331,34 +378,44 @@ matrix: # Run check-tcg against linux-user - - env: + - name: "GCC check-tcg (user)" + env: - CONFIG="--disable-system --enable-debug-tcg" - - TEST_CMD="make -j3 check-tcg V=1" + - TEST_BUILD_CMD="make build-tcg" + - TEST_CMD="make check-tcg" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" # Run check-tcg against linux-user (with plugins) # we skip sparc64-linux-user until it has been fixed somewhat - - env: - - CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user" - - TEST_CMD="make -j3 check-tcg V=1" + # we skip cris-linux-user as it doesn't use the common run loop + - name: "GCC plugins check-tcg (user)" + env: + - CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user" + - TEST_BUILD_CMD="make build-tcg" + - TEST_CMD="make check-tcg" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" # Run check-tcg against softmmu targets - - env: + - name: "GCC check-tcg (some-softmmu)" + env: - CONFIG="--enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu" - - TEST_CMD="make -j3 check-tcg V=1" + - TEST_BUILD_CMD="make -j${JOBS} build-tcg" + - TEST_CMD="make check-tcg" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" # Run check-tcg against softmmu targets (with plugins) - - env: + - name: "GCC plugins check-tcg (some-softmmu)" + env: - CONFIG="--enable-plugins --enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu" - - TEST_CMD="make -j3 check-tcg V=1" + - TEST_BUILD_CMD="make -j${JOBS} build-tcg" + - TEST_CMD="make check-tcg" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" - - arch: arm64 + - name: "[aarch64] GCC check-tcg" + arch: arm64 dist: xenial addons: apt_packages: @@ -383,11 +440,14 @@ matrix: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + # Tests dependencies + - genisoimage env: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS}" - - arch: ppc64le + - name: "[ppc64] GCC check-tcg" + arch: ppc64le dist: xenial addons: apt_packages: @@ -412,11 +472,14 @@ matrix: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + # Tests dependencies + - genisoimage env: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},ppc64le-linux-user" - - arch: s390x + - name: "[s390x] GCC check-tcg" + arch: s390x dist: bionic addons: apt_packages: @@ -441,6 +504,8 @@ matrix: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + # Tests dependencies + - genisoimage env: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" @@ -448,18 +513,16 @@ matrix: # Release builds # The make-release script expect a QEMU version, so our tag must start with a 'v'. # This is the case when release candidate tags are created. - - if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/ + - name: "Release tarball" + if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/ env: # We want to build from the release tarball - BUILD_DIR="release/build/dir" SRC_DIR="../../.." - BASE_CONFIG="--prefix=$PWD/dist" - CONFIG="--target-list=x86_64-softmmu,aarch64-softmmu,armeb-linux-user,ppc-linux-user" - - TEST_CMD="make install -j3" + - TEST_CMD="make install -j${JOBS}" - QEMU_VERSION="${TRAVIS_TAG:1}" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - before_script: - - command -v ccache && ccache --zero-stats - - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} script: - make -C ${SRC_DIR} qemu-${QEMU_VERSION}.tar.bz2 - ls -l ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 diff --git a/MAINTAINERS b/MAINTAINERS index 1f0bc72f21..c7717df720 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -737,7 +737,7 @@ F: include/hw/ssi/imx_spi.h SBSA-REF M: Radoslaw Biernacki <radoslaw.biernacki@linaro.org> M: Peter Maydell <peter.maydell@linaro.org> -R: Leif Lindholm <leif.lindholm@linaro.org> +R: Leif Lindholm <leif@nuviainc.com> L: qemu-arm@nongnu.org S: Maintained F: hw/arm/sbsa-ref.c @@ -809,6 +809,7 @@ F: hw/arm/virt-acpi-build.c STM32F205 M: Alistair Francis <alistair@alistair23.me> M: Peter Maydell <peter.maydell@linaro.org> +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/stm32f205_soc.c F: hw/misc/stm32f2xx_syscfg.c @@ -821,6 +822,7 @@ F: include/hw/*/stm32*.h STM32F405 M: Alistair Francis <alistair@alistair23.me> M: Peter Maydell <peter.maydell@linaro.org> +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/stm32f405_soc.c F: hw/misc/stm32f4xx_syscfg.c @@ -829,18 +831,21 @@ F: hw/misc/stm32f4xx_exti.c Netduino 2 M: Alistair Francis <alistair@alistair23.me> M: Peter Maydell <peter.maydell@linaro.org> +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/netduino2.c Netduino Plus 2 M: Alistair Francis <alistair@alistair23.me> M: Peter Maydell <peter.maydell@linaro.org> +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/netduinoplus2.c SmartFusion2 M: Subbaraya Sundeep <sundeep.lkml@gmail.com> M: Peter Maydell <peter.maydell@linaro.org> +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-soc.c F: hw/misc/msf2-sysreg.c @@ -854,6 +859,7 @@ F: include/hw/ssi/mss-spi.h Emcraft M2S-FG484 M: Subbaraya Sundeep <sundeep.lkml@gmail.com> M: Peter Maydell <peter.maydell@linaro.org> +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-som.c @@ -896,6 +902,7 @@ HP B160L M: Richard Henderson <rth@twiddle.net> R: Helge Deller <deller@gmx.de> S: Odd Fixes +F: default-configs/hppa-softmmu.mak F: hw/hppa/ F: pc-bios/hppa-firmware.img @@ -1401,6 +1408,7 @@ T: git https://github.com/jnsnow/qemu.git ide OMAP M: Peter Maydell <peter.maydell@linaro.org> +L: qemu-arm@nongnu.org S: Maintained F: hw/*/omap* F: include/hw/arm/omap.h @@ -1570,6 +1578,7 @@ F: include/hw/virtio/ virtio-9p M: Greg Kurz <groug@kaod.org> +R: Christian Schoenebeck <qemu_oss@crudebyte.com> S: Odd Fixes F: hw/9pfs/ X: hw/9pfs/xen-9p* @@ -1604,6 +1613,7 @@ S: Supported F: tools/virtiofsd/* F: hw/virtio/vhost-user-fs* F: include/hw/virtio/vhost-user-fs.h +F: docs/interop/virtiofsd.rst virtio-input M: Gerd Hoffmann <kraxel@redhat.com> @@ -348,6 +348,9 @@ DOCS=qemu-doc.html qemu-doc.txt qemu.1 DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-img.1 DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-nbd.8 DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga.8 +ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) +DOCS+=$(MANUAL_BUILDDIR)/interop/virtiofsd.1 +endif DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 @@ -861,6 +864,9 @@ ifdef CONFIG_VIRTFS $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" $(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1" endif +ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) + $(INSTALL_DATA) docs/interop/virtiofsd.1 "$(DESTDIR)$(mandir)/man1" +endif install-datadir: $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)" @@ -1051,7 +1057,8 @@ $(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(call build-manual,system,html) $(call define-manpage-rule,interop,\ - qemu-ga.8 qemu-img.1 qemu-nbd.8 qemu-trace-stap.1 virtfs-proxy-helper.1,\ + qemu-ga.8 qemu-img.1 qemu-nbd.8 qemu-trace-stap.1\ + virtiofsd.1 virtfs-proxy-helper.1,\ $(SRC_PATH/qemu-img-cmds.hx)) $(call define-manpage-rule,system,qemu-block-drivers.7) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index f37ce1ce85..a23a5a0b60 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -307,6 +307,13 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) return SND_PCM_FORMAT_U32_LE; } + case AUDIO_FORMAT_F32: + if (endianness) { + return SND_PCM_FORMAT_FLOAT_BE; + } else { + return SND_PCM_FORMAT_FLOAT_LE; + } + default: dolog ("Internal logic error: Bad audio format %d\n", fmt); #ifdef DEBUG_AUDIO @@ -370,6 +377,16 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt, *fmt = AUDIO_FORMAT_U32; break; + case SND_PCM_FORMAT_FLOAT_LE: + *endianness = 0; + *fmt = AUDIO_FORMAT_F32; + break; + + case SND_PCM_FORMAT_FLOAT_BE: + *endianness = 1; + *fmt = AUDIO_FORMAT_F32; + break; + default: dolog ("Unrecognized audio format %d\n", alsafmt); return -1; @@ -906,6 +923,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { .init_out = alsa_init_out, .fini_out = alsa_fini_out, .write = alsa_write, + .run_buffer_out = audio_generic_run_buffer_out, .enable_out = alsa_enable_out, .init_in = alsa_init_in, diff --git a/audio/audio.c b/audio/audio.c index f63f39769a..9ac9a20c41 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -218,6 +218,9 @@ static void audio_print_settings (struct audsettings *as) case AUDIO_FORMAT_U32: AUD_log (NULL, "U32"); break; + case AUDIO_FORMAT_F32: + AUD_log (NULL, "F32"); + break; default: AUD_log (NULL, "invalid(%d)", as->fmt); break; @@ -252,6 +255,7 @@ static int audio_validate_settings (struct audsettings *as) case AUDIO_FORMAT_U16: case AUDIO_FORMAT_S32: case AUDIO_FORMAT_U32: + case AUDIO_FORMAT_F32: break; default: invalid = 1; @@ -264,24 +268,28 @@ static int audio_validate_settings (struct audsettings *as) static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as) { - int bits = 8, sign = 0; + int bits = 8; + bool is_signed = false, is_float = false; switch (as->fmt) { case AUDIO_FORMAT_S8: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U8: break; case AUDIO_FORMAT_S16: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U16: bits = 16; break; + case AUDIO_FORMAT_F32: + is_float = true; + /* fall through */ case AUDIO_FORMAT_S32: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U32: bits = 32; @@ -292,33 +300,38 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a } return info->freq == as->freq && info->nchannels == as->nchannels - && info->sign == sign + && info->is_signed == is_signed + && info->is_float == is_float && info->bits == bits && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS); } void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) { - int bits = 8, sign = 0, mul; + int bits = 8, mul; + bool is_signed = false, is_float = false; switch (as->fmt) { case AUDIO_FORMAT_S8: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U8: mul = 1; break; case AUDIO_FORMAT_S16: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U16: bits = 16; mul = 2; break; + case AUDIO_FORMAT_F32: + is_float = true; + /* fall through */ case AUDIO_FORMAT_S32: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U32: bits = 32; @@ -331,7 +344,8 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) info->freq = as->freq; info->bits = bits; - info->sign = sign; + info->is_signed = is_signed; + info->is_float = is_float; info->nchannels = as->nchannels; info->bytes_per_frame = as->nchannels * mul; info->bytes_per_second = info->freq * info->bytes_per_frame; @@ -344,7 +358,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) return; } - if (info->sign) { + if (info->is_signed || info->is_float) { memset(buf, 0x00, len * info->bytes_per_frame); } else { @@ -770,8 +784,9 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) #ifdef DEBUG_AUDIO static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) { - dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n", - cap, info->bits, info->sign, info->freq, info->nchannels); + dolog("%s: bits %d, sign %d, float %d, freq %d, nchan %d\n", + cap, info->bits, info->is_signed, info->is_float, info->freq, + info->nchannels); } #endif @@ -879,9 +894,9 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size) } } -int AUD_get_buffer_size_out (SWVoiceOut *sw) +int AUD_get_buffer_size_out(SWVoiceOut *sw) { - return sw->hw->mix_buf->size * sw->hw->info.bytes_per_frame; + return sw->hw->samples * sw->hw->info.bytes_per_frame; } void AUD_set_active_out (SWVoiceOut *sw, int on) @@ -1076,10 +1091,8 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live) while (live) { size_t size, decr, proc; void *buf = hw->pcm_ops->get_buffer_out(hw, &size); - if (!buf) { - /* retrying will likely won't help, drop everything. */ - hw->mix_buf->pos = (hw->mix_buf->pos + live) % hw->mix_buf->size; - return clipped + live; + if (!buf || size == 0) { + break; } decr = MIN(size / hw->info.bytes_per_frame, live); @@ -1097,6 +1110,10 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live) } } + if (hw->pcm_ops->run_buffer_out) { + hw->pcm_ops->run_buffer_out(hw); + } + return clipped; } @@ -1403,7 +1420,8 @@ void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) } assert(start >= 0 && start < hw->size_emul); - *size = MIN(hw->pending_emul, hw->size_emul - start); + *size = MIN(*size, hw->pending_emul); + *size = MIN(*size, hw->size_emul - start); return hw->buf_emul + start; } @@ -1413,6 +1431,28 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size) hw->pending_emul -= size; } +void audio_generic_run_buffer_out(HWVoiceOut *hw) +{ + while (hw->pending_emul) { + size_t write_len, written; + ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; + + if (start < 0) { + start += hw->size_emul; + } + assert(start >= 0 && start < hw->size_emul); + + write_len = MIN(hw->pending_emul, hw->size_emul - start); + + written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len); + hw->pending_emul -= written; + + if (written < write_len) { + break; + } + } +} + void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size) { if (unlikely(!hw->buf_emul)) { @@ -1428,8 +1468,7 @@ void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size) return hw->buf_emul + hw->pos_emul; } -size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf, - size_t size) +size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) { assert(buf == hw->buf_emul + hw->pos_emul && size + hw->pending_emul <= hw->size_emul); @@ -1440,35 +1479,6 @@ size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf, return size; } -size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) -{ - audio_generic_put_buffer_out_nowrite(hw, buf, size); - - while (hw->pending_emul) { - size_t write_len, written; - ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; - if (start < 0) { - start += hw->size_emul; - } - assert(start >= 0 && start < hw->size_emul); - - write_len = MIN(hw->pending_emul, hw->size_emul - start); - - written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len); - hw->pending_emul -= written; - - if (written < write_len) { - break; - } - } - - /* - * fake we have written everything. non-written data remain in pending_emul, - * so we do not have to clip them multiple times - */ - return size; -} - size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size) { size_t dst_size, copy_size; @@ -1476,17 +1486,17 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size) copy_size = MIN(size, dst_size); memcpy(dst, buf, copy_size); - return hw->pcm_ops->put_buffer_out(hw, buf, copy_size); + return hw->pcm_ops->put_buffer_out(hw, dst, copy_size); } size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size) { - size_t dst_size, copy_size; - void *dst = hw->pcm_ops->get_buffer_in(hw, &dst_size); - copy_size = MIN(size, dst_size); + size_t src_size, copy_size; + void *src = hw->pcm_ops->get_buffer_in(hw, &src_size); + copy_size = MIN(size, src_size); - memcpy(dst, buf, copy_size); - hw->pcm_ops->put_buffer_in(hw, buf, copy_size); + memcpy(buf, src, copy_size); + hw->pcm_ops->put_buffer_in(hw, src, copy_size); return copy_size; } @@ -1837,11 +1847,15 @@ CaptureVoiceOut *AUD_add_capture( cap->buf = g_malloc0_n(hw->mix_buf->size, hw->info.bytes_per_frame); - hw->clip = mixeng_clip - [hw->info.nchannels == 2] - [hw->info.sign] - [hw->info.swap_endianness] - [audio_bits_to_index (hw->info.bits)]; + if (hw->info.is_float) { + hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; + } else { + hw->clip = mixeng_clip + [hw->info.nchannels == 2] + [hw->info.is_signed] + [hw->info.swap_endianness] + [audio_bits_to_index(hw->info.bits)]; + } QLIST_INSERT_HEAD (&s->cap_head, cap, entries); QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); @@ -2080,6 +2094,7 @@ int audioformat_bytes_per_sample(AudioFormat fmt) case AUDIO_FORMAT_U32: case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_F32: return 4; case AUDIO_FORMAT__MAX: diff --git a/audio/audio_int.h b/audio/audio_int.h index 5ba2078346..4775857bf2 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -40,7 +40,8 @@ struct audio_callback { struct audio_pcm_info { int bits; - int sign; + bool is_signed; + bool is_float; int freq; int nchannels; int bytes_per_frame; @@ -152,6 +153,7 @@ struct audio_pcm_ops { int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque); void (*fini_out)(HWVoiceOut *hw); size_t (*write) (HWVoiceOut *hw, void *buf, size_t size); + void (*run_buffer_out)(HWVoiceOut *hw); /* * get a buffer that after later can be passed to put_buffer_out; optional * returns the buffer, and writes it's size to size (in bytes) @@ -178,10 +180,9 @@ struct audio_pcm_ops { void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size); void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size); +void audio_generic_run_buffer_out(HWVoiceOut *hw); void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size); size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size); -size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf, - size_t size); size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size); size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size); diff --git a/audio/audio_template.h b/audio/audio_template.h index 3287d7075e..7013d3041f 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -153,15 +153,23 @@ static int glue (audio_pcm_sw_init_, TYPE) ( sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; #endif + if (sw->info.is_float) { #ifdef DAC - sw->conv = mixeng_conv + sw->conv = mixeng_conv_float[sw->info.nchannels == 2]; #else - sw->clip = mixeng_clip + sw->clip = mixeng_clip_float[sw->info.nchannels == 2]; #endif - [sw->info.nchannels == 2] - [sw->info.sign] - [sw->info.swap_endianness] - [audio_bits_to_index (sw->info.bits)]; + } else { +#ifdef DAC + sw->conv = mixeng_conv +#else + sw->clip = mixeng_clip +#endif + [sw->info.nchannels == 2] + [sw->info.is_signed] + [sw->info.swap_endianness] + [audio_bits_to_index(sw->info.bits)]; + } sw->name = g_strdup (name); err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); @@ -276,15 +284,23 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, goto err1; } + if (hw->info.is_float) { #ifdef DAC - hw->clip = mixeng_clip + hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; #else - hw->conv = mixeng_conv + hw->conv = mixeng_conv_float[hw->info.nchannels == 2]; #endif - [hw->info.nchannels == 2] - [hw->info.sign] - [hw->info.swap_endianness] - [audio_bits_to_index (hw->info.bits)]; + } else { +#ifdef DAC + hw->clip = mixeng_clip +#else + hw->conv = mixeng_conv +#endif + [hw->info.nchannels == 2] + [hw->info.is_signed] + [hw->info.swap_endianness] + [audio_bits_to_index(hw->info.bits)]; + } glue(audio_pcm_hw_alloc_resources_, TYPE)(hw); diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 66f0f459cf..4b4365660f 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -411,7 +411,7 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) } COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), (hw, size)) -COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, +COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size)) COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), @@ -471,20 +471,6 @@ static OSStatus audioDeviceIOProc( return 0; } -static UInt32 coreaudio_get_flags(struct audio_pcm_info *info, - struct audsettings *as) -{ - UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0; - if (as->endianness) { /* 0 = little, 1 = big */ - flags |= kAudioFormatFlagIsBigEndian; - } - - if (flags == 0) { /* must not be 0 */ - flags = kAudioFormatFlagsAreAllClear; - } - return flags; -} - static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { @@ -496,6 +482,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, Audiodev *dev = drv_opaque; AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; int frames; + struct audsettings fake_as; /* create mutex */ err = pthread_mutex_init(&core->mutex, NULL); @@ -504,6 +491,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, return -1; } + fake_as = *as; + as = &fake_as; + as->fmt = AUDIO_FORMAT_F32; audio_pcm_init_info (&hw->info, as); status = coreaudio_get_voice(&core->outputDeviceID); @@ -572,15 +562,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, /* set Samplerate */ core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; - core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; - core->outputStreamBasicDescription.mFormatFlags = - coreaudio_get_flags(&hw->info, as); - core->outputStreamBasicDescription.mBytesPerPacket = - core->outputStreamBasicDescription.mBytesPerFrame = - hw->info.nchannels * hw->info.bits / 8; - core->outputStreamBasicDescription.mFramesPerPacket = 1; - core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels; - core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits; status = coreaudio_set_streamformat(core->outputDeviceID, &core->outputStreamBasicDescription); @@ -687,9 +668,12 @@ static void coreaudio_audio_fini (void *opaque) static struct audio_pcm_ops coreaudio_pcm_ops = { .init_out = coreaudio_init_out, .fini_out = coreaudio_fini_out, + /* wrapper for audio_generic_write */ .write = coreaudio_write, + /* wrapper for audio_generic_get_buffer_out */ .get_buffer_out = coreaudio_get_buffer_out, - .put_buffer_out = coreaudio_put_buffer_out_nowrite, + /* wrapper for audio_generic_put_buffer_out */ + .put_buffer_out = coreaudio_put_buffer_out, .enable_out = coreaudio_enable_out }; diff --git a/audio/dsound_template.h b/audio/dsound_template.h index 7a15f91ce5..9c5ce625ab 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -244,6 +244,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, goto fail0; } + ds->first_time = true; obt_as.endianness = 0; audio_pcm_init_info (&hw->info, &obt_as); diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index c265c0094b..bd57082a8d 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -53,12 +53,14 @@ typedef struct { typedef struct { HWVoiceOut hw; LPDIRECTSOUNDBUFFER dsound_buffer; + bool first_time; dsound *s; } DSoundVoiceOut; typedef struct { HWVoiceIn hw; LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; + bool first_time; dsound *s; } DSoundVoiceIn; @@ -414,21 +416,32 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; HRESULT hr; - DWORD ppos, act_size; + DWORD ppos, wpos, act_size; size_t req_size; int err; void *ret; - hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, NULL); + hr = IDirectSoundBuffer_GetCurrentPosition( + dsb, &ppos, ds->first_time ? &wpos : NULL); if (FAILED(hr)) { dsound_logerr(hr, "Could not get playback buffer position\n"); *size = 0; return NULL; } + if (ds->first_time) { + hw->pos_emul = wpos; + ds->first_time = false; + } + req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul); req_size = MIN(req_size, hw->size_emul - hw->pos_emul); + if (req_size == 0) { + *size = 0; + return NULL; + } + err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL, &act_size, NULL, false, ds->s); if (err) { @@ -508,18 +521,24 @@ static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size) DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; HRESULT hr; - DWORD cpos, act_size; + DWORD cpos, rpos, act_size; size_t req_size; int err; void *ret; - hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, &cpos, NULL); + hr = IDirectSoundCaptureBuffer_GetCurrentPosition( + dscb, &cpos, ds->first_time ? &rpos : NULL); if (FAILED(hr)) { dsound_logerr(hr, "Could not get capture buffer position\n"); *size = 0; return NULL; } + if (ds->first_time) { + hw->pos_emul = rpos; + ds->first_time = false; + } + req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul); req_size = MIN(req_size, hw->size_emul - hw->pos_emul); diff --git a/audio/mixeng.c b/audio/mixeng.c index 2f5ba71381..c14b0d874c 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -267,6 +267,76 @@ f_sample *mixeng_clip[2][2][2][3] = { } }; +#ifdef FLOAT_MIXENG +#define FLOAT_CONV_TO(x) (x) +#define FLOAT_CONV_FROM(x) (x) +#else +static const float float_scale = UINT_MAX; +#define FLOAT_CONV_TO(x) ((x) * float_scale) + +#ifdef RECIPROCAL +static const float float_scale_reciprocal = 1.f / UINT_MAX; +#define FLOAT_CONV_FROM(x) ((x) * float_scale_reciprocal) +#else +#define FLOAT_CONV_FROM(x) ((x) / float_scale) +#endif +#endif + +static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, + int samples) +{ + float *in = (float *)src; + + while (samples--) { + dst->r = dst->l = FLOAT_CONV_TO(*in++); + dst++; + } +} + +static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, + int samples) +{ + float *in = (float *)src; + + while (samples--) { + dst->l = FLOAT_CONV_TO(*in++); + dst->r = FLOAT_CONV_TO(*in++); + dst++; + } +} + +t_sample *mixeng_conv_float[2] = { + conv_natural_float_to_mono, + conv_natural_float_to_stereo, +}; + +static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, + int samples) +{ + float *out = (float *)dst; + + while (samples--) { + *out++ = FLOAT_CONV_FROM(src->l) + FLOAT_CONV_FROM(src->r); + src++; + } +} + +static void clip_natural_float_from_stereo( + void *dst, const struct st_sample *src, int samples) +{ + float *out = (float *)dst; + + while (samples--) { + *out++ = FLOAT_CONV_FROM(src->l); + *out++ = FLOAT_CONV_FROM(src->r); + src++; + } +} + +f_sample *mixeng_clip_float[2] = { + clip_natural_float_from_mono, + clip_natural_float_from_stereo, +}; void audio_sample_to_uint64(void *samples, int pos, uint64_t *left, uint64_t *right) diff --git a/audio/mixeng.h b/audio/mixeng.h index 18e62c7c49..2dcd6df245 100644 --- a/audio/mixeng.h +++ b/audio/mixeng.h @@ -38,9 +38,14 @@ typedef struct st_sample st_sample; typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); +/* indices: [stereo][signed][swap endiannes][8, 16 or 32-bits] */ extern t_sample *mixeng_conv[2][2][2][3]; extern f_sample *mixeng_clip[2][2][2][3]; +/* indices: [stereo] */ +extern t_sample *mixeng_conv_float[2]; +extern f_sample *mixeng_clip_float[2]; + void *st_rate_start (int inrate, int outrate); void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, size_t *isamp, size_t *osamp); diff --git a/audio/noaudio.c b/audio/noaudio.c index ff99b253ff..05798ea210 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = { .init_out = no_init_out, .fini_out = no_fini_out, .write = no_write, + .run_buffer_out = audio_generic_run_buffer_out, .enable_out = no_enable_out, .init_in = no_init_in, diff --git a/audio/ossaudio.c b/audio/ossaudio.c index c43faeeea4..f88d076ec2 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -382,6 +382,15 @@ static size_t oss_get_available_bytes(OSSVoiceOut *oss) return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul); } +static void oss_run_buffer_out(HWVoiceOut *hw) +{ + OSSVoiceOut *oss = (OSSVoiceOut *)hw; + + if (!oss->mmapped) { + audio_generic_run_buffer_out(hw); + } +} + static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size) { OSSVoiceOut *oss = (OSSVoiceOut *) hw; @@ -420,7 +429,7 @@ static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len) size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul); memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy); - hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul; + hw->pos_emul = (hw->pos_emul + to_copy) % hw->size_emul; buf += to_copy; len -= to_copy; } @@ -570,20 +579,18 @@ static void oss_enable_out(HWVoiceOut *hw, bool enable) AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; if (enable) { - bool poll_mode = opdo->try_poll; + hw->poll_mode = opdo->try_poll; ldebug("enabling voice\n"); - if (poll_mode) { + if (hw->poll_mode) { oss_poll_out(hw); - poll_mode = 0; } - hw->poll_mode = poll_mode; if (!oss->mmapped) { return; } - audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->mix_buf->size); + audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->samples); trig = PCM_ENABLE_OUTPUT; if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { oss_logerr(errno, @@ -699,17 +706,15 @@ static void oss_enable_in(HWVoiceIn *hw, bool enable) AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; if (enable) { - bool poll_mode = opdo->try_poll; + hw->poll_mode = opdo->try_poll; - if (poll_mode) { + if (hw->poll_mode) { oss_poll_in(hw); - poll_mode = 0; } - hw->poll_mode = poll_mode; } else { if (hw->poll_mode) { - hw->poll_mode = 0; qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); + hw->poll_mode = 0; } } } @@ -748,6 +753,7 @@ static struct audio_pcm_ops oss_pcm_ops = { .init_out = oss_init_out, .fini_out = oss_fini_out, .write = oss_write, + .run_buffer_out = oss_run_buffer_out, .get_buffer_out = oss_get_buffer_out, .put_buffer_out = oss_put_buffer_out, .enable_out = oss_enable_out, diff --git a/audio/paaudio.c b/audio/paaudio.c index dbfe48c03a..b052084698 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -32,7 +32,6 @@ typedef struct { HWVoiceOut hw; pa_stream *stream; paaudio *g; - size_t samples; } PAVoiceOut; typedef struct { @@ -41,7 +40,6 @@ typedef struct { const void *read_data; size_t read_length; paaudio *g; - size_t samples; } PAVoiceIn; static void qpa_conn_fini(PAConnection *c); @@ -279,6 +277,9 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) case AUDIO_FORMAT_U32: format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; break; + case AUDIO_FORMAT_F32: + format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE; + break; default: dolog ("Internal logic error: Bad audio format %d\n", afmt); format = PA_SAMPLE_U8; @@ -304,6 +305,12 @@ static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness) case PA_SAMPLE_S32LE: *endianness = 0; return AUDIO_FORMAT_S32; + case PA_SAMPLE_FLOAT32BE: + *endianness = 1; + return AUDIO_FORMAT_F32; + case PA_SAMPLE_FLOAT32LE: + *endianness = 0; + return AUDIO_FORMAT_F32; default: dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); return AUDIO_FORMAT_U8; @@ -488,7 +495,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = pa->samples = audio_buffer_samples( + hw->samples = audio_buffer_samples( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, ppdo->buffer_length); @@ -536,7 +543,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = pa->samples = audio_buffer_samples( + hw->samples = audio_buffer_samples( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, ppdo->buffer_length); diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 5c6bcfcb3e..21b7a0484b 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -77,6 +77,14 @@ static int aud_to_sdlfmt (AudioFormat fmt) case AUDIO_FORMAT_U16: return AUDIO_U16LSB; + case AUDIO_FORMAT_S32: + return AUDIO_S32LSB; + + /* no unsigned 32-bit support in SDL */ + + case AUDIO_FORMAT_F32: + return AUDIO_F32LSB; + default: dolog ("Internal logic error: Bad audio format %d\n", fmt); #ifdef DEBUG_AUDIO @@ -119,6 +127,26 @@ static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) *fmt = AUDIO_FORMAT_U16; break; + case AUDIO_S32LSB: + *endianness = 0; + *fmt = AUDIO_FORMAT_S32; + break; + + case AUDIO_S32MSB: + *endianness = 1; + *fmt = AUDIO_FORMAT_S32; + break; + + case AUDIO_F32LSB: + *endianness = 0; + *fmt = AUDIO_FORMAT_F32; + break; + + case AUDIO_F32MSB: + *endianness = 1; + *fmt = AUDIO_FORMAT_F32; + break; + default: dolog ("Unrecognized SDL audio format %d\n", sdlfmt); return -1; @@ -227,7 +255,7 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), (hw, size), *size = 0, sdl_unlock) -SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, +SDL_WRAPPER_FUNC(put_buffer_out, size_t, (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), /*nothing*/, sdl_unlock_and_post) SDL_WRAPPER_FUNC(write, size_t, @@ -320,9 +348,12 @@ static void sdl_audio_fini (void *opaque) static struct audio_pcm_ops sdl_pcm_ops = { .init_out = sdl_init_out, .fini_out = sdl_fini_out, + /* wrapper for audio_generic_write */ .write = sdl_write, + /* wrapper for audio_generic_get_buffer_out */ .get_buffer_out = sdl_get_buffer_out, - .put_buffer_out = sdl_put_buffer_out_nowrite, + /* wrapper for audio_generic_put_buffer_out */ + .put_buffer_out = sdl_put_buffer_out, .enable_out = sdl_enable_out, }; diff --git a/audio/wavaudio.c b/audio/wavaudio.c index e46d834bd3..20e6853f85 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -197,6 +197,7 @@ static struct audio_pcm_ops wav_pcm_ops = { .init_out = wav_init_out, .fini_out = wav_fini_out, .write = wav_write_out, + .run_buffer_out = audio_generic_run_buffer_out, .enable_out = wav_enable_out, }; diff --git a/authz/listfile.c b/authz/listfile.c index e7a8c19bcb..b71f57d30a 100644 --- a/authz/listfile.c +++ b/authz/listfile.c @@ -239,7 +239,7 @@ qauthz_list_file_init(Object *obj) authz->file_watch = -1; #ifdef CONFIG_INOTIFY1 - authz->refresh = TRUE; + authz->refresh = true; #endif } @@ -1998,18 +1998,19 @@ char *bdrv_perm_names(uint64_t perm) { 0, NULL } }; - char *result = g_strdup(""); + GString *result = g_string_sized_new(30); struct perm_name *p; for (p = permissions; p->name; p++) { if (perm & p->perm) { - char *old = result; - result = g_strdup_printf("%s%s%s", old, *old ? ", " : "", p->name); - g_free(old); + if (result->len > 0) { + g_string_append(result, ", "); + } + g_string_append(result, p->name); } } - return result; + return g_string_free(result, FALSE); } /* @@ -6441,6 +6442,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) child->bs->exact_filename); pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename); + qobject_unref(bs->full_open_options); bs->full_open_options = qobject_ref(child->bs->full_open_options); return; diff --git a/block/backup-top.c b/block/backup-top.c index 9aed2eb4c0..fa78f3256d 100644 --- a/block/backup-top.c +++ b/block/backup-top.c @@ -190,6 +190,7 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name, BDRV_O_RDWR, errp); + bool appended = false; if (!top) { return NULL; @@ -212,8 +213,9 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, bdrv_append(top, source, &local_err); if (local_err) { error_prepend(&local_err, "Cannot append backup-top filter: "); - goto append_failed; + goto fail; } + appended = true; /* * bdrv_append() finished successfully, now we can require permissions @@ -224,14 +226,14 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, if (local_err) { error_prepend(&local_err, "Cannot set permissions for backup-top filter: "); - goto failed_after_append; + goto fail; } state->bcs = block_copy_state_new(top->backing, state->target, cluster_size, write_flags, &local_err); if (local_err) { error_prepend(&local_err, "Cannot create block-copy-state: "); - goto failed_after_append; + goto fail; } *bcs = state->bcs; @@ -239,14 +241,15 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, return top; -failed_after_append: - state->active = false; - bdrv_backup_top_drop(top); +fail: + if (appended) { + state->active = false; + bdrv_backup_top_drop(top); + } else { + bdrv_unref(top); + } -append_failed: bdrv_drained_end(source); - bdrv_unref_child(top, state->target); - bdrv_unref(top); error_propagate(errp, local_err); return NULL; diff --git a/block/io.c b/block/io.c index 1eb2b2bddc..7e4cb74cf4 100644 --- a/block/io.c +++ b/block/io.c @@ -1565,10 +1565,12 @@ static bool bdrv_init_padding(BlockDriverState *bs, pad->tail = align - pad->tail; } - if ((!pad->head && !pad->tail) || !bytes) { + if (!pad->head && !pad->tail) { return false; } + assert(bytes); /* Nothing good in aligning zero-length requests */ + sum = pad->head + bytes + pad->tail; pad->buf_len = (sum > align && pad->head && pad->tail) ? 2 * align : align; pad->buf = qemu_blockalign(bs, pad->buf_len); @@ -1706,6 +1708,18 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, return ret; } + if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) { + /* + * Aligning zero request is nonsense. Even if driver has special meaning + * of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass + * it to driver due to request_alignment. + * + * Still, no reason to return an error if someone do unaligned + * zero-length read occasionally. + */ + return 0; + } + bdrv_inc_in_flight(bs); /* Don't do copy-on-read if we read data before write operation */ @@ -2116,6 +2130,18 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, return -ENOTSUP; } + if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) { + /* + * Aligning zero request is nonsense. Even if driver has special meaning + * of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass + * it to driver due to request_alignment. + * + * Still, no reason to return an error if someone do unaligned + * zero-length write occasionally. + */ + return 0; + } + bdrv_inc_in_flight(bs); /* * Align write if necessary by performing a read-modify-write cycle. diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 8982b7b762..1947f13a2d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -124,12 +124,11 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, #endif new_l1_size2 = sizeof(uint64_t) * new_l1_size; - new_l1_table = qemu_try_blockalign(bs->file->bs, - ROUND_UP(new_l1_size2, 512)); + new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2); if (new_l1_table == NULL) { return -ENOMEM; } - memset(new_l1_table, 0, ROUND_UP(new_l1_size2, 512)); + memset(new_l1_table, 0, new_l1_size2); if (s->l1_size) { memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); @@ -217,26 +216,31 @@ static int l2_load(BlockDriverState *bs, uint64_t offset, } /* - * Writes one sector of the L1 table to the disk (can't update single entries - * and we really don't want bdrv_pread to perform a read-modify-write) + * Writes an L1 entry to disk (note that depending on the alignment + * requirements this function may write more that just one entry in + * order to prevent bdrv_pwrite from performing a read-modify-write) */ -#define L1_ENTRIES_PER_SECTOR (512 / 8) int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) { BDRVQcow2State *s = bs->opaque; - uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 }; int l1_start_index; int i, ret; + int bufsize = MAX(sizeof(uint64_t), + MIN(bs->file->bs->bl.request_alignment, s->cluster_size)); + int nentries = bufsize / sizeof(uint64_t); + g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries); - l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1); - for (i = 0; i < L1_ENTRIES_PER_SECTOR && l1_start_index + i < s->l1_size; - i++) - { + if (buf == NULL) { + return -ENOMEM; + } + + l1_start_index = QEMU_ALIGN_DOWN(l1_index, nentries); + for (i = 0; i < MIN(nentries, s->l1_size - l1_start_index); i++) { buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); } ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, - s->l1_table_offset + 8 * l1_start_index, sizeof(buf), false); + s->l1_table_offset + 8 * l1_start_index, bufsize, false); if (ret < 0) { return ret; } @@ -244,7 +248,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, - buf, sizeof(buf)); + buf, bufsize); if (ret < 0) { return ret; } @@ -777,6 +781,10 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, (cluster_offset + compressed_size - 1) / QCOW2_COMPRESSED_SECTOR_SIZE - (cluster_offset / QCOW2_COMPRESSED_SECTOR_SIZE); + /* The offset and size must fit in their fields of the L2 table entry */ + assert((cluster_offset & s->cluster_offset_mask) == cluster_offset); + assert((nb_csectors & s->csize_mask) == nb_csectors); + cluster_offset |= QCOW_OFLAG_COMPRESSED | ((uint64_t)nb_csectors << s->csize_shift); @@ -972,6 +980,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) assert(l2_index + m->nb_clusters <= s->l2_slice_size); for (i = 0; i < m->nb_clusters; i++) { + uint64_t offset = cluster_offset + (i << s->cluster_bits); /* if two concurrent writes happen to the same unallocated cluster * each write allocates separate cluster and writes data concurrently. * The first one to complete updates l2 table with pointer to its @@ -982,8 +991,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) old_cluster[j++] = l2_slice[l2_index + i]; } - l2_slice[l2_index + i] = cpu_to_be64((cluster_offset + - (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); + /* The offset must fit in the offset field of the L2 table entry */ + assert((offset & L2E_OFFSET_MASK) == offset); + + l2_slice[l2_index + i] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); } @@ -1913,6 +1924,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } + /* The offset must fit in the offset field */ + assert((offset & L2E_OFFSET_MASK) == offset); + if (l2_refcount > 1) { /* For shared L2 tables, set the refcount accordingly * (it is already 1 and needs to be l2_refcount) */ diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index f67ac6b2d8..c963bc8de1 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1262,7 +1262,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, * l1_table_offset when it is the current s->l1_table_offset! Be careful * when changing this! */ if (l1_table_offset != s->l1_table_offset) { - l1_table = g_try_malloc0(ROUND_UP(l1_size2, 512)); + l1_table = g_try_malloc0(l1_size2); if (l1_size2 && l1_table == NULL) { ret = -ENOMEM; goto fail; diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 5ab64da1ec..82c32d4c9b 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -1024,8 +1024,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, return ret; } new_l1_bytes = sn->l1_size * sizeof(uint64_t); - new_l1_table = qemu_try_blockalign(bs->file->bs, - ROUND_UP(new_l1_bytes, 512)); + new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_bytes); if (new_l1_table == NULL) { return -ENOMEM; } diff --git a/block/qcow2.c b/block/qcow2.c index cef9d72b3a..ef96606f8d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -453,16 +453,15 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs) static void report_unsupported_feature(Error **errp, Qcow2Feature *table, uint64_t mask) { - char *features = g_strdup(""); - char *old; + g_autoptr(GString) features = g_string_sized_new(60); while (table && table->name[0] != '\0') { if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { if (mask & (1ULL << table->bit)) { - old = features; - features = g_strdup_printf("%s%s%.46s", old, *old ? ", " : "", - table->name); - g_free(old); + if (features->len > 0) { + g_string_append(features, ", "); + } + g_string_append_printf(features, "%.46s", table->name); mask &= ~(1ULL << table->bit); } } @@ -470,14 +469,14 @@ static void report_unsupported_feature(Error **errp, Qcow2Feature *table, } if (mask) { - old = features; - features = g_strdup_printf("%s%sUnknown incompatible feature: %" PRIx64, - old, *old ? ", " : "", mask); - g_free(old); + if (features->len > 0) { + g_string_append(features, ", "); + } + g_string_append_printf(features, + "Unknown incompatible feature: %" PRIx64, mask); } - error_setg(errp, "Unsupported qcow2 feature(s): %s", features); - g_free(features); + error_setg(errp, "Unsupported qcow2 feature(s): %s", features->str); } /* @@ -1492,7 +1491,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (s->l1_size > 0) { s->l1_table = qemu_try_blockalign(bs->file->bs, - ROUND_UP(s->l1_size * sizeof(uint64_t), 512)); + s->l1_size * sizeof(uint64_t)); if (s->l1_table == NULL) { error_setg(errp, "Could not allocate L1 table"); ret = -ENOMEM; @@ -2168,10 +2167,7 @@ static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs, offset, bytes, qiov, qiov_offset); case QCOW2_CLUSTER_NORMAL: - if ((file_cluster_offset & 511) != 0) { - return -EIO; - } - + assert(offset_into_cluster(s, file_cluster_offset) == 0); if (bs->encrypted) { return qcow2_co_preadv_encrypted(bs, file_cluster_offset, offset, bytes, qiov, qiov_offset); @@ -2507,7 +2503,7 @@ static coroutine_fn int qcow2_co_pwritev_part( goto out_locked; } - assert((cluster_offset & 511) == 0); + assert(offset_into_cluster(s, cluster_offset) == 0); ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset + offset_in_cluster, @@ -3276,7 +3272,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) /* Validate options and set default values */ if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) { - error_setg(errp, "Image size must be a multiple of 512 bytes"); + error_setg(errp, "Image size must be a multiple of %u bytes", + (unsigned) BDRV_SECTOR_SIZE); ret = -EINVAL; goto out; } @@ -3832,10 +3829,6 @@ qcow2_co_copy_range_from(BlockDriverState *bs, case QCOW2_CLUSTER_NORMAL: child = s->data_file; copy_offset += offset_into_cluster(s, src_offset); - if ((copy_offset & 511) != 0) { - ret = -EIO; - goto out; - } break; default: @@ -3897,7 +3890,7 @@ qcow2_co_copy_range_to(BlockDriverState *bs, goto fail; } - assert((cluster_offset & 511) == 0); + assert(offset_into_cluster(s, cluster_offset) == 0); ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset + offset_in_cluster, cur_bytes, true); @@ -3954,8 +3947,9 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, return -ENOTSUP; } - if (offset & 511) { - error_setg(errp, "The new size must be a multiple of 512"); + if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { + error_setg(errp, "The new size must be a multiple of %u", + (unsigned) BDRV_SECTOR_SIZE); return -EINVAL; } diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 8c20baa4a4..de2f2ff713 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -144,6 +144,7 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr, } void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + bool has_description, const char *description, bool has_writable, bool writable, bool has_bitmap, const char *bitmap, Error **errp) { @@ -167,6 +168,11 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, return; } + if (has_description && strlen(description) > NBD_MAX_STRING_SIZE) { + error_setg(errp, "description '%s' too long", description); + return; + } + if (nbd_export_find(name)) { error_setg(errp, "NBD server already has export named '%s'", name); return; @@ -195,7 +201,8 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, writable = false; } - exp = nbd_export_new(bs, 0, len, name, NULL, bitmap, !writable, !writable, + exp = nbd_export_new(bs, 0, len, name, description, bitmap, + !writable, !writable, NULL, false, on_eject_blk, errp); if (!exp) { goto out; diff --git a/docs/arm-cpu-features.rst b/docs/arm-cpu-features.rst index dbf3b7cf42..fc1623aeca 100644 --- a/docs/arm-cpu-features.rst +++ b/docs/arm-cpu-features.rst @@ -185,7 +185,7 @@ the list of KVM VCPU features and their descriptions. kvm-no-adjvtime By default kvm-no-adjvtime is disabled. This means that by default the virtual time - adjustment is enabled (vtime is *not not* + adjustment is enabled (vtime is not *not* adjusted). When virtual time adjustment is enabled each diff --git a/docs/interop/conf.py b/docs/interop/conf.py index b0f322207c..b3cda17042 100644 --- a/docs/interop/conf.py +++ b/docs/interop/conf.py @@ -27,5 +27,8 @@ man_pages = [ [], 1), ('virtfs-proxy-helper', 'virtfs-proxy-helper', u'QEMU 9p virtfs proxy filesystem helper', - ['M. Mohan Kumar'], 1) + ['M. Mohan Kumar'], 1), + ('virtiofsd', 'virtiofsd', u'QEMU virtio-fs shared file system daemon', + ['Stefan Hajnoczi <stefanha@redhat.com>', + 'Masayoshi Mizuma <m.mizuma@jp.fujitsu.com>'], 1), ] diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 3b763b1eeb..e8455b4270 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -24,3 +24,4 @@ Contents: vhost-user vhost-user-gpu virtfs-proxy-helper + virtiofsd diff --git a/docs/interop/qemu-img.rst b/docs/interop/qemu-img.rst index fa27e5c7b4..42e4451db4 100644 --- a/docs/interop/qemu-img.rst +++ b/docs/interop/qemu-img.rst @@ -247,7 +247,7 @@ Command description: Amends the image format specific *OPTIONS* for the image file *FILENAME*. Not all file formats support this operation. -.. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-n] [-i AIO] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-w] [-U] FILENAME +.. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-i AIO] [-n] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-w] [-U] FILENAME Run a simple sequential I/O benchmark on the specified image. If ``-w`` is specified, a write test is performed, otherwise a read test is performed. @@ -264,13 +264,13 @@ Command description: ``--no-drain`` is specified, a flush is issued without draining the request queue first. + if ``-i`` is specified, *AIO* option can be used to specify different + AIO backends: ``threads``, ``native`` or ``io_uring``. + If ``-n`` is specified, the native AIO backend is used if possible. On Linux, this option only works if ``-t none`` or ``-t directsync`` is specified as well. - if ``-i`` is specified, *AIO* option can be used to specify different - AIO backends: ``threads``, ``native`` or ``io_uring``. - For write tests, by default a buffer filled with zeros is written. This can be overridden with a pattern byte specified by *PATTERN*. diff --git a/docs/interop/qemu-nbd.rst b/docs/interop/qemu-nbd.rst index df7b6b9d0d..e548403100 100644 --- a/docs/interop/qemu-nbd.rst +++ b/docs/interop/qemu-nbd.rst @@ -72,13 +72,6 @@ driver options if ``--image-opts`` is specified. Export the disk as read-only. -.. option:: -P, --partition=NUM - - Deprecated: Only expose MBR partition *NUM*. Understands physical - partitions 1-4 and logical partition 5. New code should instead use - :option:`--image-opts` with the raw driver wrapping a subset of the - original image. - .. option:: -B, --bitmap=NAME If *filename* has a qcow2 persistent bitmap *NAME*, expose @@ -224,14 +217,14 @@ a 1 megabyte subset of a raw file, using the export name 'subset': -t -x subset -p 10810 \ --image-opts driver=raw,offset=1M,size=1M,file.driver=file,file.filename=file.raw -Serve a read-only copy of just the first MBR partition of a guest -image over a Unix socket with as many as 5 simultaneous readers, with -a persistent process forked as a daemon: +Serve a read-only copy of a guest image over a Unix socket with as +many as 5 simultaneous readers, with a persistent process forked as a +daemon: :: qemu-nbd --fork --persistent --shared=5 --socket=/path/to/sock \ - --partition=1 --read-only --format=qcow2 file.qcow2 + --read-only --format=qcow2 file.qcow2 Expose the guest-visible contents of a qcow2 file via a block device /dev/nbd0 (and possibly creating /dev/nbd0p1 and friends for diff --git a/docs/interop/virtiofsd.rst b/docs/interop/virtiofsd.rst new file mode 100644 index 0000000000..378594c422 --- /dev/null +++ b/docs/interop/virtiofsd.rst @@ -0,0 +1,120 @@ +QEMU virtio-fs shared file system daemon +======================================== + +Synopsis +-------- + +**virtiofsd** [*OPTIONS*] + +Description +----------- + +Share a host directory tree with a guest through a virtio-fs device. This +program is a vhost-user backend that implements the virtio-fs device. Each +virtio-fs device instance requires its own virtiofsd process. + +This program is designed to work with QEMU's ``--device vhost-user-fs-pci`` +but should work with any virtual machine monitor (VMM) that supports +vhost-user. See the Examples section below. + +This program must be run as the root user. Upon startup the program will +switch into a new file system namespace with the shared directory tree as its +root. This prevents "file system escapes" due to symlinks and other file +system objects that might lead to files outside the shared directory. The +program also sandboxes itself using seccomp(2) to prevent ptrace(2) and other +vectors that could allow an attacker to compromise the system after gaining +control of the virtiofsd process. + +Options +------- + +.. program:: virtiofsd + +.. option:: -h, --help + + Print help. + +.. option:: -V, --version + + Print version. + +.. option:: -d + + Enable debug output. + +.. option:: --syslog + + Print log messages to syslog instead of stderr. + +.. option:: -o OPTION + + * debug - + Enable debug output. + + * flock|no_flock - + Enable/disable flock. The default is ``no_flock``. + + * log_level=LEVEL - + Print only log messages matching LEVEL or more severe. LEVEL is one of + ``err``, ``warn``, ``info``, or ``debug``. The default is ``info``. + + * norace - + Disable racy fallback. The default is false. + + * posix_lock|no_posix_lock - + Enable/disable remote POSIX locks. The default is ``posix_lock``. + + * readdirplus|no_readdirplus - + Enable/disable readdirplus. The default is ``readdirplus``. + + * source=PATH - + Share host directory tree located at PATH. This option is required. + + * timeout=TIMEOUT - + I/O timeout in seconds. The default depends on cache= option. + + * writeback|no_writeback - + Enable/disable writeback cache. The cache alows the FUSE client to buffer + and merge write requests. The default is ``no_writeback``. + + * xattr|no_xattr - + Enable/disable extended attributes (xattr) on files and directories. The + default is ``no_xattr``. + +.. option:: --socket-path=PATH + + Listen on vhost-user UNIX domain socket at PATH. + +.. option:: --fd=FDNUM + + Accept connections from vhost-user UNIX domain socket file descriptor FDNUM. + The file descriptor must already be listening for connections. + +.. option:: --thread-pool-size=NUM + + Restrict the number of worker threads per request queue to NUM. The default + is 64. + +.. option:: --cache=none|auto|always + + Select the desired trade-off between coherency and performance. ``none`` + forbids the FUSE client from caching to achieve best coherency at the cost of + performance. ``auto`` acts similar to NFS with a 1 second metadata cache + timeout. ``always`` sets a long cache lifetime at the expense of coherency. + +Examples +-------- + +Export ``/var/lib/fs/vm001/`` on vhost-user UNIX domain socket +``/var/run/vm001-vhost-fs.sock``: + +:: + + host# virtiofsd --socket-path=/var/run/vm001-vhost-fs.sock -o source=/var/lib/fs/vm001 + host# qemu-system-x86_64 \ + -chardev socket,id=char0,path=/var/run/vm001-vhost-fs.sock \ + -device vhost-user-fs-pci,chardev=char0,tag=myfs \ + -object memory-backend-memfd,id=mem,size=4G,share=on \ + -numa node,memdev=mem \ + ... + guest# mount -t virtiofs myfs /mnt diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 54239c9bbf..7eb210ffa8 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -578,6 +578,25 @@ static int synth_init(FsContext *ctx, Error **errp) NULL, v9fs_synth_qtest_flush_write, ctx); assert(!ret); + + /* Directory for READDIR test */ + { + V9fsSynthNode *dir = NULL; + ret = qemu_v9fs_synth_mkdir( + NULL, 0700, QTEST_V9FS_SYNTH_READDIR_DIR, &dir + ); + assert(!ret); + for (i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) { + char *name = g_strdup_printf( + QTEST_V9FS_SYNTH_READDIR_FILE, i + ); + ret = qemu_v9fs_synth_add_file( + dir, 0, name, NULL, NULL, ctx + ); + assert(!ret); + g_free(name); + } + } } return 0; diff --git a/hw/9pfs/9p-synth.h b/hw/9pfs/9p-synth.h index af7a993a1e..036d7e4a5b 100644 --- a/hw/9pfs/9p-synth.h +++ b/hw/9pfs/9p-synth.h @@ -55,6 +55,11 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, #define QTEST_V9FS_SYNTH_LOPEN_FILE "LOPEN" #define QTEST_V9FS_SYNTH_WRITE_FILE "WRITE" +/* for READDIR test */ +#define QTEST_V9FS_SYNTH_READDIR_DIR "ReadDirDir" +#define QTEST_V9FS_SYNTH_READDIR_FILE "ReadDirFile%d" +#define QTEST_V9FS_SYNTH_READDIR_NFILES 100 + /* Any write to the "FLUSH" file is handled one byte at a time by the * backend. If the byte is zero, the backend returns success (ie, 1), * otherwise it forces the server to try again forever. Thus allowing diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index b0e445d6bd..9e046f7acb 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1363,8 +1363,20 @@ static void coroutine_fn v9fs_version(void *opaque) s->proto_version = V9FS_PROTO_2000L; } else { v9fs_string_sprintf(&version, "unknown"); + /* skip min. msize check, reporting invalid version has priority */ + goto marshal; } + if (s->msize < P9_MIN_MSIZE) { + err = -EMSGSIZE; + error_report( + "9pfs: Client requested msize < minimum msize (" + stringify(P9_MIN_MSIZE) ") supported by this server." + ); + goto out; + } + +marshal: err = pdu_marshal(pdu, offset, "ds", s->msize, &version); if (err < 0) { goto out; @@ -2422,6 +2434,7 @@ static void coroutine_fn v9fs_readdir(void *opaque) int32_t count; uint32_t max_count; V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; retval = pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count); @@ -2430,6 +2443,14 @@ static void coroutine_fn v9fs_readdir(void *opaque) } trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); + /* Enough space for a R_readdir header: size[4] Rreaddir tag[2] count[4] */ + if (max_count > s->msize - 11) { + max_count = s->msize - 11; + warn_report_once( + "9p: bad client: T_readdir with count > msize - 11" + ); + } + fidp = get_fid(pdu, fid); if (fidp == NULL) { retval = -EINVAL; diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 8d07a0b301..b8f72a3bd9 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -100,6 +100,17 @@ typedef enum P9ProtoVersion { V9FS_PROTO_2000L = 0x02, } P9ProtoVersion; +/** + * @brief Minimum message size supported by this 9pfs server. + * + * A client establishes a session by sending a Tversion request along with a + * 'msize' parameter which suggests the server a maximum message size ever to be + * used for communication (for both requests and replies) between client and + * server during that session. If client suggests a 'msize' smaller than this + * value then session is denied by server with an error response. + */ +#define P9_MIN_MSIZE 4096 + #define P9_NOTAG UINT16_MAX #define P9_NOFID UINT32_MAX #define P9_MAXWELEM 16 diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index bb025e0bd0..221a78674e 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -347,11 +347,15 @@ static void stellaris_gptm_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); s->opaque[0] = s->opaque[1] = s; +} + +static void stellaris_gptm_realize(DeviceState *dev, Error **errp) +{ + gptm_state *s = STELLARIS_GPTM(dev); s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]); s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); } - /* System controller. */ typedef struct { @@ -1536,6 +1540,7 @@ static void stellaris_gptm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_stellaris_gptm; + dc->realize = stellaris_gptm_realize; } static const TypeInfo stellaris_gptm_info = { diff --git a/hw/bt/Kconfig b/hw/bt/Kconfig deleted file mode 100644 index e69de29bb2..0000000000 --- a/hw/bt/Kconfig +++ /dev/null diff --git a/hw/core/machine.c b/hw/core/machine.c index 3e288bfceb..d8e30e4895 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -148,7 +148,8 @@ GlobalProperty hw_compat_2_5[] = { const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); GlobalProperty hw_compat_2_4[] = { - { "virtio-blk-device", "scsi", "true" }, + /* Optional because the 'scsi' property is Linux-only */ + { "virtio-blk-device", "scsi", "true", .optional = true }, { "e1000", "extra_mac_registers", "off" }, { "virtio-pci", "x-disable-pcie", "on" }, { "virtio-pci", "migrate-extra", "off" }, diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c index 1e458d7fba..ccff5ed55b 100644 --- a/hw/dma/bcm2835_dma.c +++ b/hw/dma/bcm2835_dma.c @@ -54,7 +54,7 @@ static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c) { BCM2835DMAChan *ch = &s->chan[c]; - uint32_t data, xlen, ylen; + uint32_t data, xlen, xlen_td, ylen; int16_t dst_stride, src_stride; if (!(s->enable & (1 << c))) { @@ -70,18 +70,19 @@ static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c) ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16); ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20); + ylen = 1; if (ch->ti & BCM2708_DMA_TDMODE) { /* 2D transfer mode */ - ylen = (ch->txfr_len >> 16) & 0x3fff; + ylen += (ch->txfr_len >> 16) & 0x3fff; xlen = ch->txfr_len & 0xffff; dst_stride = ch->stride >> 16; src_stride = ch->stride & 0xffff; } else { - ylen = 1; xlen = ch->txfr_len; dst_stride = 0; src_stride = 0; } + xlen_td = xlen; while (ylen != 0) { /* Normal transfer mode */ @@ -117,6 +118,7 @@ static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c) if (--ylen != 0) { ch->source_ad += src_stride; ch->dest_ad += dst_stride; + xlen = xlen_td; } } ch->cs |= BCM2708_DMA_END; diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 9cda968501..fb973a983d 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -400,7 +400,7 @@ static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *bus) { - g_autofree char *cmd_flags; + g_autofree char *cmd_flags = NULL; uint32_t count; if (bus->cmd & (I2CD_RX_BUFF_ENABLE | I2CD_RX_BUFF_ENABLE)) { diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index 7c2a375527..e8e62bd96b 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "ui/console.h" #include "hw/i386/pc.h" #include "hw/input/i8042.h" @@ -269,6 +270,11 @@ static void vmmouse_realizefn(DeviceState *dev, Error **errp) DPRINTF("vmmouse_init\n"); + if (!object_resolve_path_type("", TYPE_VMPORT, NULL)) { + error_setg(errp, "vmmouse needs a machine with vmport"); + return; + } + vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s); vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s); vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s); diff --git a/hw/rtc/m48t59-internal.h b/hw/rtc/m48t59-internal.h index 4d4f2a6fed..cd648241e9 100644 --- a/hw/rtc/m48t59-internal.h +++ b/hw/rtc/m48t59-internal.h @@ -26,11 +26,6 @@ #ifndef HW_M48T59_INTERNAL_H #define HW_M48T59_INTERNAL_H -#define M48T59_DEBUG 0 - -#define NVRAM_PRINTF(fmt, ...) do { \ - if (M48T59_DEBUG) { printf(fmt , ## __VA_ARGS__); } } while (0) - /* * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has * alarm and a watchdog timer and related control registers. In the diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 1269134bcb..47d48054fd 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -35,6 +35,7 @@ #include "exec/address-spaces.h" #include "qemu/bcd.h" #include "qemu/module.h" +#include "trace.h" #include "m48t59-internal.h" #include "migration/vmstate.h" @@ -192,8 +193,7 @@ void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) struct tm tm; int tmp; - if (addr > 0x1FF8 && addr < 0x2000) - NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); + trace_m48txx_nvram_mem_write(addr, val); /* check for NVRAM access */ if ((NVRAM->model == 2 && addr < 0x7f8) || @@ -450,8 +450,7 @@ uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) } break; } - if (addr > 0x1FF9 && addr < 0x2000) - NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); + trace_m48txx_nvram_mem_read(addr, retval); return retval; } @@ -462,7 +461,7 @@ static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val, { M48t59State *NVRAM = opaque; - NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" => 0x%"PRIx64"\n", __func__, addr, val); + trace_m48txx_nvram_io_write(addr, val); switch (addr) { case 0: NVRAM->addr &= ~0x00FF; @@ -494,7 +493,7 @@ static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) retval = -1; break; } - NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" <= 0x%08x\n", __func__, addr, retval); + trace_m48txx_nvram_io_read(addr, retval); return retval; } diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index d6749f4616..52c1566198 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -17,3 +17,9 @@ pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks" # aspeed-rtc.c aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 + +# m48t59.c +m48txx_nvram_io_read(uint64_t addr, uint64_t value) "io read addr:0x%04" PRIx64 " value:0x%02" PRIx64 +m48txx_nvram_io_write(uint64_t addr, uint64_t value) "io write addr:0x%04" PRIx64 " value:0x%02" PRIx64 +m48txx_nvram_mem_read(uint32_t addr, uint32_t value) "mem read addr:0x%04x value:0x%02x" +m48txx_nvram_mem_write(uint32_t addr, uint32_t value) "mem write addr:0x%04x value:0x%02x" diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 11d476c4a2..ffd98727ee 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -24,7 +24,6 @@ #include "qemu/option.h" #include "sysemu/sysemu.h" #include "qemu/uuid.h" -#include "sysemu/cpus.h" #include "hw/firmware/smbios.h" #include "hw/loader.h" #include "hw/boards.h" diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index 85d122dbcb..74c58bcf24 100644 --- a/hw/timer/armv7m_systick.c +++ b/hw/timer/armv7m_systick.c @@ -216,6 +216,11 @@ static void systick_instance_init(Object *obj) memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); +} + +static void systick_realize(DeviceState *dev, Error **errp) +{ + SysTickState *s = SYSTICK(dev); s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s); } @@ -238,6 +243,7 @@ static void systick_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_systick; dc->reset = systick_reset; + dc->realize = systick_realize; } static const TypeInfo armv7m_systick_info = { diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index fb370ce0f0..06ec8a02c2 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -314,7 +314,11 @@ static void stm32f2xx_timer_init(Object *obj) memory_region_init_io(&s->iomem, obj, &stm32f2xx_timer_ops, s, "stm32f2xx_timer", 0x400); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +} +static void stm32f2xx_timer_realize(DeviceState *dev, Error **errp) +{ + STM32F2XXTimerState *s = STM32F2XXTIMER(dev); s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s); } @@ -325,6 +329,7 @@ static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data) dc->reset = stm32f2xx_timer_reset; device_class_set_props(dc, stm32f2xx_timer_properties); dc->vmsd = &vmstate_stm32f2xx_timer; + dc->realize = stm32f2xx_timer_realize; } static const TypeInfo stm32f2xx_timer_info = { diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index f0eaa75ce7..7cdba0560a 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -36,3 +36,8 @@ config VFIO_AP default y select VFIO depends on LINUX && S390_CCW_VIRTIO + +config VFIO_IGD + bool + default y if PC_PCI + depends on VFIO_PCI diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs index abad8b818c..9bb1c09e84 100644 --- a/hw/vfio/Makefile.objs +++ b/hw/vfio/Makefile.objs @@ -5,3 +5,4 @@ obj-$(CONFIG_VFIO_PLATFORM) += platform.o obj-$(CONFIG_VFIO_XGMAC) += calxeda-xgmac.o obj-$(CONFIG_VFIO_AMD_XGBE) += amd-xgbe.o obj-$(CONFIG_VFIO_AP) += ap.o +obj-$(CONFIG_VFIO_IGD) += igd.o diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c new file mode 100644 index 0000000000..64e332746b --- /dev/null +++ b/hw/vfio/igd.c @@ -0,0 +1,616 @@ +/* + * IGD device quirks + * + * Copyright Red Hat, Inc. 2016 + * + * Authors: + * Alex Williamson <alex.williamson@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/nvram/fw_cfg.h" +#include "pci.h" +#include "trace.h" + +/* + * Intel IGD support + * + * Obviously IGD is not a discrete device, this is evidenced not only by it + * being integrated into the CPU, but by the various chipset and BIOS + * dependencies that it brings along with it. Intel is trying to move away + * from this and Broadwell and newer devices can run in what Intel calls + * "Universal Pass-Through" mode, or UPT. Theoretically in UPT mode, nothing + * more is required beyond assigning the IGD device to a VM. There are + * however support limitations to this mode. It only supports IGD as a + * secondary graphics device in the VM and it doesn't officially support any + * physical outputs. + * + * The code here attempts to enable what we'll call legacy mode assignment, + * IGD retains most of the capabilities we expect for it to have on bare + * metal. To enable this mode, the IGD device must be assigned to the VM + * at PCI address 00:02.0, it must have a ROM, it very likely needs VGA + * support, we must have VM BIOS support for reserving and populating some + * of the required tables, and we need to tweak the chipset with revisions + * and IDs and an LPC/ISA bridge device. The intention is to make all of + * this happen automatically by installing the device at the correct VM PCI + * bus address. If any of the conditions are not met, we cross our fingers + * and hope the user knows better. + * + * NB - It is possible to enable physical outputs in UPT mode by supplying + * an OpRegion table. We don't do this by default because the guest driver + * behaves differently if an OpRegion is provided and no monitor is attached + * vs no OpRegion and a monitor being attached or not. Effectively, if a + * headless setup is desired, the OpRegion gets in the way of that. + */ + +/* + * This presumes the device is already known to be an Intel VGA device, so we + * take liberties in which device ID bits match which generation. This should + * not be taken as an indication that all the devices are supported, or even + * supportable, some of them don't even support VT-d. + * See linux:include/drm/i915_pciids.h for IDs. + */ +static int igd_gen(VFIOPCIDevice *vdev) +{ + if ((vdev->device_id & 0xfff) == 0xa84) { + return 8; /* Broxton */ + } + + switch (vdev->device_id & 0xff00) { + /* Old, untested, unavailable, unknown */ + case 0x0000: + case 0x2500: + case 0x2700: + case 0x2900: + case 0x2a00: + case 0x2e00: + case 0x3500: + case 0xa000: + return -1; + /* SandyBridge, IvyBridge, ValleyView, Haswell */ + case 0x0100: + case 0x0400: + case 0x0a00: + case 0x0c00: + case 0x0d00: + case 0x0f00: + return 6; + /* BroadWell, CherryView, SkyLake, KabyLake */ + case 0x1600: + case 0x1900: + case 0x2200: + case 0x5900: + return 8; + } + + return 8; /* Assume newer is compatible */ +} + +typedef struct VFIOIGDQuirk { + struct VFIOPCIDevice *vdev; + uint32_t index; + uint32_t bdsm; +} VFIOIGDQuirk; + +#define IGD_GMCH 0x50 /* Graphics Control Register */ +#define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ + + +/* + * The rather short list of registers that we copy from the host devices. + * The LPC/ISA bridge values are definitely needed to support the vBIOS, the + * host bridge values may or may not be needed depending on the guest OS. + * Since we're only munging revision and subsystem values on the host bridge, + * we don't require our own device. The LPC/ISA bridge needs to be our very + * own though. + */ +typedef struct { + uint8_t offset; + uint8_t len; +} IGDHostInfo; + +static const IGDHostInfo igd_host_bridge_infos[] = { + {PCI_REVISION_ID, 2}, + {PCI_SUBSYSTEM_VENDOR_ID, 2}, + {PCI_SUBSYSTEM_ID, 2}, +}; + +static const IGDHostInfo igd_lpc_bridge_infos[] = { + {PCI_VENDOR_ID, 2}, + {PCI_DEVICE_ID, 2}, + {PCI_REVISION_ID, 2}, + {PCI_SUBSYSTEM_VENDOR_ID, 2}, + {PCI_SUBSYSTEM_ID, 2}, +}; + +static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev, + struct vfio_region_info *info, + const IGDHostInfo *list, int len) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = pread(vdev->vbasedev.fd, pdev->config + list[i].offset, + list[i].len, info->offset + list[i].offset); + if (ret != list[i].len) { + error_report("IGD copy failed: %m"); + return -errno; + } + } + + return 0; +} + +/* + * Stuff a few values into the host bridge. + */ +static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info) +{ + PCIBus *bus; + PCIDevice *host_bridge; + int ret; + + bus = pci_device_root_bus(&vdev->pdev); + host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0)); + + if (!host_bridge) { + error_report("Can't find host bridge"); + return -ENODEV; + } + + ret = vfio_pci_igd_copy(vdev, host_bridge, info, igd_host_bridge_infos, + ARRAY_SIZE(igd_host_bridge_infos)); + if (!ret) { + trace_vfio_pci_igd_host_bridge_enabled(vdev->vbasedev.name); + } + + return ret; +} + +/* + * IGD LPC/ISA bridge support code. The vBIOS needs this, but we can't write + * arbitrary values into just any bridge, so we must create our own. We try + * to handle if the user has created it for us, which they might want to do + * to enable multifunction so we don't occupy the whole PCI slot. + */ +static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp) +{ + if (pdev->devfn != PCI_DEVFN(0x1f, 0)) { + error_setg(errp, "VFIO dummy ISA/LPC bridge must have address 1f.0"); + } +} + +static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "VFIO dummy ISA/LPC bridge for IGD assignment"; + dc->hotpluggable = false; + k->realize = vfio_pci_igd_lpc_bridge_realize; + k->class_id = PCI_CLASS_BRIDGE_ISA; +} + +static TypeInfo vfio_pci_igd_lpc_bridge_info = { + .name = "vfio-pci-igd-lpc-bridge", + .parent = TYPE_PCI_DEVICE, + .class_init = vfio_pci_igd_lpc_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void vfio_pci_igd_register_types(void) +{ + type_register_static(&vfio_pci_igd_lpc_bridge_info); +} + +type_init(vfio_pci_igd_register_types) + +static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info) +{ + PCIDevice *lpc_bridge; + int ret; + + lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x1f, 0)); + if (!lpc_bridge) { + lpc_bridge = pci_create_simple(pci_device_root_bus(&vdev->pdev), + PCI_DEVFN(0x1f, 0), "vfio-pci-igd-lpc-bridge"); + } + + ret = vfio_pci_igd_copy(vdev, lpc_bridge, info, igd_lpc_bridge_infos, + ARRAY_SIZE(igd_lpc_bridge_infos)); + if (!ret) { + trace_vfio_pci_igd_lpc_bridge_enabled(vdev->vbasedev.name); + } + + return ret; +} + +/* + * IGD Gen8 and newer support up to 8MB for the GTT and use a 64bit PTE + * entry, older IGDs use 2MB and 32bit. Each PTE maps a 4k page. Therefore + * we either have 2M/4k * 4 = 2k or 8M/4k * 8 = 16k as the maximum iobar index + * for programming the GTT. + * + * See linux:include/drm/i915_drm.h for shift and mask values. + */ +static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) +{ + uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); + int ggms, gen = igd_gen(vdev); + + gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); + ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; + if (gen > 6) { + ggms = 1 << ggms; + } + + ggms *= MiB; + + return (ggms / (4 * KiB)) * (gen < 8 ? 4 : 8); +} + +/* + * The IGD ROM will make use of stolen memory (GGMS) for support of VESA modes. + * Somehow the host stolen memory range is used for this, but how the ROM gets + * it is a mystery, perhaps it's hardcoded into the ROM. Thankfully though, it + * reprograms the GTT through the IOBAR where we can trap it and transpose the + * programming to the VM allocated buffer. That buffer gets reserved by the VM + * firmware via the fw_cfg entry added below. Here we're just monitoring the + * IOBAR address and data registers to detect a write sequence targeting the + * GTTADR. This code is developed by observed behavior and doesn't have a + * direct spec reference, unfortunately. + */ +static uint64_t vfio_igd_quirk_data_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + + igd->index = ~0; + + return vfio_region_read(&vdev->bars[4].region, addr + 4, size); +} + +static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + uint64_t val = data; + int gen = igd_gen(vdev); + + /* + * Programming the GGMS starts at index 0x1 and uses every 4th index (ie. + * 0x1, 0x5, 0x9, 0xd,...). For pre-Gen8 each 4-byte write is a whole PTE + * entry, with 0th bit enable set. For Gen8 and up, PTEs are 64bit, so + * entries 0x5 & 0xd are the high dword, in our case zero. Each PTE points + * to a 4k page, which we translate to a page from the VM allocated region, + * pointed to by the BDSM register. If this is not set, we fail. + * + * We trap writes to the full configured GTT size, but we typically only + * see the vBIOS writing up to (nearly) the 1MB barrier. In fact it often + * seems to miss the last entry for an even 1MB GTT. Doing a gratuitous + * write of that last entry does work, but is hopefully unnecessary since + * we clear the previous GTT on initialization. + */ + if ((igd->index % 4 == 1) && igd->index < vfio_igd_gtt_max(vdev)) { + if (gen < 8 || (igd->index % 8 == 1)) { + uint32_t base; + + base = pci_get_long(vdev->pdev.config + IGD_BDSM); + if (!base) { + hw_error("vfio-igd: Guest attempted to program IGD GTT before " + "BIOS reserved stolen memory. Unsupported BIOS?"); + } + + val = data - igd->bdsm + base; + } else { + val = 0; /* upper 32bits of pte, we only enable below 4G PTEs */ + } + + trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name, + igd->index, data, val); + } + + vfio_region_write(&vdev->bars[4].region, addr + 4, val, size); + + igd->index = ~0; +} + +static const MemoryRegionOps vfio_igd_data_quirk = { + .read = vfio_igd_quirk_data_read, + .write = vfio_igd_quirk_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t vfio_igd_quirk_index_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + + igd->index = ~0; + + return vfio_region_read(&vdev->bars[4].region, addr, size); +} + +static void vfio_igd_quirk_index_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + + igd->index = data; + + vfio_region_write(&vdev->bars[4].region, addr, data, size); +} + +static const MemoryRegionOps vfio_igd_index_quirk = { + .read = vfio_igd_quirk_index_read, + .write = vfio_igd_quirk_index_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) +{ + struct vfio_region_info *rom = NULL, *opregion = NULL, + *host = NULL, *lpc = NULL; + VFIOQuirk *quirk; + VFIOIGDQuirk *igd; + PCIDevice *lpc_bridge; + int i, ret, ggms_mb, gms_mb = 0, gen; + uint64_t *bdsm_size; + uint32_t gmch; + uint16_t cmd_orig, cmd; + Error *err = NULL; + + /* + * This must be an Intel VGA device at address 00:02.0 for us to even + * consider enabling legacy mode. The vBIOS has dependencies on the + * PCI bus address. + */ + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || nr != 4 || + &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x2, 0))) { + return; + } + + /* + * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we + * can stuff host values into, so if there's already one there and it's not + * one we can hack on, legacy mode is no-go. Sorry Q35. + */ + lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x1f, 0)); + if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), + "vfio-pci-igd-lpc-bridge")) { + error_report("IGD device %s cannot support legacy mode due to existing " + "devices at address 1f.0", vdev->vbasedev.name); + return; + } + + /* + * IGD is not a standard, they like to change their specs often. We + * only attempt to support back to SandBridge and we hope that newer + * devices maintain compatibility with generation 8. + */ + gen = igd_gen(vdev); + if (gen != 6 && gen != 8) { + error_report("IGD device %s is unsupported in legacy mode, " + "try SandyBridge or newer", vdev->vbasedev.name); + return; + } + + /* + * Most of what we're doing here is to enable the ROM to run, so if + * there's no ROM, there's no point in setting up this quirk. + * NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support. + */ + ret = vfio_get_region_info(&vdev->vbasedev, + VFIO_PCI_ROM_REGION_INDEX, &rom); + if ((ret || !rom->size) && !vdev->pdev.romfile) { + error_report("IGD device %s has no ROM, legacy mode disabled", + vdev->vbasedev.name); + goto out; + } + + /* + * Ignore the hotplug corner case, mark the ROM failed, we can't + * create the devices we need for legacy mode in the hotplug scenario. + */ + if (vdev->pdev.qdev.hotplugged) { + error_report("IGD device %s hotplugged, ROM disabled, " + "legacy mode disabled", vdev->vbasedev.name); + vdev->rom_read_failed = true; + goto out; + } + + /* + * Check whether we have all the vfio device specific regions to + * support legacy mode (added in Linux v4.6). If not, bail. + */ + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); + if (ret) { + error_report("IGD device %s does not support OpRegion access," + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); + if (ret) { + error_report("IGD device %s does not support host bridge access," + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); + if (ret) { + error_report("IGD device %s does not support LPC bridge access," + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); + + /* + * If IGD VGA Disable is clear (expected) and VGA is not already enabled, + * try to enable it. Probably shouldn't be using legacy mode without VGA, + * but also no point in us enabling VGA if disabled in hardware. + */ + if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) { + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); + error_report("IGD device %s failed to enable VGA access, " + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + /* Create our LPC/ISA bridge */ + ret = vfio_pci_igd_lpc_init(vdev, lpc); + if (ret) { + error_report("IGD device %s failed to create LPC bridge, " + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + /* Stuff some host values into the VM PCI host bridge */ + ret = vfio_pci_igd_host_init(vdev, host); + if (ret) { + error_report("IGD device %s failed to modify host bridge, " + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + /* Setup OpRegion access */ + ret = vfio_pci_igd_opregion_init(vdev, opregion, &err); + if (ret) { + error_append_hint(&err, "IGD legacy mode disabled\n"); + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); + goto out; + } + + /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ + quirk = vfio_quirk_alloc(2); + igd = quirk->data = g_malloc0(sizeof(*igd)); + igd->vdev = vdev; + igd->index = ~0; + igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); + igd->bdsm &= ~((1 * MiB) - 1); /* 1MB aligned */ + + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, + igd, "vfio-igd-index-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 0, &quirk->mem[0], 1); + + memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_data_quirk, + igd, "vfio-igd-data-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 4, &quirk->mem[1], 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + /* Determine the size of stolen memory needed for GTT */ + ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; + if (gen > 6) { + ggms_mb = 1 << ggms_mb; + } + + /* + * Assume we have no GMS memory, but allow it to be overrided by device + * option (experimental). The spec doesn't actually allow zero GMS when + * when IVD (IGD VGA Disable) is clear, but the claim is that it's unused, + * so let's not waste VM memory for it. + */ + gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8)); + + if (vdev->igd_gms) { + if (vdev->igd_gms <= 0x10) { + gms_mb = vdev->igd_gms * 32; + gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8); + } else { + error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms); + vdev->igd_gms = 0; + } + } + + /* + * Request reserved memory for stolen memory via fw_cfg. VM firmware + * must allocate a 1MB aligned reserved memory region below 4GB with + * the requested size (in bytes) for use by the Intel PCI class VGA + * device at VM address 00:02.0. The base address of this reserved + * memory region must be written to the device BDSM regsiter at PCI + * config offset 0x5C. + */ + bdsm_size = g_malloc(sizeof(*bdsm_size)); + *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * MiB); + fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", + bdsm_size, sizeof(*bdsm_size)); + + /* GMCH is read-only, emulated */ + pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); + pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); + pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); + + /* BDSM is read-write, emulated. The BIOS needs to be able to write it */ + pci_set_long(vdev->pdev.config + IGD_BDSM, 0); + pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); + pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); + + /* + * This IOBAR gives us access to GTTADR, which allows us to write to + * the GTT itself. So let's go ahead and write zero to all the GTT + * entries to avoid spurious DMA faults. Be sure I/O access is enabled + * before talking to the device. + */ + if (pread(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), + vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { + error_report("IGD device %s - failed to read PCI command register", + vdev->vbasedev.name); + } + + cmd = cmd_orig | PCI_COMMAND_IO; + + if (pwrite(vdev->vbasedev.fd, &cmd, sizeof(cmd), + vdev->config_offset + PCI_COMMAND) != sizeof(cmd)) { + error_report("IGD device %s - failed to write PCI command register", + vdev->vbasedev.name); + } + + for (i = 1; i < vfio_igd_gtt_max(vdev); i += 4) { + vfio_region_write(&vdev->bars[4].region, 0, i, 4); + vfio_region_write(&vdev->bars[4].region, 4, 0, 4); + } + + if (pwrite(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), + vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { + error_report("IGD device %s - failed to restore PCI command register", + vdev->vbasedev.name); + } + + trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); + +out: + g_free(rom); + g_free(opregion); + g_free(host); + g_free(lpc); +} diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 136f3a9ad6..2d348f8237 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -26,21 +26,6 @@ #include "pci.h" #include "trace.h" -/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */ -static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device) -{ - return (vendor == PCI_ANY_ID || vendor == vdev->vendor_id) && - (device == PCI_ANY_ID || device == vdev->device_id); -} - -static bool vfio_is_vga(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - uint16_t class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); - - return class == PCI_CLASS_DISPLAY_VGA; -} - /* * List of device ids/vendor ids for which to disable * option rom loading. This avoids the guest hangs during rom @@ -283,7 +268,7 @@ static const MemoryRegionOps vfio_ati_3c3_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static VFIOQuirk *vfio_quirk_alloc(int nr_mem) +VFIOQuirk *vfio_quirk_alloc(int nr_mem) { VFIOQuirk *quirk = g_new0(VFIOQuirk, 1); QLIST_INIT(&quirk->ioeventfds); @@ -1166,88 +1151,6 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) trace_vfio_quirk_rtl8168_probe(vdev->vbasedev.name); } -/* - * Intel IGD support - * - * Obviously IGD is not a discrete device, this is evidenced not only by it - * being integrated into the CPU, but by the various chipset and BIOS - * dependencies that it brings along with it. Intel is trying to move away - * from this and Broadwell and newer devices can run in what Intel calls - * "Universal Pass-Through" mode, or UPT. Theoretically in UPT mode, nothing - * more is required beyond assigning the IGD device to a VM. There are - * however support limitations to this mode. It only supports IGD as a - * secondary graphics device in the VM and it doesn't officially support any - * physical outputs. - * - * The code here attempts to enable what we'll call legacy mode assignment, - * IGD retains most of the capabilities we expect for it to have on bare - * metal. To enable this mode, the IGD device must be assigned to the VM - * at PCI address 00:02.0, it must have a ROM, it very likely needs VGA - * support, we must have VM BIOS support for reserving and populating some - * of the required tables, and we need to tweak the chipset with revisions - * and IDs and an LPC/ISA bridge device. The intention is to make all of - * this happen automatically by installing the device at the correct VM PCI - * bus address. If any of the conditions are not met, we cross our fingers - * and hope the user knows better. - * - * NB - It is possible to enable physical outputs in UPT mode by supplying - * an OpRegion table. We don't do this by default because the guest driver - * behaves differently if an OpRegion is provided and no monitor is attached - * vs no OpRegion and a monitor being attached or not. Effectively, if a - * headless setup is desired, the OpRegion gets in the way of that. - */ - -/* - * This presumes the device is already known to be an Intel VGA device, so we - * take liberties in which device ID bits match which generation. This should - * not be taken as an indication that all the devices are supported, or even - * supportable, some of them don't even support VT-d. - * See linux:include/drm/i915_pciids.h for IDs. - */ -static int igd_gen(VFIOPCIDevice *vdev) -{ - if ((vdev->device_id & 0xfff) == 0xa84) { - return 8; /* Broxton */ - } - - switch (vdev->device_id & 0xff00) { - /* Old, untested, unavailable, unknown */ - case 0x0000: - case 0x2500: - case 0x2700: - case 0x2900: - case 0x2a00: - case 0x2e00: - case 0x3500: - case 0xa000: - return -1; - /* SandyBridge, IvyBridge, ValleyView, Haswell */ - case 0x0100: - case 0x0400: - case 0x0a00: - case 0x0c00: - case 0x0d00: - case 0x0f00: - return 6; - /* BroadWell, CherryView, SkyLake, KabyLake */ - case 0x1600: - case 0x1900: - case 0x2200: - case 0x5900: - return 8; - } - - return 8; /* Assume newer is compatible */ -} - -typedef struct VFIOIGDQuirk { - struct VFIOPCIDevice *vdev; - uint32_t index; - uint32_t bdsm; -} VFIOIGDQuirk; - -#define IGD_GMCH 0x50 /* Graphics Control Register */ -#define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ #define IGD_ASLS 0xfc /* ASL Storage Register */ /* @@ -1299,519 +1202,6 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, } /* - * The rather short list of registers that we copy from the host devices. - * The LPC/ISA bridge values are definitely needed to support the vBIOS, the - * host bridge values may or may not be needed depending on the guest OS. - * Since we're only munging revision and subsystem values on the host bridge, - * we don't require our own device. The LPC/ISA bridge needs to be our very - * own though. - */ -typedef struct { - uint8_t offset; - uint8_t len; -} IGDHostInfo; - -static const IGDHostInfo igd_host_bridge_infos[] = { - {PCI_REVISION_ID, 2}, - {PCI_SUBSYSTEM_VENDOR_ID, 2}, - {PCI_SUBSYSTEM_ID, 2}, -}; - -static const IGDHostInfo igd_lpc_bridge_infos[] = { - {PCI_VENDOR_ID, 2}, - {PCI_DEVICE_ID, 2}, - {PCI_REVISION_ID, 2}, - {PCI_SUBSYSTEM_VENDOR_ID, 2}, - {PCI_SUBSYSTEM_ID, 2}, -}; - -static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev, - struct vfio_region_info *info, - const IGDHostInfo *list, int len) -{ - int i, ret; - - for (i = 0; i < len; i++) { - ret = pread(vdev->vbasedev.fd, pdev->config + list[i].offset, - list[i].len, info->offset + list[i].offset); - if (ret != list[i].len) { - error_report("IGD copy failed: %m"); - return -errno; - } - } - - return 0; -} - -/* - * Stuff a few values into the host bridge. - */ -static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info) -{ - PCIBus *bus; - PCIDevice *host_bridge; - int ret; - - bus = pci_device_root_bus(&vdev->pdev); - host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0)); - - if (!host_bridge) { - error_report("Can't find host bridge"); - return -ENODEV; - } - - ret = vfio_pci_igd_copy(vdev, host_bridge, info, igd_host_bridge_infos, - ARRAY_SIZE(igd_host_bridge_infos)); - if (!ret) { - trace_vfio_pci_igd_host_bridge_enabled(vdev->vbasedev.name); - } - - return ret; -} - -/* - * IGD LPC/ISA bridge support code. The vBIOS needs this, but we can't write - * arbitrary values into just any bridge, so we must create our own. We try - * to handle if the user has created it for us, which they might want to do - * to enable multifunction so we don't occupy the whole PCI slot. - */ -static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp) -{ - if (pdev->devfn != PCI_DEVFN(0x1f, 0)) { - error_setg(errp, "VFIO dummy ISA/LPC bridge must have address 1f.0"); - } -} - -static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->desc = "VFIO dummy ISA/LPC bridge for IGD assignment"; - dc->hotpluggable = false; - k->realize = vfio_pci_igd_lpc_bridge_realize; - k->class_id = PCI_CLASS_BRIDGE_ISA; -} - -static TypeInfo vfio_pci_igd_lpc_bridge_info = { - .name = "vfio-pci-igd-lpc-bridge", - .parent = TYPE_PCI_DEVICE, - .class_init = vfio_pci_igd_lpc_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static void vfio_pci_igd_register_types(void) -{ - type_register_static(&vfio_pci_igd_lpc_bridge_info); -} - -type_init(vfio_pci_igd_register_types) - -static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info) -{ - PCIDevice *lpc_bridge; - int ret; - - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x1f, 0)); - if (!lpc_bridge) { - lpc_bridge = pci_create_simple(pci_device_root_bus(&vdev->pdev), - PCI_DEVFN(0x1f, 0), "vfio-pci-igd-lpc-bridge"); - } - - ret = vfio_pci_igd_copy(vdev, lpc_bridge, info, igd_lpc_bridge_infos, - ARRAY_SIZE(igd_lpc_bridge_infos)); - if (!ret) { - trace_vfio_pci_igd_lpc_bridge_enabled(vdev->vbasedev.name); - } - - return ret; -} - -/* - * IGD Gen8 and newer support up to 8MB for the GTT and use a 64bit PTE - * entry, older IGDs use 2MB and 32bit. Each PTE maps a 4k page. Therefore - * we either have 2M/4k * 4 = 2k or 8M/4k * 8 = 16k as the maximum iobar index - * for programming the GTT. - * - * See linux:include/drm/i915_drm.h for shift and mask values. - */ -static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) -{ - uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); - int ggms, gen = igd_gen(vdev); - - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); - ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6) { - ggms = 1 << ggms; - } - - ggms *= MiB; - - return (ggms / (4 * KiB)) * (gen < 8 ? 4 : 8); -} - -/* - * The IGD ROM will make use of stolen memory (GGMS) for support of VESA modes. - * Somehow the host stolen memory range is used for this, but how the ROM gets - * it is a mystery, perhaps it's hardcoded into the ROM. Thankfully though, it - * reprograms the GTT through the IOBAR where we can trap it and transpose the - * programming to the VM allocated buffer. That buffer gets reserved by the VM - * firmware via the fw_cfg entry added below. Here we're just monitoring the - * IOBAR address and data registers to detect a write sequence targeting the - * GTTADR. This code is developed by observed behavior and doesn't have a - * direct spec reference, unfortunately. - */ -static uint64_t vfio_igd_quirk_data_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - - igd->index = ~0; - - return vfio_region_read(&vdev->bars[4].region, addr + 4, size); -} - -static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - uint64_t val = data; - int gen = igd_gen(vdev); - - /* - * Programming the GGMS starts at index 0x1 and uses every 4th index (ie. - * 0x1, 0x5, 0x9, 0xd,...). For pre-Gen8 each 4-byte write is a whole PTE - * entry, with 0th bit enable set. For Gen8 and up, PTEs are 64bit, so - * entries 0x5 & 0xd are the high dword, in our case zero. Each PTE points - * to a 4k page, which we translate to a page from the VM allocated region, - * pointed to by the BDSM register. If this is not set, we fail. - * - * We trap writes to the full configured GTT size, but we typically only - * see the vBIOS writing up to (nearly) the 1MB barrier. In fact it often - * seems to miss the last entry for an even 1MB GTT. Doing a gratuitous - * write of that last entry does work, but is hopefully unnecessary since - * we clear the previous GTT on initialization. - */ - if ((igd->index % 4 == 1) && igd->index < vfio_igd_gtt_max(vdev)) { - if (gen < 8 || (igd->index % 8 == 1)) { - uint32_t base; - - base = pci_get_long(vdev->pdev.config + IGD_BDSM); - if (!base) { - hw_error("vfio-igd: Guest attempted to program IGD GTT before " - "BIOS reserved stolen memory. Unsupported BIOS?"); - } - - val = data - igd->bdsm + base; - } else { - val = 0; /* upper 32bits of pte, we only enable below 4G PTEs */ - } - - trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name, - igd->index, data, val); - } - - vfio_region_write(&vdev->bars[4].region, addr + 4, val, size); - - igd->index = ~0; -} - -static const MemoryRegionOps vfio_igd_data_quirk = { - .read = vfio_igd_quirk_data_read, - .write = vfio_igd_quirk_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t vfio_igd_quirk_index_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - - igd->index = ~0; - - return vfio_region_read(&vdev->bars[4].region, addr, size); -} - -static void vfio_igd_quirk_index_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - - igd->index = data; - - vfio_region_write(&vdev->bars[4].region, addr, data, size); -} - -static const MemoryRegionOps vfio_igd_index_quirk = { - .read = vfio_igd_quirk_index_read, - .write = vfio_igd_quirk_index_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) -{ - struct vfio_region_info *rom = NULL, *opregion = NULL, - *host = NULL, *lpc = NULL; - VFIOQuirk *quirk; - VFIOIGDQuirk *igd; - PCIDevice *lpc_bridge; - int i, ret, ggms_mb, gms_mb = 0, gen; - uint64_t *bdsm_size; - uint32_t gmch; - uint16_t cmd_orig, cmd; - Error *err = NULL; - - /* - * This must be an Intel VGA device at address 00:02.0 for us to even - * consider enabling legacy mode. The vBIOS has dependencies on the - * PCI bus address. - */ - if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || nr != 4 || - &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x2, 0))) { - return; - } - - /* - * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we - * can stuff host values into, so if there's already one there and it's not - * one we can hack on, legacy mode is no-go. Sorry Q35. - */ - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x1f, 0)); - if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), - "vfio-pci-igd-lpc-bridge")) { - error_report("IGD device %s cannot support legacy mode due to existing " - "devices at address 1f.0", vdev->vbasedev.name); - return; - } - - /* - * IGD is not a standard, they like to change their specs often. We - * only attempt to support back to SandBridge and we hope that newer - * devices maintain compatibility with generation 8. - */ - gen = igd_gen(vdev); - if (gen != 6 && gen != 8) { - error_report("IGD device %s is unsupported in legacy mode, " - "try SandyBridge or newer", vdev->vbasedev.name); - return; - } - - /* - * Most of what we're doing here is to enable the ROM to run, so if - * there's no ROM, there's no point in setting up this quirk. - * NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support. - */ - ret = vfio_get_region_info(&vdev->vbasedev, - VFIO_PCI_ROM_REGION_INDEX, &rom); - if ((ret || !rom->size) && !vdev->pdev.romfile) { - error_report("IGD device %s has no ROM, legacy mode disabled", - vdev->vbasedev.name); - goto out; - } - - /* - * Ignore the hotplug corner case, mark the ROM failed, we can't - * create the devices we need for legacy mode in the hotplug scenario. - */ - if (vdev->pdev.qdev.hotplugged) { - error_report("IGD device %s hotplugged, ROM disabled, " - "legacy mode disabled", vdev->vbasedev.name); - vdev->rom_read_failed = true; - goto out; - } - - /* - * Check whether we have all the vfio device specific regions to - * support legacy mode (added in Linux v4.6). If not, bail. - */ - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); - if (ret) { - error_report("IGD device %s does not support OpRegion access," - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); - if (ret) { - error_report("IGD device %s does not support host bridge access," - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); - if (ret) { - error_report("IGD device %s does not support LPC bridge access," - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); - - /* - * If IGD VGA Disable is clear (expected) and VGA is not already enabled, - * try to enable it. Probably shouldn't be using legacy mode without VGA, - * but also no point in us enabling VGA if disabled in hardware. - */ - if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - error_report("IGD device %s failed to enable VGA access, " - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - /* Create our LPC/ISA bridge */ - ret = vfio_pci_igd_lpc_init(vdev, lpc); - if (ret) { - error_report("IGD device %s failed to create LPC bridge, " - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - /* Stuff some host values into the VM PCI host bridge */ - ret = vfio_pci_igd_host_init(vdev, host); - if (ret) { - error_report("IGD device %s failed to modify host bridge, " - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - /* Setup OpRegion access */ - ret = vfio_pci_igd_opregion_init(vdev, opregion, &err); - if (ret) { - error_append_hint(&err, "IGD legacy mode disabled\n"); - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - goto out; - } - - /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ - quirk = vfio_quirk_alloc(2); - igd = quirk->data = g_malloc0(sizeof(*igd)); - igd->vdev = vdev; - igd->index = ~0; - igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); - igd->bdsm &= ~((1 * MiB) - 1); /* 1MB aligned */ - - memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, - igd, "vfio-igd-index-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, - 0, &quirk->mem[0], 1); - - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_data_quirk, - igd, "vfio-igd-data-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, - 4, &quirk->mem[1], 1); - - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - - /* Determine the size of stolen memory needed for GTT */ - ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6) { - ggms_mb = 1 << ggms_mb; - } - - /* - * Assume we have no GMS memory, but allow it to be overrided by device - * option (experimental). The spec doesn't actually allow zero GMS when - * when IVD (IGD VGA Disable) is clear, but the claim is that it's unused, - * so let's not waste VM memory for it. - */ - gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8)); - - if (vdev->igd_gms) { - if (vdev->igd_gms <= 0x10) { - gms_mb = vdev->igd_gms * 32; - gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8); - } else { - error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms); - vdev->igd_gms = 0; - } - } - - /* - * Request reserved memory for stolen memory via fw_cfg. VM firmware - * must allocate a 1MB aligned reserved memory region below 4GB with - * the requested size (in bytes) for use by the Intel PCI class VGA - * device at VM address 00:02.0. The base address of this reserved - * memory region must be written to the device BDSM regsiter at PCI - * config offset 0x5C. - */ - bdsm_size = g_malloc(sizeof(*bdsm_size)); - *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * MiB); - fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", - bdsm_size, sizeof(*bdsm_size)); - - /* GMCH is read-only, emulated */ - pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); - pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); - pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); - - /* BDSM is read-write, emulated. The BIOS needs to be able to write it */ - pci_set_long(vdev->pdev.config + IGD_BDSM, 0); - pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); - pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); - - /* - * This IOBAR gives us access to GTTADR, which allows us to write to - * the GTT itself. So let's go ahead and write zero to all the GTT - * entries to avoid spurious DMA faults. Be sure I/O access is enabled - * before talking to the device. - */ - if (pread(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { - error_report("IGD device %s - failed to read PCI command register", - vdev->vbasedev.name); - } - - cmd = cmd_orig | PCI_COMMAND_IO; - - if (pwrite(vdev->vbasedev.fd, &cmd, sizeof(cmd), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd)) { - error_report("IGD device %s - failed to write PCI command register", - vdev->vbasedev.name); - } - - for (i = 1; i < vfio_igd_gtt_max(vdev); i += 4) { - vfio_region_write(&vdev->bars[4].region, 0, i, 4); - vfio_region_write(&vdev->bars[4].region, 4, 0, 4); - } - - if (pwrite(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { - error_report("IGD device %s - failed to restore PCI command register", - vdev->vbasedev.name); - } - - trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); - -out: - g_free(rom); - g_free(opregion); - g_free(host); - g_free(lpc); -} - -/* * Common quirk probe entry points. */ void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) @@ -1860,7 +1250,9 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) vfio_probe_nvidia_bar5_quirk(vdev, nr); vfio_probe_nvidia_bar0_quirk(vdev, nr); vfio_probe_rtl8168_bar2_quirk(vdev, nr); +#ifdef CONFIG_VFIO_IGD vfio_probe_igd_bar4_quirk(vdev, nr); +#endif } void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 35626cd63e..0da7a20a7e 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -172,6 +172,21 @@ typedef struct VFIOPCIDevice { Notifier irqchip_change_notifier; } VFIOPCIDevice; +/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */ +static inline bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device) +{ + return (vendor == PCI_ANY_ID || vendor == vdev->vendor_id) && + (device == PCI_ANY_ID || device == vdev->device_id); +} + +static inline bool vfio_is_vga(VFIOPCIDevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + uint16_t class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); + + return class == PCI_CLASS_DISPLAY_VGA; +} + uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len); @@ -189,6 +204,8 @@ void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); void vfio_quirk_reset(VFIOPCIDevice *vdev); +VFIOQuirk *vfio_quirk_alloc(int nr_mem); +void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index ba488818d2..99c674e949 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -108,7 +108,7 @@ void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev, /* * before qdev initialization(qdev_init()), this function sets bus_name and - * map_irq callback which are necessry for pci_bridge_initfn() to + * map_irq callback which are necessary for pci_bridge_initfn() to * initialize bus. */ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 6358a324a7..7956e9054a 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -41,7 +41,6 @@ extern const char *keyboard_layout; extern int win2k_install_hack; extern int alt_grab; extern int ctrl_grab; -extern int cursor_hide; extern int graphic_rotate; extern int no_shutdown; extern int old_param; diff --git a/io/channel-websock.c b/io/channel-websock.c index fc36d44eba..47a0e941d9 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -49,13 +49,20 @@ "Server: QEMU VNC\r\n" \ "Date: %s\r\n" +#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_WITH_PROTO_RES_OK \ + "HTTP/1.1 101 Switching Protocols\r\n" \ + QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \ + "Upgrade: websocket\r\n" \ + "Connection: Upgrade\r\n" \ + "Sec-WebSocket-Accept: %s\r\n" \ + "Sec-WebSocket-Protocol: binary\r\n" \ + "\r\n" #define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK \ "HTTP/1.1 101 Switching Protocols\r\n" \ QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \ "Upgrade: websocket\r\n" \ "Connection: Upgrade\r\n" \ "Sec-WebSocket-Accept: %s\r\n" \ - "Sec-WebSocket-Protocol: binary\r\n" \ "\r\n" #define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND \ "HTTP/1.1 404 Not Found\r\n" \ @@ -336,6 +343,7 @@ qio_channel_websock_find_header(QIOChannelWebsockHTTPHeader *hdrs, static void qio_channel_websock_handshake_send_res_ok(QIOChannelWebsock *ioc, const char *key, + const bool use_protocols, Error **errp) { char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + @@ -361,8 +369,14 @@ static void qio_channel_websock_handshake_send_res_ok(QIOChannelWebsock *ioc, } date = qio_channel_websock_date_str(); - qio_channel_websock_handshake_send_res( - ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK, date, accept); + if (use_protocols) { + qio_channel_websock_handshake_send_res( + ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_WITH_PROTO_RES_OK, + date, accept); + } else { + qio_channel_websock_handshake_send_res( + ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK, date, accept); + } g_free(date); g_free(accept); @@ -387,10 +401,6 @@ static void qio_channel_websock_handshake_process(QIOChannelWebsock *ioc, protocols = qio_channel_websock_find_header( hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL); - if (!protocols) { - error_setg(errp, "Missing websocket protocol header data"); - goto bad_request; - } version = qio_channel_websock_find_header( hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_VERSION); @@ -430,10 +440,12 @@ static void qio_channel_websock_handshake_process(QIOChannelWebsock *ioc, trace_qio_channel_websock_http_request(ioc, protocols, version, host, connection, upgrade, key); - if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) { - error_setg(errp, "No '%s' protocol is supported by client '%s'", - QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols); - goto bad_request; + if (protocols) { + if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) { + error_setg(errp, "No '%s' protocol is supported by client '%s'", + QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols); + goto bad_request; + } } if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) { @@ -467,7 +479,7 @@ static void qio_channel_websock_handshake_process(QIOChannelWebsock *ioc, goto bad_request; } - qio_channel_websock_handshake_send_res_ok(ioc, key, errp); + qio_channel_websock_handshake_send_res_ok(ioc, key, !!protocols, errp); return; bad_request: diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index d0e0af893a..558fe06b8f 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -2351,7 +2351,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) continue; } - qmp_nbd_server_add(info->value->device, false, NULL, + qmp_nbd_server_add(info->value->device, false, NULL, false, NULL, true, writable, false, NULL, &local_err); if (local_err != NULL) { @@ -2373,7 +2373,7 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) bool writable = qdict_get_try_bool(qdict, "writable", false); Error *local_err = NULL; - qmp_nbd_server_add(device, !!name, name, true, writable, + qmp_nbd_server_add(device, !!name, name, false, NULL, true, writable, false, NULL, &local_err); hmp_handle_error(mon, local_err); } diff --git a/monitor/misc.c b/monitor/misc.c index 4752150a67..9c3484d0a7 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -1749,6 +1749,7 @@ static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) 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); } diff --git a/python/qemu/accel.py b/python/qemu/accel.py index 0b38ddf0ab..36ae85791e 100644 --- a/python/qemu/accel.py +++ b/python/qemu/accel.py @@ -24,7 +24,8 @@ LOG = logging.getLogger(__name__) # support which often includes its 32 bit cousin. ADDITIONAL_ARCHES = { "x86_64" : "i386", - "aarch64" : "armhf" + "aarch64" : "armhf", + "ppc64le" : "ppc64", } def list_accel(qemu_bin): diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 734efd8536..183d8f3d38 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -112,6 +112,7 @@ class QEMUMachine(object): self._sock_dir = sock_dir self._launched = False self._machine = None + self._console_index = 0 self._console_set = False self._console_device_type = None self._console_address = None @@ -241,6 +242,8 @@ class QEMUMachine(object): 'chardev=mon,mode=control']) if self._machine is not None: args.extend(['-machine', self._machine]) + for i in range(self._console_index): + args.extend(['-serial', 'null']) if self._console_set: self._console_address = os.path.join(self._sock_dir, self._name + "-console.sock") @@ -527,7 +530,7 @@ class QEMUMachine(object): """ self._machine = machine_type - def set_console(self, device_type=None): + def set_console(self, device_type=None, console_index=0): """ Sets the device type for a console device @@ -548,9 +551,14 @@ class QEMUMachine(object): chardev:console" command line argument will be used instead, resorting to the machine's default device type. + @param console_index: the index of the console device to use. + If not zero, the command line will create + 'index - 1' consoles and connect them to + the 'null' backing character device. """ self._console_set = True self._console_device_type = device_type + self._console_index = console_index @property def console_socket(self): diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index 5c8cf6a056..f40586eedd 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -1,5 +1,4 @@ -# QEMU Monitor Protocol Python class -# +""" QEMU Monitor Protocol Python class """ # Copyright (C) 2009, 2010 Red Hat Inc. # # Authors: @@ -15,29 +14,37 @@ import logging class QMPError(Exception): - pass + """ + QMP base exception + """ class QMPConnectError(QMPError): - pass + """ + QMP connection exception + """ class QMPCapabilitiesError(QMPError): - pass + """ + QMP negotiate capabilities exception + """ class QMPTimeoutError(QMPError): - pass + """ + QMP timeout exception + """ -class QEMUMonitorProtocol(object): +class QEMUMonitorProtocol: + """ + Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then + allow to handle commands and events. + """ #: Logger object for debugging messages logger = logging.getLogger('QMP') - #: Socket's error class - error = socket.error - #: Socket's timeout - timeout = socket.timeout def __init__(self, address, server=False): """ @@ -47,7 +54,7 @@ class QEMUMonitorProtocol(object): or a tuple in the form ( address, port ) for a TCP connection @param server: server mode listens on the socket (bool) - @raise socket.error on socket connection errors + @raise OSError on socket connection errors @note No connection is established, this is done by the connect() or accept() methods """ @@ -73,7 +80,7 @@ class QEMUMonitorProtocol(object): raise QMPConnectError # Greeting seems ok, negotiate capabilities resp = self.cmd('qmp_capabilities') - if "return" in resp: + if resp and "return" in resp: return greeting raise QMPCapabilitiesError @@ -81,7 +88,7 @@ class QEMUMonitorProtocol(object): while True: data = self.__sockfile.readline() if not data: - return + return None resp = json.loads(data) if 'event' in resp: self.logger.debug("<<< %s", resp) @@ -107,8 +114,8 @@ class QEMUMonitorProtocol(object): self.__sock.setblocking(0) try: self.__json_read() - except socket.error as err: - if err[0] == errno.EAGAIN: + except OSError as err: + if err.errno == errno.EAGAIN: # No data available pass self.__sock.setblocking(1) @@ -128,12 +135,21 @@ class QEMUMonitorProtocol(object): raise QMPConnectError("Error while reading from socket") self.__sock.settimeout(None) + def __enter__(self): + # Implement context manager enter function. + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + # Implement context manager exit function. + self.close() + return False + def connect(self, negotiate=True): """ Connect to the QMP Monitor and perform capabilities negotiation. - @return QMP greeting dict - @raise socket.error on socket connection errors + @return QMP greeting dict, or None if negotiate is false + @raise OSError on socket connection errors @raise QMPConnectError if the greeting is not received @raise QMPCapabilitiesError if fails to negotiate capabilities """ @@ -141,17 +157,25 @@ class QEMUMonitorProtocol(object): self.__sockfile = self.__sock.makefile() if negotiate: return self.__negotiate_capabilities() + return None - def accept(self): + def accept(self, timeout=15.0): """ Await connection from QMP Monitor and perform capabilities negotiation. + @param timeout: timeout in seconds (nonnegative float number, or + None). The value passed will set the behavior of the + underneath QMP socket as described in [1]. Default value + is set to 15.0. @return QMP greeting dict - @raise socket.error on socket connection errors + @raise OSError on socket connection errors @raise QMPConnectError if the greeting is not received @raise QMPCapabilitiesError if fails to negotiate capabilities + + [1] + https://docs.python.org/3/library/socket.html#socket.socket.settimeout """ - self.__sock.settimeout(15) + self.__sock.settimeout(timeout) self.__sock, _ = self.__sock.accept() self.__sockfile = self.__sock.makefile() return self.__negotiate_capabilities() @@ -167,10 +191,10 @@ class QEMUMonitorProtocol(object): self.logger.debug(">>> %s", qmp_cmd) try: self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8')) - except socket.error as err: - if err[0] == errno.EPIPE: - return - raise socket.error(err) + except OSError as err: + if err.errno == errno.EPIPE: + return None + raise err resp = self.__json_read() self.logger.debug("<<< %s", resp) return resp @@ -243,14 +267,35 @@ class QEMUMonitorProtocol(object): self.__events = [] def close(self): - self.__sock.close() - self.__sockfile.close() + """ + Close the socket and socket file. + """ + if self.__sock: + self.__sock.close() + if self.__sockfile: + self.__sockfile.close() def settimeout(self, timeout): + """ + Set the socket timeout. + + @param timeout (float): timeout in seconds, or None. + @note This is a wrap around socket.settimeout + """ self.__sock.settimeout(timeout) def get_sock_fd(self): + """ + Get the socket file descriptor. + + @return The file descriptor number. + """ return self.__sock.fileno() def is_scm_available(self): + """ + Check if the socket allows for SCM_RIGHTS. + + @return True if SCM_RIGHTS is available, otherwise False. + """ return self.__sock.family == socket.AF_UNIX diff --git a/qapi/audio.json b/qapi/audio.json index 83312b2339..d8c507cced 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -276,7 +276,7 @@ # Since: 4.0 ## { 'enum': 'AudioFormat', - 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] } + 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32', 'f32' ] } ## # @AudiodevDriver: diff --git a/qapi/block.json b/qapi/block.json index 145c268bb6..7898104dae 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -250,9 +250,12 @@ # @name: Export name. If unspecified, the @device parameter is used as the # export name. (Since 2.12) # +# @description: Free-form description of the export, up to 4096 bytes. +# (Since 5.0) +# # @writable: Whether clients should be able to write to the device via the # NBD connection (default false). - +# # @bitmap: Also export the dirty bitmap reachable from @device, so the # NBD client can use NBD_OPT_SET_META_CONTEXT with # "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0) @@ -263,8 +266,8 @@ # Since: 1.3.0 ## { 'command': 'nbd-server-add', - 'data': {'device': 'str', '*name': 'str', '*writable': 'bool', - '*bitmap': 'str' } } + 'data': {'device': 'str', '*name': 'str', '*description': 'str', + '*writable': 'bool', '*bitmap': 'str' } } ## # @NbdServerRemoveMode: diff --git a/qapi/ui.json b/qapi/ui.json index e04525d8b4..f8c803fe43 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1144,6 +1144,8 @@ # @type: Which DisplayType qemu should use. # @full-screen: Start user interface in fullscreen mode (default: off). # @window-close: Allow to quit qemu with window close button (default: on). +# @show-cursor: Force showing the mouse cursor (default: off). +# (since: 5.0) # @gl: Enable OpenGL support (default: off). # # Since: 2.12 @@ -1153,6 +1155,7 @@ 'base' : { 'type' : 'DisplayType', '*full-screen' : 'bool', '*window-close' : 'bool', + '*show-cursor' : 'bool', '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', 'data' : { 'gtk' : 'DisplayGTK', diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi index ea3e10bde3..0671c26c80 100644 --- a/qemu-deprecated.texi +++ b/qemu-deprecated.texi @@ -148,6 +148,11 @@ QEMU 5.0 introduced an alternative syntax to specify the size of the translation block cache, @option{-accel tcg,tb-size=}. The new syntax deprecates the previously available @option{-tb-size} option. +@subsection -show-cursor option (since 5.0) + +Use @option{-display sdl,show-cursor=on} or + @option{-display gtk,show-cursor=on} instead. + @section QEMU Machine Protocol (QMP) commands @subsection change (since 2.5.0) @@ -313,37 +318,6 @@ The above, converted to the current supported format: @section Related binaries -@subsection qemu-nbd --partition (since 4.0.0) - -The ``qemu-nbd --partition $digit'' code (also spelled @option{-P}) -can only handle MBR partitions, and has never correctly handled -logical partitions beyond partition 5. If you know the offset and -length of the partition (perhaps by using @code{sfdisk} within the -guest), you can achieve the effect of exporting just that subset of -the disk by use of the @option{--image-opts} option with a raw -blockdev using the @code{offset} and @code{size} parameters layered on -top of any other existing blockdev. For example, if partition 1 is -100MiB long starting at 1MiB, the old command: - -@code{qemu-nbd -t -P 1 -f qcow2 file.qcow2} - -can be rewritten as: - -@code{qemu-nbd -t --image-opts driver=raw,offset=1M,size=100M,file.driver=qcow2,file.backing.driver=file,file.backing.filename=file.qcow2} - -Alternatively, the @code{nbdkit} project provides a more powerful -partition filter on top of its nbd plugin, which can be used to select -an arbitrary MBR or GPT partition on top of any other full-image NBD -export. Using this to rewrite the above example results in: - -@code{qemu-nbd -t -k /tmp/sock -f qcow2 file.qcow2 &} -@code{nbdkit -f --filter=partition nbd socket=/tmp/sock partition=1} - -Note that if you are exposing the export via /dev/nbd0, it is easier -to just export the entire image and then mount only /dev/nbd0p1 than -it is to reinvoke @command{qemu-nbd -c /dev/nbd0} limited to just a -subset of the image. - @subsection qemu-img convert -n -o (since 4.2.0) All options specified in @option{-o} are image creation options, so @@ -351,14 +325,6 @@ they have no effect when used with @option{-n} to skip image creation. Silently ignored options can be confusing, so this combination of options will be made an error in future versions. -@section Build system - -@subsection Python 2 support (since 4.1.0) - -In the future, QEMU will require Python 3 to be available at -build time. Support for Python 2 in scripts shipped with QEMU -is deprecated. - @section Backwards compatibility @subsection Runnability guarantee of CPU models (since 4.1.0) @@ -400,3 +366,21 @@ trouble after a recent upgrade. The "autoload" parameter has been ignored since 2.12.0. All bitmaps are automatically loaded from qcow2 images. + +@section Related binaries + +@subsection qemu-nbd --partition (removed in 5.0.0) + +The ``qemu-nbd --partition $digit'' code (also spelled @option{-P}) +could only handle MBR partitions, and never correctly handled logical +partitions beyond partition 5. Exporting a partition can still be +done by utilizing the @option{--image-opts} option with a raw blockdev +using the @code{offset} and @code{size} parameters layered on top of +any other existing blockdev. For example, if partition 1 is 100MiB +long starting at 1MiB, the old command: + +@code{qemu-nbd -t -P 1 -f qcow2 file.qcow2} + +can be rewritten as: + +@code{qemu-nbd -t --image-opts driver=raw,offset=1M,size=100M,file.driver=qcow2,file.file.driver=file,file.file.filename=file.qcow2} diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 3fd836ca90..d7fbc6b1f4 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -16,9 +16,9 @@ SRST ERST DEF("bench", img_bench, - "bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-i aio] [-w] [-U] filename") + "bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-i aio] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] [-U] filename") SRST -.. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-n] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-i AIO] [-w] [-U] FILENAME +.. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-i AIO] [-n] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-w] [-U] FILENAME ERST DEF("check", img_check, "check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") diff --git a/qemu-nbd.c b/qemu-nbd.c index db29a0d0ed..4aa005004e 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -100,7 +100,6 @@ static void usage(const char *name) "\n" "Exposing part of the image:\n" " -o, --offset=OFFSET offset into the image\n" -" -P, --partition=NUM only expose partition NUM\n" " -B, --bitmap=NAME expose a persistent dirty bitmap\n" "\n" "General purpose options:\n" @@ -156,96 +155,6 @@ QEMU_COPYRIGHT "\n" , name); } -struct partition_record -{ - uint8_t bootable; - uint8_t start_head; - uint32_t start_cylinder; - uint8_t start_sector; - uint8_t system; - uint8_t end_head; - uint8_t end_cylinder; - uint8_t end_sector; - uint32_t start_sector_abs; - uint32_t nb_sectors_abs; -}; - -static void read_partition(uint8_t *p, struct partition_record *r) -{ - r->bootable = p[0]; - r->start_head = p[1]; - r->start_cylinder = p[3] | ((p[2] << 2) & 0x0300); - r->start_sector = p[2] & 0x3f; - r->system = p[4]; - r->end_head = p[5]; - r->end_cylinder = p[7] | ((p[6] << 2) & 0x300); - r->end_sector = p[6] & 0x3f; - - r->start_sector_abs = ldl_le_p(p + 8); - r->nb_sectors_abs = ldl_le_p(p + 12); -} - -static int find_partition(BlockBackend *blk, int partition, - uint64_t *offset, uint64_t *size) -{ - struct partition_record mbr[4]; - uint8_t data[MBR_SIZE]; - int i; - int ext_partnum = 4; - int ret; - - ret = blk_pread(blk, 0, data, sizeof(data)); - if (ret < 0) { - error_report("error while reading: %s", strerror(-ret)); - exit(EXIT_FAILURE); - } - - if (data[510] != 0x55 || data[511] != 0xaa) { - return -EINVAL; - } - - for (i = 0; i < 4; i++) { - read_partition(&data[446 + 16 * i], &mbr[i]); - - if (!mbr[i].system || !mbr[i].nb_sectors_abs) { - continue; - } - - if (mbr[i].system == 0xF || mbr[i].system == 0x5) { - struct partition_record ext[4]; - uint8_t data1[MBR_SIZE]; - int j; - - ret = blk_pread(blk, mbr[i].start_sector_abs * MBR_SIZE, - data1, sizeof(data1)); - if (ret < 0) { - error_report("error while reading: %s", strerror(-ret)); - exit(EXIT_FAILURE); - } - - for (j = 0; j < 4; j++) { - read_partition(&data1[446 + 16 * j], &ext[j]); - if (!ext[j].system || !ext[j].nb_sectors_abs) { - continue; - } - - if ((ext_partnum + j + 1) == partition) { - *offset = (uint64_t)ext[j].start_sector_abs << 9; - *size = (uint64_t)ext[j].nb_sectors_abs << 9; - return 0; - } - } - ext_partnum += 4; - } else if ((i + 1) == partition) { - *offset = (uint64_t)mbr[i].start_sector_abs << 9; - *size = (uint64_t)mbr[i].nb_sectors_abs << 9; - return 0; - } - } - - return -ENOENT; -} - static void termsig_handler(int signum) { atomic_cmpxchg(&state, RUNNING, TERMINATE); @@ -617,7 +526,7 @@ int main(int argc, char **argv) int64_t fd_size; QemuOpts *sn_opts = NULL; const char *sn_id_or_name = NULL; - const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L"; + const char *sopt = "hVb:o:p:rsnc:dvk:e:f:tl:x:T:D:B:L"; struct option lopt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -626,7 +535,6 @@ int main(int argc, char **argv) { "socket", required_argument, NULL, 'k' }, { "offset", required_argument, NULL, 'o' }, { "read-only", no_argument, NULL, 'r' }, - { "partition", required_argument, NULL, 'P' }, { "bitmap", required_argument, NULL, 'B' }, { "connect", required_argument, NULL, 'c' }, { "disconnect", no_argument, NULL, 'd' }, @@ -657,7 +565,6 @@ int main(int argc, char **argv) int ch; int opt_ind = 0; int flags = BDRV_O_RDWR; - int partition = 0; int ret = 0; bool seen_cache = false; bool seen_discard = false; @@ -789,15 +696,6 @@ int main(int argc, char **argv) readonly = true; flags &= ~BDRV_O_RDWR; break; - case 'P': - warn_report("The '-P' option is deprecated; use --image-opts with " - "a raw device wrapper for subset exports instead"); - if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 || - partition < 1 || partition > 8) { - error_report("Invalid partition '%s'", optarg); - exit(EXIT_FAILURE); - } - break; case 'B': bitmap = optarg; break; @@ -894,7 +792,7 @@ int main(int argc, char **argv) error_report("List mode is incompatible with a file name"); exit(EXIT_FAILURE); } - if (export_name || export_description || dev_offset || partition || + if (export_name || export_description || dev_offset || device || disconnect || fmt || sn_id_or_name || bitmap || seen_aio || seen_discard || seen_cache) { error_report("List mode is incompatible with per-device settings"); @@ -1158,33 +1056,6 @@ int main(int argc, char **argv) } fd_size -= dev_offset; - if (partition) { - uint64_t limit; - - if (dev_offset) { - error_report("Cannot request partition and offset together"); - exit(EXIT_FAILURE); - } - ret = find_partition(blk, partition, &dev_offset, &limit); - if (ret < 0) { - error_report("Could not find partition %d: %s", partition, - strerror(-ret)); - exit(EXIT_FAILURE); - } - /* - * MBR partition limits are (32-bit << 9); this assert lets - * the compiler know that we can't overflow 64 bits. - */ - assert(dev_offset + limit >= dev_offset); - if (dev_offset + limit > fd_size) { - error_report("Discovered partition %d at offset %" PRIu64 - " size %" PRIu64 ", but size exceeds file length %" - PRId64, partition, dev_offset, limit, fd_size); - exit(EXIT_FAILURE); - } - fd_size = limit; - } - export = nbd_export_new(bs, dev_offset, fd_size, export_name, export_description, bitmap, readonly, shared > 1, nbd_export_closed, writethrough, NULL, diff --git a/qemu-options.hx b/qemu-options.hx index 224a8e8712..ac315c1ac4 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1955,7 +1955,7 @@ STEXI Start in full screen. ETEXI -DEF("g", 1, QEMU_OPTION_g , +DEF("g", HAS_ARG, QEMU_OPTION_g , "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n", QEMU_ARCH_PPC | QEMU_ARCH_SPARC | QEMU_ARCH_M68K) STEXI @@ -2428,8 +2428,7 @@ Use @option{model=help} to list the available device types. The hardware MAC address can be set with @option{mac=@var{macaddr}}. The following two example do exactly the same, to show how @option{-nic} can -be used to shorten the command line length (note that the e1000 is the default -on i386, so the @option{model=e1000} parameter could even be omitted here, too): +be used to shorten the command line length: @example @value{qemu_system} -netdev user,id=n1,ipv6=off -device e1000,netdev=n1,mac=52:54:98:76:54:32 @value{qemu_system} -nic user,ipv6=off,model=e1000,mac=52:54:98:76:54:32 @@ -2843,9 +2842,12 @@ netdev with ID @var{nd} by using the @option{netdev=@var{nd}} option. Legacy option to configure or create an on-board (or machine default) Network Interface Card(NIC) and connect it either to the emulated hub with ID 0 (i.e. the default hub), or to the netdev @var{nd}. -The NIC is an e1000 by default on the PC target. Optionally, the MAC address -can be changed to @var{mac}, the device address set to @var{addr} (PCI cards -only), and a @var{name} can be assigned for use in monitor commands. +If @var{model} is omitted, then the default NIC model associated with +the machine type is used. Note that the default NIC model may change in +future QEMU releases, so it is highly recommended to always specify a model. +Optionally, the MAC address can be changed to @var{mac}, the device +address set to @var{addr} (PCI cards only), and a @var{name} can be +assigned for use in monitor commands. Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors that the card should have; this option currently only affects virtio cards; set @var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single diff --git a/scripts/analyse-9p-simpletrace.py b/scripts/analyse-9p-simpletrace.py index 710e01adba..7dfcb6ba2f 100755 --- a/scripts/analyse-9p-simpletrace.py +++ b/scripts/analyse-9p-simpletrace.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Pretty print 9p simpletrace log # Usage: ./analyse-9p-simpletrace <trace-events> <trace-pid> # # Author: Harsh Prateek Bora -from __future__ import print_function import os import simpletrace diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index 7d9b574300..63c11f4fce 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Analyse lock events and compute statistics @@ -6,7 +6,6 @@ # Author: Alex Bennée <alex.bennee@linaro.org> # -from __future__ import print_function import simpletrace import argparse import numpy as np diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 3aef6e3dfe..ce43a306f8 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1460,6 +1460,12 @@ sub process { } } +# Only allow Python 3 interpreter + if ($realline == 1 && + $line =~ /^\+#!\ *\/usr\/bin\/(?:env )?python$/) { + ERROR("please use python3 interpreter\n" . $herecurr); + } + # Accept git diff extended headers as valid patches if ($line =~ /^(?:rename|copy) (?:from|to) [\w\/\.\-]+\s*$/) { $is_patch = 1; diff --git a/scripts/decodetree.py b/scripts/decodetree.py index d8c59cab60..2a8f2b6e06 100755 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2018 Linaro Limited # # This library is free software; you can redistribute it and/or diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 15f213a6cd..305d0427af 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2017 Red Hat Inc # @@ -23,7 +23,6 @@ Run QEMU with all combinations of -machine and -device types, check for crashes and unexpected errors. """ -from __future__ import print_function import os import sys diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 9371e45813..4177261d33 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -12,7 +12,6 @@ Authors: This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. """ -from __future__ import print_function import ctypes import struct diff --git a/scripts/kvm/kvm_flightrecorder b/scripts/kvm/kvm_flightrecorder index 54a56745e4..78ca3af9c4 100755 --- a/scripts/kvm/kvm_flightrecorder +++ b/scripts/kvm/kvm_flightrecorder @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # KVM Flight Recorder - ring buffer tracing script # @@ -32,7 +32,6 @@ # consuming CPU cycles. No disk I/O is performed since the ring buffer holds a # fixed-size in-memory trace. -from __future__ import print_function import sys import os diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index 5dfeb2e03a..971ed0e721 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -10,7 +10,6 @@ # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. -from __future__ import print_function MSR_IA32_VMX_BASIC = 0x480 MSR_IA32_VMX_PINBASED_CTLS = 0x481 MSR_IA32_VMX_PROCBASED_CTLS = 0x482 diff --git a/scripts/minikconf.py b/scripts/minikconf.py index febd9a479f..90b99517c1 100644..100755 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # # Mini-Kconfig parser # @@ -10,7 +11,6 @@ # or, at your option, any later version. See the COPYING file in # the top-level directory. -from __future__ import print_function import os import sys import re diff --git a/scripts/modules/module_block.py b/scripts/modules/module_block.py index 08646af92c..f23191fac1 100644 --- a/scripts/modules/module_block.py +++ b/scripts/modules/module_block.py @@ -10,7 +10,6 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from __future__ import print_function import sys import os diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py index f93f3c7c23..4b03f7d53b 100755 --- a/scripts/qapi-gen.py +++ b/scripts/qapi-gen.py @@ -1,10 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # QAPI generator # # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. -from __future__ import print_function import argparse import re diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 6f1c17f71f..1787a53d91 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -4,7 +4,6 @@ # See the COPYING file in the top-level directory. """This script produces the documentation of a qapi schema in texinfo format""" -from __future__ import print_function import re from qapi.gen import QAPIGenDoc, QAPISchemaVisitor diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index 30cf8a9a0d..ce122984a9 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # QEMU Guest Agent Client # @@ -36,7 +36,6 @@ # See also: https://wiki.qemu.org/Features/QAPI/GuestAgent # -from __future__ import print_function import os import sys import base64 diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 6cb46fdae2..0625fc2aba 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # QMP command line tool # @@ -10,7 +10,6 @@ # This work is licensed under the terms of the GNU GPLv2 or later. # See the COPYING file in the top-level directory. -from __future__ import print_function import sys, os from qmp import QEMUMonitorProtocol diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index f1cddeafbc..a01d31de1e 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Low-level QEMU shell on top of QMP. # @@ -65,7 +65,6 @@ # which will echo back the properly formatted JSON-compliant QMP that is being # sent to QEMU, which is useful for debugging and documentation generation. -from __future__ import print_function import json import ast import readline diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index 4d85970a78..6bada2c33d 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 ## # QEMU Object Model test tools # diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index ec5275d53a..007b4cd442 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -11,7 +11,6 @@ # the COPYING file in the top-level directory. ## -from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index 0f97440973..03bda3446b 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -11,7 +11,6 @@ # the COPYING file in the top-level directory. ## -from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index 26ed9e3263..c37fe78b00 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -11,7 +11,6 @@ # the COPYING file in the top-level directory. ## -from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index 31603c681f..1c8acf61e7 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -13,7 +13,6 @@ # the COPYING file in the top-level directory. ## -from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py index 656f0388ad..409b4321f2 100755 --- a/scripts/render_block_graph.py +++ b/scripts/render_block_graph.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Render Qemu Block Graph # diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index ee7fda2638..4cbc1e47c6 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Dump the contents of a recorded execution stream @@ -18,7 +18,6 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see <http://www.gnu.org/licenses/>. -from __future__ import print_function import argparse import struct from collections import namedtuple diff --git a/scripts/signrom.py b/scripts/signrom.py index 313ee28a17..43693dba56 100644..100755 --- a/scripts/signrom.py +++ b/scripts/signrom.py @@ -1,4 +1,5 @@ -from __future__ import print_function +#!/usr/bin/env python3 + # # Option ROM signing utility # @@ -44,14 +45,8 @@ fout.write(data) checksum = 0 for b in data: - # catch Python 2 vs. 3 differences - if isinstance(b, int): - checksum += b - else: - checksum += ord(b) -checksum = (256 - checksum) % 256 - -# Python 3 no longer allows chr(checksum) + checksum = (checksum - b) & 255 + fout.write(struct.pack('B', checksum)) fin.close() diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 45485b864b..20f0026066 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Pretty-printer for simple trace backend binary trace files # @@ -9,7 +9,6 @@ # # For help see docs/devel/tracing.txt -from __future__ import print_function import struct import inspect from tracetool import read_events, Event diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 3beaa66bd8..264cc9eecc 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 44c118bc2a..13d29f1e42 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py index 259c6a6531..54cab2c4de 100644 --- a/scripts/tracetool/backend/__init__.py +++ b/scripts/tracetool/backend/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py index c2f3a4e5a8..638990db79 100644 --- a/scripts/tracetool/backend/dtrace.py +++ b/scripts/tracetool/backend/dtrace.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index 92f71b28f9..e9844dd335 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 33c95af8e9..23b274c0fd 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index c2fd1c24c4..b650c262b5 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 668fb73fee..1373a90192 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py index 280cb7c106..a772a3b53b 100644 --- a/scripts/tracetool/backend/ust.py +++ b/scripts/tracetool/backend/ust.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py index cf6e0e2da5..aba2f7a441 100644 --- a/scripts/tracetool/format/__init__.py +++ b/scripts/tracetool/format/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index 31207961b0..78af8aff72 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index c7cb2a93a6..d3980b914b 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index 5596b304e6..83e1a2f355 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index 9ab0cf2cce..b486beb672 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index 57b04061cf..4f4633b4e6 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py index e8ef3e762d..8fc808f2ef 100644 --- a/scripts/tracetool/format/stap.py +++ b/scripts/tracetool/format/stap.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/tcg_h.py b/scripts/tracetool/format/tcg_h.py index 1651cc3f71..0180e3d76c 100644 --- a/scripts/tracetool/format/tcg_h.py +++ b/scripts/tracetool/format/tcg_h.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py index 1b3522a716..6527b69afd 100644 --- a/scripts/tracetool/format/tcg_helper_c.py +++ b/scripts/tracetool/format/tcg_helper_c.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/tcg_helper_h.py b/scripts/tracetool/format/tcg_helper_h.py index 6b184b641b..98ebe52f18 100644 --- a/scripts/tracetool/format/tcg_helper_h.py +++ b/scripts/tracetool/format/tcg_helper_h.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/tcg_helper_wrapper_h.py b/scripts/tracetool/format/tcg_helper_wrapper_h.py index ff53447512..6adeab74df 100644 --- a/scripts/tracetool/format/tcg_helper_wrapper_h.py +++ b/scripts/tracetool/format/tcg_helper_wrapper_h.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py index 264784cdf2..deced9533d 100644 --- a/scripts/tracetool/format/ust_events_c.py +++ b/scripts/tracetool/format/ust_events_c.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py index b14054ac01..6ce559f6cc 100644 --- a/scripts/tracetool/format/ust_events_h.py +++ b/scripts/tracetool/format/ust_events_h.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/transform.py b/scripts/tracetool/transform.py index 2ca9286046..8fd4dcf20d 100644 --- a/scripts/tracetool/transform.py +++ b/scripts/tracetool/transform.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/tracetool/vcpu.py b/scripts/tracetool/vcpu.py index 452c7f589d..0b104e4f15 100644 --- a/scripts/tracetool/vcpu.py +++ b/scripts/tracetool/vcpu.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index f8b7b8f772..539ead62b4 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Compares vmstate information stored in JSON format, obtained from # the -dump-vmstate QEMU command. @@ -19,7 +19,6 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, see <http://www.gnu.org/licenses/>. -from __future__ import print_function import argparse import json import sys diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 6e6948e960..18ac562346 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -29,6 +29,6 @@ # define TARGET_PAGE_BITS_MIN 10 #endif -#define NB_MMU_MODES 8 +#define NB_MMU_MODES 9 #endif diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 7f5b244bde..3a9d31ea9d 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -76,6 +76,7 @@ void arm_gt_ptimer_cb(void *opaque); void arm_gt_vtimer_cb(void *opaque); void arm_gt_htimer_cb(void *opaque); void arm_gt_stimer_cb(void *opaque); +void arm_gt_hvtimer_cb(void *opaque); #define ARM_AFF0_SHIFT 0 #define ARM_AFF0_MASK (0xFFULL << ARM_AFF0_SHIFT) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f86e71a260..b0762a76c4 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -410,58 +410,173 @@ static void arm_cpu_reset(CPUState *s) arm_rebuild_hflags(env); } +static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, + unsigned int target_el, + unsigned int cur_el, bool secure, + uint64_t hcr_el2) +{ + CPUARMState *env = cs->env_ptr; + bool pstate_unmasked; + bool unmasked = false; + + /* + * Don't take exceptions if they target a lower EL. + * This check should catch any exceptions that would not be taken + * but left pending. + */ + if (cur_el > target_el) { + return false; + } + + switch (excp_idx) { + case EXCP_FIQ: + pstate_unmasked = !(env->daif & PSTATE_F); + break; + + case EXCP_IRQ: + pstate_unmasked = !(env->daif & PSTATE_I); + break; + + case EXCP_VFIQ: + if (secure || !(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFIQs are only taken when hypervized and non-secure. */ + return false; + } + return !(env->daif & PSTATE_F); + case EXCP_VIRQ: + if (secure || !(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VIRQs are only taken when hypervized and non-secure. */ + return false; + } + return !(env->daif & PSTATE_I); + default: + g_assert_not_reached(); + } + + /* + * Use the target EL, current execution state and SCR/HCR settings to + * determine whether the corresponding CPSR bit is used to mask the + * interrupt. + */ + if ((target_el > cur_el) && (target_el != 1)) { + /* Exceptions targeting a higher EL may not be maskable */ + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + /* + * 64-bit masking rules are simple: exceptions to EL3 + * can't be masked, and exceptions to EL2 can only be + * masked from Secure state. The HCR and SCR settings + * don't affect the masking logic, only the interrupt routing. + */ + if (target_el == 3 || !secure) { + unmasked = true; + } + } else { + /* + * The old 32-bit-only environment has a more complicated + * masking setup. HCR and SCR bits not only affect interrupt + * routing but also change the behaviour of masking. + */ + bool hcr, scr; + + switch (excp_idx) { + case EXCP_FIQ: + /* + * If FIQs are routed to EL3 or EL2 then there are cases where + * we override the CPSR.F in determining if the exception is + * masked or not. If neither of these are set then we fall back + * to the CPSR.F setting otherwise we further assess the state + * below. + */ + hcr = hcr_el2 & HCR_FMO; + scr = (env->cp15.scr_el3 & SCR_FIQ); + + /* + * When EL3 is 32-bit, the SCR.FW bit controls whether the + * CPSR.F bit masks FIQ interrupts when taken in non-secure + * state. If SCR.FW is set then FIQs can be masked by CPSR.F + * when non-secure but only when FIQs are only routed to EL3. + */ + scr = scr && !((env->cp15.scr_el3 & SCR_FW) && !hcr); + break; + case EXCP_IRQ: + /* + * When EL3 execution state is 32-bit, if HCR.IMO is set then + * we may override the CPSR.I masking when in non-secure state. + * The SCR.IRQ setting has already been taken into consideration + * when setting the target EL, so it does not have a further + * affect here. + */ + hcr = hcr_el2 & HCR_IMO; + scr = false; + break; + default: + g_assert_not_reached(); + } + + if ((scr || hcr) && !secure) { + unmasked = true; + } + } + } + + /* + * The PSTATE bits only mask the interrupt if we have not overriden the + * ability above. + */ + return unmasked || pstate_unmasked; +} + bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); CPUARMState *env = cs->env_ptr; uint32_t cur_el = arm_current_el(env); bool secure = arm_is_secure(env); + uint64_t hcr_el2 = arm_hcr_el2_eff(env); uint32_t target_el; uint32_t excp_idx; - bool ret = false; + + /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ if (interrupt_request & CPU_INTERRUPT_FIQ) { excp_idx = EXCP_FIQ; target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el)) { - cs->exception_index = excp_idx; - env->exception.target_el = target_el; - cc->do_interrupt(cs); - ret = true; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; } } if (interrupt_request & CPU_INTERRUPT_HARD) { excp_idx = EXCP_IRQ; target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el)) { - cs->exception_index = excp_idx; - env->exception.target_el = target_el; - cc->do_interrupt(cs); - ret = true; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; } } if (interrupt_request & CPU_INTERRUPT_VIRQ) { excp_idx = EXCP_VIRQ; target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el)) { - cs->exception_index = excp_idx; - env->exception.target_el = target_el; - cc->do_interrupt(cs); - ret = true; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; } } if (interrupt_request & CPU_INTERRUPT_VFIQ) { excp_idx = EXCP_VFIQ; target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el)) { - cs->exception_index = excp_idx; - env->exception.target_el = target_el; - cc->do_interrupt(cs); - ret = true; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; } } + return false; - return ret; + found: + cs->exception_index = excp_idx; + env->exception.target_el = target_el; + cc->do_interrupt(cs); + return true; } #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) @@ -1272,7 +1387,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } } - { uint64_t scale; @@ -1295,6 +1409,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) arm_gt_htimer_cb, cpu); cpu->gt_timer[GTIMER_SEC] = timer_new(QEMU_CLOCK_VIRTUAL, scale, arm_gt_stimer_cb, cpu); + cpu->gt_timer[GTIMER_HYPVIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_hvtimer_cb, cpu); } #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 608fcbd0b7..0b3036c484 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -144,11 +144,12 @@ typedef struct ARMGenericTimer { uint64_t ctl; /* Timer Control register */ } ARMGenericTimer; -#define GTIMER_PHYS 0 -#define GTIMER_VIRT 1 -#define GTIMER_HYP 2 -#define GTIMER_SEC 3 -#define NUM_GTIMERS 4 +#define GTIMER_PHYS 0 +#define GTIMER_VIRT 1 +#define GTIMER_HYP 2 +#define GTIMER_SEC 3 +#define GTIMER_HYPVIRT 4 +#define NUM_GTIMERS 5 typedef struct { uint64_t raw_tcr; @@ -1424,13 +1425,6 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define HCR_ATA (1ULL << 56) #define HCR_DCT (1ULL << 57) -/* - * When we actually implement ARMv8.1-VHE we should add HCR_E2H to - * HCR_MASK and then clear it again if the feature bit is not set in - * hcr_write(). - */ -#define HCR_MASK ((1ULL << 34) - 1) - #define SCR_NS (1U << 0) #define SCR_IRQ (1U << 1) #define SCR_FIQ (1U << 2) @@ -2582,6 +2576,19 @@ struct ARMCPRegInfo { * fieldoffset is 0 then no reset will be done. */ CPResetFn *resetfn; + + /* + * "Original" writefn and readfn. + * For ARMv8.1-VHE register aliases, we overwrite the read/write + * accessor functions of various EL1/EL0 to perform the runtime + * check for which sysreg should actually be modified, and then + * forwards the operation. Before overwriting the accessors, + * the original function is copied here, so that accesses that + * really do go to the EL1/EL0 version proceed normally. + * (The corresponding EL2 register is linked via opaque.) + */ + CPReadFn *orig_readfn; + CPWriteFn *orig_writefn; }; /* Macros which are lvalues for the field in CPUARMState for the @@ -2702,117 +2709,6 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); #define ARM_CPUID_TI915T 0x54029152 #define ARM_CPUID_TI925T 0x54029252 -static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, - unsigned int target_el) -{ - CPUARMState *env = cs->env_ptr; - unsigned int cur_el = arm_current_el(env); - bool secure = arm_is_secure(env); - bool pstate_unmasked; - int8_t unmasked = 0; - uint64_t hcr_el2; - - /* Don't take exceptions if they target a lower EL. - * This check should catch any exceptions that would not be taken but left - * pending. - */ - if (cur_el > target_el) { - return false; - } - - hcr_el2 = arm_hcr_el2_eff(env); - - switch (excp_idx) { - case EXCP_FIQ: - pstate_unmasked = !(env->daif & PSTATE_F); - break; - - case EXCP_IRQ: - pstate_unmasked = !(env->daif & PSTATE_I); - break; - - case EXCP_VFIQ: - if (secure || !(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { - /* VFIQs are only taken when hypervized and non-secure. */ - return false; - } - return !(env->daif & PSTATE_F); - case EXCP_VIRQ: - if (secure || !(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { - /* VIRQs are only taken when hypervized and non-secure. */ - return false; - } - return !(env->daif & PSTATE_I); - default: - g_assert_not_reached(); - } - - /* Use the target EL, current execution state and SCR/HCR settings to - * determine whether the corresponding CPSR bit is used to mask the - * interrupt. - */ - if ((target_el > cur_el) && (target_el != 1)) { - /* Exceptions targeting a higher EL may not be maskable */ - if (arm_feature(env, ARM_FEATURE_AARCH64)) { - /* 64-bit masking rules are simple: exceptions to EL3 - * can't be masked, and exceptions to EL2 can only be - * masked from Secure state. The HCR and SCR settings - * don't affect the masking logic, only the interrupt routing. - */ - if (target_el == 3 || !secure) { - unmasked = 1; - } - } else { - /* The old 32-bit-only environment has a more complicated - * masking setup. HCR and SCR bits not only affect interrupt - * routing but also change the behaviour of masking. - */ - bool hcr, scr; - - switch (excp_idx) { - case EXCP_FIQ: - /* If FIQs are routed to EL3 or EL2 then there are cases where - * we override the CPSR.F in determining if the exception is - * masked or not. If neither of these are set then we fall back - * to the CPSR.F setting otherwise we further assess the state - * below. - */ - hcr = hcr_el2 & HCR_FMO; - scr = (env->cp15.scr_el3 & SCR_FIQ); - - /* When EL3 is 32-bit, the SCR.FW bit controls whether the - * CPSR.F bit masks FIQ interrupts when taken in non-secure - * state. If SCR.FW is set then FIQs can be masked by CPSR.F - * when non-secure but only when FIQs are only routed to EL3. - */ - scr = scr && !((env->cp15.scr_el3 & SCR_FW) && !hcr); - break; - case EXCP_IRQ: - /* When EL3 execution state is 32-bit, if HCR.IMO is set then - * we may override the CPSR.I masking when in non-secure state. - * The SCR.IRQ setting has already been taken into consideration - * when setting the target EL, so it does not have a further - * affect here. - */ - hcr = hcr_el2 & HCR_IMO; - scr = false; - break; - default: - g_assert_not_reached(); - } - - if ((scr || hcr) && !secure) { - unmasked = 1; - } - } - } - - /* The PSTATE bits only mask the interrupt if we have not overriden the - * ability above. - */ - return unmasked || pstate_unmasked; -} - #define ARM_CPU_TYPE_SUFFIX "-" TYPE_ARM_CPU #define ARM_CPU_TYPE_NAME(name) (name ARM_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_ARM_CPU @@ -2826,18 +2722,21 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * + NonSecure EL1 & 0 stage 1 * + NonSecure EL1 & 0 stage 2 * + NonSecure EL2 - * + Secure EL1 & EL0 + * + NonSecure EL2 & 0 (ARMv8.1-VHE) + * + Secure EL1 & 0 * + Secure EL3 * If EL3 is 32-bit: * + NonSecure PL1 & 0 stage 1 * + NonSecure PL1 & 0 stage 2 * + NonSecure PL2 - * + Secure PL0 & PL1 + * + Secure PL0 + * + Secure PL1 * (reminder: for 32 bit EL3, Secure PL1 is *EL3*, not EL1.) * * For QEMU, an mmu_idx is not quite the same as a translation regime because: - * 1. we need to split the "EL1 & 0" regimes into two mmu_idxes, because they - * may differ in access permissions even if the VA->PA map is the same + * 1. we need to split the "EL1 & 0" and "EL2 & 0" regimes into two mmu_idxes, + * because they may differ in access permissions even if the VA->PA map is + * the same * 2. we want to cache in our TLB the full VA->IPA->PA lookup for a stage 1+2 * translation, which means that we have one mmu_idx that deals with two * concatenated translation regimes [this sort of combined s1+2 TLB is @@ -2849,19 +2748,23 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * 4. we can also safely fold together the "32 bit EL3" and "64 bit EL3" * translation regimes, because they map reasonably well to each other * and they can't both be active at the same time. - * This gives us the following list of mmu_idx values: + * 5. we want to be able to use the TLB for accesses done as part of a + * stage1 page table walk, rather than having to walk the stage2 page + * table over and over. + * + * This gives us the following list of cases: * - * NS EL0 (aka NS PL0) stage 1+2 - * NS EL1 (aka NS PL1) stage 1+2 + * NS EL0 EL1&0 stage 1+2 (aka NS PL0) + * NS EL1 EL1&0 stage 1+2 (aka NS PL1) + * NS EL0 EL2&0 + * NS EL2 EL2&0 * NS EL2 (aka NS PL2) + * S EL0 EL1&0 (aka S PL0) + * S EL1 EL1&0 (not used if EL3 is 32 bit) * S EL3 (aka S PL1) - * S EL0 (aka S PL0) - * S EL1 (not used if EL3 is 32 bit) - * NS EL0+1 stage 2 + * NS EL1&0 stage 2 * - * (The last of these is an mmu_idx because we want to be able to use the TLB - * for the accesses done as part of a stage 1 page table walk, rather than - * having to walk the stage 2 page table over and over.) + * for a total of 9 different mmu_idx. * * R profile CPUs have an MPU, but can use the same set of MMU indexes * as A profile. They only need to distinguish NS EL0 and NS EL1 (and @@ -2899,106 +2802,88 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * For M profile we arrange them to have a bit for priv, a bit for negpri * and a bit for secure. */ -#define ARM_MMU_IDX_A 0x10 /* A profile */ -#define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */ -#define ARM_MMU_IDX_M 0x40 /* M profile */ +#define ARM_MMU_IDX_A 0x10 /* A profile */ +#define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */ +#define ARM_MMU_IDX_M 0x40 /* M profile */ -/* meanings of the bits for M profile mmu idx values */ -#define ARM_MMU_IDX_M_PRIV 0x1 +/* Meanings of the bits for M profile mmu idx values */ +#define ARM_MMU_IDX_M_PRIV 0x1 #define ARM_MMU_IDX_M_NEGPRI 0x2 -#define ARM_MMU_IDX_M_S 0x4 +#define ARM_MMU_IDX_M_S 0x4 /* Secure */ -#define ARM_MMU_IDX_TYPE_MASK (~0x7) -#define ARM_MMU_IDX_COREIDX_MASK 0x7 +#define ARM_MMU_IDX_TYPE_MASK \ + (ARM_MMU_IDX_A | ARM_MMU_IDX_M | ARM_MMU_IDX_NOTLB) +#define ARM_MMU_IDX_COREIDX_MASK 0xf typedef enum ARMMMUIdx { - ARMMMUIdx_S12NSE0 = 0 | ARM_MMU_IDX_A, - ARMMMUIdx_S12NSE1 = 1 | ARM_MMU_IDX_A, - ARMMMUIdx_S1E2 = 2 | ARM_MMU_IDX_A, - ARMMMUIdx_S1E3 = 3 | ARM_MMU_IDX_A, - ARMMMUIdx_S1SE0 = 4 | ARM_MMU_IDX_A, - ARMMMUIdx_S1SE1 = 5 | ARM_MMU_IDX_A, - ARMMMUIdx_S2NS = 6 | ARM_MMU_IDX_A, - ARMMMUIdx_MUser = 0 | ARM_MMU_IDX_M, - ARMMMUIdx_MPriv = 1 | ARM_MMU_IDX_M, - ARMMMUIdx_MUserNegPri = 2 | ARM_MMU_IDX_M, - ARMMMUIdx_MPrivNegPri = 3 | ARM_MMU_IDX_M, - ARMMMUIdx_MSUser = 4 | ARM_MMU_IDX_M, - ARMMMUIdx_MSPriv = 5 | ARM_MMU_IDX_M, - ARMMMUIdx_MSUserNegPri = 6 | ARM_MMU_IDX_M, - ARMMMUIdx_MSPrivNegPri = 7 | ARM_MMU_IDX_M, - /* Indexes below here don't have TLBs and are used only for AT system - * instructions or for the first stage of an S12 page table walk. + /* + * A-profile. */ - ARMMMUIdx_S1NSE0 = 0 | ARM_MMU_IDX_NOTLB, - ARMMMUIdx_S1NSE1 = 1 | ARM_MMU_IDX_NOTLB, -} ARMMMUIdx; + ARMMMUIdx_E10_0 = 0 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_0 = 1 | ARM_MMU_IDX_A, -/* Bit macros for the core-mmu-index values for each index, - * for use when calling tlb_flush_by_mmuidx() and friends. - */ -typedef enum ARMMMUIdxBit { - ARMMMUIdxBit_S12NSE0 = 1 << 0, - ARMMMUIdxBit_S12NSE1 = 1 << 1, - ARMMMUIdxBit_S1E2 = 1 << 2, - ARMMMUIdxBit_S1E3 = 1 << 3, - ARMMMUIdxBit_S1SE0 = 1 << 4, - ARMMMUIdxBit_S1SE1 = 1 << 5, - ARMMMUIdxBit_S2NS = 1 << 6, - ARMMMUIdxBit_MUser = 1 << 0, - ARMMMUIdxBit_MPriv = 1 << 1, - ARMMMUIdxBit_MUserNegPri = 1 << 2, - ARMMMUIdxBit_MPrivNegPri = 1 << 3, - ARMMMUIdxBit_MSUser = 1 << 4, - ARMMMUIdxBit_MSPriv = 1 << 5, - ARMMMUIdxBit_MSUserNegPri = 1 << 6, - ARMMMUIdxBit_MSPrivNegPri = 1 << 7, -} ARMMMUIdxBit; + ARMMMUIdx_E10_1 = 2 | ARM_MMU_IDX_A, -#define MMU_USER_IDX 0 + ARMMMUIdx_E2 = 3 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2 = 4 | ARM_MMU_IDX_A, -static inline int arm_to_core_mmu_idx(ARMMMUIdx mmu_idx) -{ - return mmu_idx & ARM_MMU_IDX_COREIDX_MASK; -} + ARMMMUIdx_SE10_0 = 5 | ARM_MMU_IDX_A, + ARMMMUIdx_SE10_1 = 6 | ARM_MMU_IDX_A, + ARMMMUIdx_SE3 = 7 | ARM_MMU_IDX_A, -static inline ARMMMUIdx core_to_arm_mmu_idx(CPUARMState *env, int mmu_idx) -{ - if (arm_feature(env, ARM_FEATURE_M)) { - return mmu_idx | ARM_MMU_IDX_M; - } else { - return mmu_idx | ARM_MMU_IDX_A; - } -} + ARMMMUIdx_Stage2 = 8 | ARM_MMU_IDX_A, -/* Return the exception level we're running at if this is our mmu_idx */ -static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) -{ - switch (mmu_idx & ARM_MMU_IDX_TYPE_MASK) { - case ARM_MMU_IDX_A: - return mmu_idx & 3; - case ARM_MMU_IDX_M: - return mmu_idx & ARM_MMU_IDX_M_PRIV; - default: - g_assert_not_reached(); - } -} + /* + * These are not allocated TLBs and are used only for AT system + * instructions or for the first stage of an S12 page table walk. + */ + ARMMMUIdx_Stage1_E0 = 0 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E1 = 1 | ARM_MMU_IDX_NOTLB, + + /* + * M-profile. + */ + ARMMMUIdx_MUser = ARM_MMU_IDX_M, + ARMMMUIdx_MPriv = ARM_MMU_IDX_M | ARM_MMU_IDX_M_PRIV, + ARMMMUIdx_MUserNegPri = ARMMMUIdx_MUser | ARM_MMU_IDX_M_NEGPRI, + ARMMMUIdx_MPrivNegPri = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_NEGPRI, + ARMMMUIdx_MSUser = ARMMMUIdx_MUser | ARM_MMU_IDX_M_S, + ARMMMUIdx_MSPriv = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_S, + ARMMMUIdx_MSUserNegPri = ARMMMUIdx_MUserNegPri | ARM_MMU_IDX_M_S, + ARMMMUIdx_MSPrivNegPri = ARMMMUIdx_MPrivNegPri | ARM_MMU_IDX_M_S, +} ARMMMUIdx; /* - * Return the MMU index for a v7M CPU with all relevant information - * manually specified. + * Bit macros for the core-mmu-index values for each index, + * for use when calling tlb_flush_by_mmuidx() and friends. */ -ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env, - bool secstate, bool priv, bool negpri); +#define TO_CORE_BIT(NAME) \ + ARMMMUIdxBit_##NAME = 1 << (ARMMMUIdx_##NAME & ARM_MMU_IDX_COREIDX_MASK) -/* Return the MMU index for a v7M CPU in the specified security and - * privilege state. - */ -ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, - bool secstate, bool priv); +typedef enum ARMMMUIdxBit { + TO_CORE_BIT(E10_0), + TO_CORE_BIT(E20_0), + TO_CORE_BIT(E10_1), + TO_CORE_BIT(E2), + TO_CORE_BIT(E20_2), + TO_CORE_BIT(SE10_0), + TO_CORE_BIT(SE10_1), + TO_CORE_BIT(SE3), + TO_CORE_BIT(Stage2), + + TO_CORE_BIT(MUser), + TO_CORE_BIT(MPriv), + TO_CORE_BIT(MUserNegPri), + TO_CORE_BIT(MPrivNegPri), + TO_CORE_BIT(MSUser), + TO_CORE_BIT(MSPriv), + TO_CORE_BIT(MSUserNegPri), + TO_CORE_BIT(MSPrivNegPri), +} ARMMMUIdxBit; -/* Return the MMU index for a v7M CPU in the specified security state */ -ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate); +#undef TO_CORE_BIT + +#define MMU_USER_IDX 0 /** * cpu_mmu_index: @@ -3159,15 +3044,7 @@ static inline bool arm_sctlr_b(CPUARMState *env) (env->cp15.sctlr_el[1] & SCTLR_B) != 0; } -static inline uint64_t arm_sctlr(CPUARMState *env, int el) -{ - if (el == 0) { - /* FIXME: ARMv8.1-VHE S2 translation regime. */ - return env->cp15.sctlr_el[1]; - } else { - return env->cp15.sctlr_el[el]; - } -} +uint64_t arm_sctlr(CPUARMState *env, int el); static inline bool arm_cpu_data_is_big_endian_a32(CPUARMState *env, bool sctlr_b) @@ -3221,55 +3098,73 @@ typedef ARMCPU ArchCPU; * We put flags which are shared between 32 and 64 bit mode at the top * of the word, and flags which apply to only one mode at the bottom. * + * 31 20 18 14 9 0 + * +--------------+-----+-----+----------+--------------+ + * | | | TBFLAG_A32 | | + * | | +-----+----------+ TBFLAG_AM32 | + * | TBFLAG_ANY | |TBFLAG_M32| | + * | | +-+----------+--------------| + * | | | TBFLAG_A64 | + * +--------------+---------+---------------------------+ + * 31 20 15 0 + * * Unless otherwise noted, these bits are cached in env->hflags. */ FIELD(TBFLAG_ANY, AARCH64_STATE, 31, 1) -FIELD(TBFLAG_ANY, MMUIDX, 28, 3) -FIELD(TBFLAG_ANY, SS_ACTIVE, 27, 1) -FIELD(TBFLAG_ANY, PSTATE_SS, 26, 1) /* Not cached. */ +FIELD(TBFLAG_ANY, SS_ACTIVE, 30, 1) +FIELD(TBFLAG_ANY, PSTATE_SS, 29, 1) /* Not cached. */ +FIELD(TBFLAG_ANY, BE_DATA, 28, 1) +FIELD(TBFLAG_ANY, MMUIDX, 24, 4) /* Target EL if we take a floating-point-disabled exception */ -FIELD(TBFLAG_ANY, FPEXC_EL, 24, 2) -FIELD(TBFLAG_ANY, BE_DATA, 23, 1) +FIELD(TBFLAG_ANY, FPEXC_EL, 22, 2) +/* For A-profile only, target EL for debug exceptions. */ +FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 20, 2) + /* - * For A-profile only, target EL for debug exceptions. - * Note that this overlaps with the M-profile-only HANDLER and STACKCHECK bits. + * Bit usage when in AArch32 state, both A- and M-profile. */ -FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 21, 2) +FIELD(TBFLAG_AM32, CONDEXEC, 0, 8) /* Not cached. */ +FIELD(TBFLAG_AM32, THUMB, 8, 1) /* Not cached. */ -/* Bit usage when in AArch32 state: */ -FIELD(TBFLAG_A32, THUMB, 0, 1) /* Not cached. */ -FIELD(TBFLAG_A32, VECLEN, 1, 3) /* Not cached. */ -FIELD(TBFLAG_A32, VECSTRIDE, 4, 2) /* Not cached. */ +/* + * Bit usage when in AArch32 state, for A-profile only. + */ +FIELD(TBFLAG_A32, VECLEN, 9, 3) /* Not cached. */ +FIELD(TBFLAG_A32, VECSTRIDE, 12, 2) /* Not cached. */ /* * We store the bottom two bits of the CPAR as TB flags and handle * checks on the other bits at runtime. This shares the same bits as * VECSTRIDE, which is OK as no XScale CPU has VFP. * Not cached, because VECLEN+VECSTRIDE are not cached. */ -FIELD(TBFLAG_A32, XSCALE_CPAR, 4, 2) +FIELD(TBFLAG_A32, XSCALE_CPAR, 12, 2) +FIELD(TBFLAG_A32, VFPEN, 14, 1) /* Partially cached, minus FPEXC. */ +FIELD(TBFLAG_A32, SCTLR_B, 15, 1) +FIELD(TBFLAG_A32, HSTR_ACTIVE, 16, 1) /* * Indicates whether cp register reads and writes by guest code should access * the secure or nonsecure bank of banked registers; note that this is not * the same thing as the current security state of the processor! */ -FIELD(TBFLAG_A32, NS, 6, 1) -FIELD(TBFLAG_A32, VFPEN, 7, 1) /* Partially cached, minus FPEXC. */ -FIELD(TBFLAG_A32, CONDEXEC, 8, 8) /* Not cached. */ -FIELD(TBFLAG_A32, SCTLR_B, 16, 1) -FIELD(TBFLAG_A32, HSTR_ACTIVE, 17, 1) - -/* For M profile only, set if FPCCR.LSPACT is set */ -FIELD(TBFLAG_A32, LSPACT, 18, 1) /* Not cached. */ -/* For M profile only, set if we must create a new FP context */ -FIELD(TBFLAG_A32, NEW_FP_CTXT_NEEDED, 19, 1) /* Not cached. */ -/* For M profile only, set if FPCCR.S does not match current security state */ -FIELD(TBFLAG_A32, FPCCR_S_WRONG, 20, 1) /* Not cached. */ -/* For M profile only, Handler (ie not Thread) mode */ -FIELD(TBFLAG_A32, HANDLER, 21, 1) -/* For M profile only, whether we should generate stack-limit checks */ -FIELD(TBFLAG_A32, STACKCHECK, 22, 1) - -/* Bit usage when in AArch64 state */ +FIELD(TBFLAG_A32, NS, 17, 1) + +/* + * Bit usage when in AArch32 state, for M-profile only. + */ +/* Handler (ie not Thread) mode */ +FIELD(TBFLAG_M32, HANDLER, 9, 1) +/* Whether we should generate stack-limit checks */ +FIELD(TBFLAG_M32, STACKCHECK, 10, 1) +/* Set if FPCCR.LSPACT is set */ +FIELD(TBFLAG_M32, LSPACT, 11, 1) /* Not cached. */ +/* Set if we must create a new FP context */ +FIELD(TBFLAG_M32, NEW_FP_CTXT_NEEDED, 12, 1) /* Not cached. */ +/* Set if FPCCR.S does not match current security state */ +FIELD(TBFLAG_M32, FPCCR_S_WRONG, 13, 1) /* Not cached. */ + +/* + * Bit usage when in AArch64 state + */ FIELD(TBFLAG_A64, TBII, 0, 2) FIELD(TBFLAG_A64, SVEEXC_EL, 2, 2) FIELD(TBFLAG_A64, ZCR_LEN, 4, 4) @@ -3277,6 +3172,7 @@ FIELD(TBFLAG_A64, PAUTH_ACTIVE, 8, 1) FIELD(TBFLAG_A64, BT, 9, 1) FIELD(TBFLAG_A64, BTYPE, 10, 2) /* Not cached. */ FIELD(TBFLAG_A64, TBID, 12, 2) +FIELD(TBFLAG_A64, UNPRIV, 14, 1) static inline bool bswap_code(bool sctlr_b) { @@ -3685,6 +3581,11 @@ static inline bool isar_feature_aa64_sve(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SVE) != 0; } +static inline bool isar_feature_aa64_vh(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0; +} + static inline bool isar_feature_aa64_lor(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, LO) != 0; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 2d97bf45e1..c80fb5fd43 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -672,6 +672,7 @@ static void aarch64_max_initfn(Object *obj) t = cpu->isar.id_aa64mmfr1; t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 1); /* HPD */ t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); + t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); cpu->isar.id_aa64mmfr1 = t; /* Replicate the same data to the 32-bit id registers. */ diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index dde80273ff..2e3e90c6a5 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -20,6 +20,7 @@ static bool linked_bp_matches(ARMCPU *cpu, int lbn) int ctx_cmps = extract32(cpu->dbgdidr, 20, 4); int bt; uint32_t contextidr; + uint64_t hcr_el2; /* * Links to unimplemented or non-context aware breakpoints are @@ -40,24 +41,44 @@ static bool linked_bp_matches(ARMCPU *cpu, int lbn) } bt = extract64(bcr, 20, 4); - - /* - * We match the whole register even if this is AArch32 using the - * short descriptor format (in which case it holds both PROCID and ASID), - * since we don't implement the optional v7 context ID masking. - */ - contextidr = extract64(env->cp15.contextidr_el[1], 0, 32); + hcr_el2 = arm_hcr_el2_eff(env); switch (bt) { case 3: /* linked context ID match */ - if (arm_current_el(env) > 1) { - /* Context matches never fire in EL2 or (AArch64) EL3 */ + switch (arm_current_el(env)) { + default: + /* Context matches never fire in AArch64 EL3 */ return false; + case 2: + if (!(hcr_el2 & HCR_E2H)) { + /* Context matches never fire in EL2 without E2H enabled. */ + return false; + } + contextidr = env->cp15.contextidr_el[2]; + break; + case 1: + contextidr = env->cp15.contextidr_el[1]; + break; + case 0: + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + contextidr = env->cp15.contextidr_el[2]; + } else { + contextidr = env->cp15.contextidr_el[1]; + } + break; } - return (contextidr == extract64(env->cp15.dbgbvr[lbn], 0, 32)); - case 5: /* linked address mismatch (reserved in AArch64) */ + break; + + case 7: /* linked contextidr_el1 match */ + contextidr = env->cp15.contextidr_el[1]; + break; + case 13: /* linked contextidr_el2 match */ + contextidr = env->cp15.contextidr_el[2]; + break; + case 9: /* linked VMID match (reserved if no EL2) */ case 11: /* linked context ID and VMID match (reserved if no EL2) */ + case 15: /* linked full context ID match */ default: /* * Links to Unlinked context breakpoints must generate no @@ -66,7 +87,12 @@ static bool linked_bp_matches(ARMCPU *cpu, int lbn) return false; } - return false; + /* + * We match the whole register even if this is AArch32 using the + * short descriptor format (in which case it holds both PROCID and ASID), + * since we don't implement the optional v7 context ID masking. + */ + return contextidr == (uint32_t)env->cp15.dbgbvr[lbn]; } static bool bp_wp_matches(ARMCPU *cpu, int n, bool is_wp) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 36aa6badfd..bf45f8a785 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -70,7 +70,7 @@ static void daif_check(CPUARMState *env, uint32_t op, uint32_t imm, uintptr_t ra) { /* DAIF update to PSTATE. This is OK from EL0 only if UMA is set. */ - if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) { + if (arm_current_el(env) == 0 && !(arm_sctlr(env, 0) & SCTLR_UMA)) { raise_exception_ra(env, EXCP_UDEF, syn_aa64_sysregtrap(0, extract32(op, 0, 3), extract32(op, 3, 3), 4, diff --git a/target/arm/helper.c b/target/arm/helper.c index 19a57a17da..7d15d5c933 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -614,56 +614,54 @@ static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* Invalidate all (TLBIALL) */ - ARMCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); if (tlb_force_broadcast(env)) { - tlbiall_is_write(env, NULL, value); - return; + tlb_flush_all_cpus_synced(cs); + } else { + tlb_flush(cs); } - - tlb_flush(CPU(cpu)); } static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ - ARMCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + value &= TARGET_PAGE_MASK; if (tlb_force_broadcast(env)) { - tlbimva_is_write(env, NULL, value); - return; + tlb_flush_page_all_cpus_synced(cs, value); + } else { + tlb_flush_page(cs, value); } - - tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); } static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* Invalidate by ASID (TLBIASID) */ - ARMCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); if (tlb_force_broadcast(env)) { - tlbiasid_is_write(env, NULL, value); - return; + tlb_flush_all_cpus_synced(cs); + } else { + tlb_flush(cs); } - - tlb_flush(CPU(cpu)); } static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ - ARMCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + value &= TARGET_PAGE_MASK; if (tlb_force_broadcast(env)) { - tlbimvaa_is_write(env, NULL, value); - return; + tlb_flush_page_all_cpus_synced(cs, value); + } else { + tlb_flush_page(cs, value); } - - tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); } static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -672,9 +670,9 @@ static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = env_cpu(env); tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0 | - ARMMMUIdxBit_S2NS); + ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2); } static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -683,9 +681,9 @@ static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = env_cpu(env); tlb_flush_by_mmuidx_all_cpus_synced(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0 | - ARMMMUIdxBit_S2NS); + ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2); } static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -706,7 +704,7 @@ static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 40); - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S2NS); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); } static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -722,7 +720,7 @@ static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 40); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_S2NS); + ARMMMUIdxBit_Stage2); } static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -730,7 +728,7 @@ static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_S1E2); + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2); } static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -738,7 +736,7 @@ static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_S1E2); + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); } static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -747,7 +745,7 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = env_cpu(env); uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S1E2); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); } static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -757,7 +755,7 @@ static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_S1E2); + ARMMMUIdxBit_E2); } static const ARMCPRegInfo cp_reginfo[] = { @@ -2326,10 +2324,18 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, * Writable only at the highest implemented exception level. */ int el = arm_current_el(env); + uint64_t hcr; + uint32_t cntkctl; switch (el) { case 0: - if (!extract32(env->cp15.c14_cntkctl, 0, 2)) { + hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + cntkctl = env->cp15.cnthctl_el2; + } else { + cntkctl = env->cp15.c14_cntkctl; + } + if (!extract32(cntkctl, 0, 2)) { return CP_ACCESS_TRAP; } break; @@ -2357,17 +2363,47 @@ static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx, { unsigned int cur_el = arm_current_el(env); bool secure = arm_is_secure(env); + uint64_t hcr = arm_hcr_el2_eff(env); - /* CNT[PV]CT: not visible from PL0 if ELO[PV]CTEN is zero */ - if (cur_el == 0 && - !extract32(env->cp15.c14_cntkctl, timeridx, 1)) { - return CP_ACCESS_TRAP; - } + switch (cur_el) { + case 0: + /* If HCR_EL2.<E2H,TGE> == '11': check CNTHCTL_EL2.EL0[PV]CTEN. */ + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + return (extract32(env->cp15.cnthctl_el2, timeridx, 1) + ? CP_ACCESS_OK : CP_ACCESS_TRAP_EL2); + } - if (arm_feature(env, ARM_FEATURE_EL2) && - timeridx == GTIMER_PHYS && !secure && cur_el < 2 && - !extract32(env->cp15.cnthctl_el2, 0, 1)) { - return CP_ACCESS_TRAP_EL2; + /* CNT[PV]CT: not visible from PL0 if EL0[PV]CTEN is zero */ + if (!extract32(env->cp15.c14_cntkctl, timeridx, 1)) { + return CP_ACCESS_TRAP; + } + + /* If HCR_EL2.<E2H,TGE> == '10': check CNTHCTL_EL2.EL1PCTEN. */ + if (hcr & HCR_E2H) { + if (timeridx == GTIMER_PHYS && + !extract32(env->cp15.cnthctl_el2, 10, 1)) { + return CP_ACCESS_TRAP_EL2; + } + } else { + /* If HCR_EL2.<E2H> == 0: check CNTHCTL_EL2.EL1PCEN. */ + if (arm_feature(env, ARM_FEATURE_EL2) && + timeridx == GTIMER_PHYS && !secure && + !extract32(env->cp15.cnthctl_el2, 1, 1)) { + return CP_ACCESS_TRAP_EL2; + } + } + break; + + case 1: + /* Check CNTHCTL_EL2.EL1PCTEN, which changes location based on E2H. */ + if (arm_feature(env, ARM_FEATURE_EL2) && + timeridx == GTIMER_PHYS && !secure && + (hcr & HCR_E2H + ? !extract32(env->cp15.cnthctl_el2, 10, 1) + : !extract32(env->cp15.cnthctl_el2, 0, 1))) { + return CP_ACCESS_TRAP_EL2; + } + break; } return CP_ACCESS_OK; } @@ -2377,19 +2413,41 @@ static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx, { unsigned int cur_el = arm_current_el(env); bool secure = arm_is_secure(env); + uint64_t hcr = arm_hcr_el2_eff(env); - /* CNT[PV]_CVAL, CNT[PV]_CTL, CNT[PV]_TVAL: not visible from PL0 if - * EL0[PV]TEN is zero. - */ - if (cur_el == 0 && - !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { - return CP_ACCESS_TRAP; - } + switch (cur_el) { + case 0: + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + /* If HCR_EL2.<E2H,TGE> == '11': check CNTHCTL_EL2.EL0[PV]TEN. */ + return (extract32(env->cp15.cnthctl_el2, 9 - timeridx, 1) + ? CP_ACCESS_OK : CP_ACCESS_TRAP_EL2); + } - if (arm_feature(env, ARM_FEATURE_EL2) && - timeridx == GTIMER_PHYS && !secure && cur_el < 2 && - !extract32(env->cp15.cnthctl_el2, 1, 1)) { - return CP_ACCESS_TRAP_EL2; + /* + * CNT[PV]_CVAL, CNT[PV]_CTL, CNT[PV]_TVAL: not visible from + * EL0 if EL0[PV]TEN is zero. + */ + if (!extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { + return CP_ACCESS_TRAP; + } + /* fall through */ + + case 1: + if (arm_feature(env, ARM_FEATURE_EL2) && + timeridx == GTIMER_PHYS && !secure) { + if (hcr & HCR_E2H) { + /* If HCR_EL2.<E2H,TGE> == '10': check CNTHCTL_EL2.EL1PTEN. */ + if (!extract32(env->cp15.cnthctl_el2, 11, 1)) { + return CP_ACCESS_TRAP_EL2; + } + } else { + /* If HCR_EL2.<E2H> == 0: check CNTHCTL_EL2.EL1PCEN. */ + if (!extract32(env->cp15.cnthctl_el2, 1, 1)) { + return CP_ACCESS_TRAP_EL2; + } + } + } + break; } return CP_ACCESS_OK; } @@ -2515,9 +2573,31 @@ static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) return gt_get_countervalue(env); } +static uint64_t gt_virt_cnt_offset(CPUARMState *env) +{ + uint64_t hcr; + + switch (arm_current_el(env)) { + case 2: + hcr = arm_hcr_el2_eff(env); + if (hcr & HCR_E2H) { + return 0; + } + break; + case 0: + hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + return 0; + } + break; + } + + return env->cp15.cntvoff_el2; +} + static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_get_countervalue(env) - env->cp15.cntvoff_el2; + return gt_get_countervalue(env) - gt_virt_cnt_offset(env); } static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2532,7 +2612,14 @@ static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx) { - uint64_t offset = timeridx == GTIMER_VIRT ? env->cp15.cntvoff_el2 : 0; + uint64_t offset = 0; + + switch (timeridx) { + case GTIMER_VIRT: + case GTIMER_HYPVIRT: + offset = gt_virt_cnt_offset(env); + break; + } return (uint32_t)(env->cp15.c14_timer[timeridx].cval - (gt_get_countervalue(env) - offset)); @@ -2542,7 +2629,14 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx, uint64_t value) { - uint64_t offset = timeridx == GTIMER_VIRT ? env->cp15.cntvoff_el2 : 0; + uint64_t offset = 0; + + switch (timeridx) { + case GTIMER_VIRT: + case GTIMER_HYPVIRT: + offset = gt_virt_cnt_offset(env); + break; + } trace_arm_gt_tval_write(timeridx, value); env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + @@ -2601,6 +2695,70 @@ static void gt_phys_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_ctl_write(env, ri, GTIMER_PHYS, value); } +static int gt_phys_redir_timeridx(CPUARMState *env) +{ + switch (arm_mmu_idx(env)) { + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + return GTIMER_HYP; + default: + return GTIMER_PHYS; + } +} + +static int gt_virt_redir_timeridx(CPUARMState *env) +{ + switch (arm_mmu_idx(env)) { + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + return GTIMER_HYPVIRT; + default: + return GTIMER_VIRT; + } +} + +static uint64_t gt_phys_redir_cval_read(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + int timeridx = gt_phys_redir_timeridx(env); + return env->cp15.c14_timer[timeridx].cval; +} + +static void gt_phys_redir_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = gt_phys_redir_timeridx(env); + gt_cval_write(env, ri, timeridx, value); +} + +static uint64_t gt_phys_redir_tval_read(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + int timeridx = gt_phys_redir_timeridx(env); + return gt_tval_read(env, ri, timeridx); +} + +static void gt_phys_redir_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = gt_phys_redir_timeridx(env); + gt_tval_write(env, ri, timeridx, value); +} + +static uint64_t gt_phys_redir_ctl_read(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + int timeridx = gt_phys_redir_timeridx(env); + return env->cp15.c14_timer[timeridx].ctl; +} + +static void gt_phys_redir_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = gt_phys_redir_timeridx(env); + gt_ctl_write(env, ri, timeridx, value); +} + static void gt_virt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) { gt_timer_reset(env, ri, GTIMER_VIRT); @@ -2639,6 +2797,48 @@ static void gt_cntvoff_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_recalc_timer(cpu, GTIMER_VIRT); } +static uint64_t gt_virt_redir_cval_read(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + int timeridx = gt_virt_redir_timeridx(env); + return env->cp15.c14_timer[timeridx].cval; +} + +static void gt_virt_redir_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = gt_virt_redir_timeridx(env); + gt_cval_write(env, ri, timeridx, value); +} + +static uint64_t gt_virt_redir_tval_read(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + int timeridx = gt_virt_redir_timeridx(env); + return gt_tval_read(env, ri, timeridx); +} + +static void gt_virt_redir_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = gt_virt_redir_timeridx(env); + gt_tval_write(env, ri, timeridx, value); +} + +static uint64_t gt_virt_redir_ctl_read(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + int timeridx = gt_virt_redir_timeridx(env); + return env->cp15.c14_timer[timeridx].ctl; +} + +static void gt_virt_redir_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int timeridx = gt_virt_redir_timeridx(env); + gt_ctl_write(env, ri, timeridx, value); +} + static void gt_hyp_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) { gt_timer_reset(env, ri, GTIMER_HYP); @@ -2695,6 +2895,34 @@ static void gt_sec_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_ctl_write(env, ri, GTIMER_SEC, value); } +static void gt_hv_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_HYPVIRT); +} + +static void gt_hv_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_HYPVIRT, value); +} + +static uint64_t gt_hv_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_HYPVIRT); +} + +static void gt_hv_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_HYPVIRT, value); +} + +static void gt_hv_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_HYPVIRT, value); +} + void arm_gt_ptimer_cb(void *opaque) { ARMCPU *cpu = opaque; @@ -2723,6 +2951,13 @@ void arm_gt_stimer_cb(void *opaque) gt_recalc_timer(cpu, GTIMER_SEC); } +void arm_gt_hvtimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_HYPVIRT); +} + static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) { ARMCPU *cpu = env_archcpu(env); @@ -2760,7 +2995,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), - .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, + .readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read, + .writefn = gt_phys_redir_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTP_CTL_S", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, @@ -2777,14 +3013,16 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .resetvalue = 0, - .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, + .readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read, + .writefn = gt_phys_redir_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL0_RW, .accessfn = gt_vtimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), - .writefn = gt_virt_ctl_write, .raw_writefn = raw_write, + .readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read, + .writefn = gt_virt_redir_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, @@ -2792,14 +3030,15 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_vtimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .resetvalue = 0, - .writefn = gt_virt_ctl_write, .raw_writefn = raw_write, + .readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read, + .writefn = gt_virt_redir_ctl_write, .raw_writefn = raw_write, }, /* TimerValue views: a 32 bit downcounting view of the underlying state */ { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, .secure = ARM_CP_SECSTATE_NS, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .accessfn = gt_ptimer_access, - .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, + .readfn = gt_phys_redir_tval_read, .writefn = gt_phys_redir_tval_write, }, { .name = "CNTP_TVAL_S", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, @@ -2812,18 +3051,18 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .accessfn = gt_ptimer_access, .resetfn = gt_phys_timer_reset, - .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, + .readfn = gt_phys_redir_tval_read, .writefn = gt_phys_redir_tval_write, }, { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .accessfn = gt_vtimer_access, - .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write, + .readfn = gt_virt_redir_tval_read, .writefn = gt_virt_redir_tval_write, }, { .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .accessfn = gt_vtimer_access, .resetfn = gt_virt_timer_reset, - .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write, + .readfn = gt_virt_redir_tval_read, .writefn = gt_virt_redir_tval_write, }, /* The counter itself */ { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, @@ -2853,7 +3092,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .accessfn = gt_ptimer_access, - .writefn = gt_phys_cval_write, .raw_writefn = raw_write, + .readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read, + .writefn = gt_phys_redir_cval_write, .raw_writefn = raw_write, }, { .name = "CNTP_CVAL_S", .cp = 15, .crm = 14, .opc1 = 2, .secure = ARM_CP_SECSTATE_S, @@ -2869,14 +3109,16 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .resetvalue = 0, .accessfn = gt_ptimer_access, - .writefn = gt_phys_cval_write, .raw_writefn = raw_write, + .readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read, + .writefn = gt_phys_redir_cval_write, .raw_writefn = raw_write, }, { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, .access = PL0_RW, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .accessfn = gt_vtimer_access, - .writefn = gt_virt_cval_write, .raw_writefn = raw_write, + .readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read, + .writefn = gt_virt_redir_cval_write, .raw_writefn = raw_write, }, { .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, @@ -2884,7 +3126,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .resetvalue = 0, .accessfn = gt_vtimer_access, - .writefn = gt_virt_cval_write, .raw_writefn = raw_write, + .readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read, + .writefn = gt_virt_redir_cval_write, .raw_writefn = raw_write, }, /* Secure timer -- this is actually restricted to only EL3 * and configurably Secure-EL1 via the accessfn. @@ -2915,6 +3158,15 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { REGINFO_SENTINEL }; +static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { + return CP_ACCESS_TRAP; + } + return CP_ACCESS_OK; +} + #else /* In user-mode most of the generic timer registers are inaccessible @@ -3009,7 +3261,8 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, bool take_exc = false; if (fi.s1ptw && current_el == 1 && !arm_is_secure(env) - && (mmu_idx == ARMMMUIdx_S1NSE1 || mmu_idx == ARMMMUIdx_S1NSE0)) { + && (mmu_idx == ARMMMUIdx_Stage1_E1 || + mmu_idx == ARMMMUIdx_Stage1_E0)) { /* * Synchronous stage 2 fault on an access made as part of the * translation table walk for AT S1E0* or AT S1E1* insn @@ -3085,7 +3338,7 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, format64 = arm_s1_regime_using_lpae_format(env, mmu_idx); if (arm_feature(env, ARM_FEATURE_EL2)) { - if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { + if (mmu_idx == ARMMMUIdx_E10_0 || mmu_idx == ARMMMUIdx_E10_1) { format64 |= env->cp15.hcr_el2 & (HCR_VM | HCR_DC); } else { format64 |= arm_current_el(env) == 2; @@ -3154,13 +3407,13 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) /* stage 1 current state PL1: ATS1CPR, ATS1CPW */ switch (el) { case 3: - mmu_idx = ARMMMUIdx_S1E3; + mmu_idx = ARMMMUIdx_SE3; break; case 2: - mmu_idx = ARMMMUIdx_S1NSE1; + mmu_idx = ARMMMUIdx_Stage1_E1; break; case 1: - mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; + mmu_idx = secure ? ARMMMUIdx_SE10_1 : ARMMMUIdx_Stage1_E1; break; default: g_assert_not_reached(); @@ -3170,13 +3423,13 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) /* stage 1 current state PL0: ATS1CUR, ATS1CUW */ switch (el) { case 3: - mmu_idx = ARMMMUIdx_S1SE0; + mmu_idx = ARMMMUIdx_SE10_0; break; case 2: - mmu_idx = ARMMMUIdx_S1NSE0; + mmu_idx = ARMMMUIdx_Stage1_E0; break; case 1: - mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S1NSE0; + mmu_idx = secure ? ARMMMUIdx_SE10_0 : ARMMMUIdx_Stage1_E0; break; default: g_assert_not_reached(); @@ -3184,11 +3437,11 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) break; case 4: /* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */ - mmu_idx = ARMMMUIdx_S12NSE1; + mmu_idx = ARMMMUIdx_E10_1; break; case 6: /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ - mmu_idx = ARMMMUIdx_S12NSE0; + mmu_idx = ARMMMUIdx_E10_0; break; default: g_assert_not_reached(); @@ -3205,7 +3458,7 @@ static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; uint64_t par64; - par64 = do_ats_write(env, value, access_type, ARMMMUIdx_S1E2); + par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2); A32_BANKED_CURRENT_REG_SET(env, par, par64); } @@ -3230,26 +3483,26 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, case 0: switch (ri->opc1) { case 0: /* AT S1E1R, AT S1E1W */ - mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; + mmu_idx = secure ? ARMMMUIdx_SE10_1 : ARMMMUIdx_Stage1_E1; break; case 4: /* AT S1E2R, AT S1E2W */ - mmu_idx = ARMMMUIdx_S1E2; + mmu_idx = ARMMMUIdx_E2; break; case 6: /* AT S1E3R, AT S1E3W */ - mmu_idx = ARMMMUIdx_S1E3; + mmu_idx = ARMMMUIdx_SE3; break; default: g_assert_not_reached(); } break; case 2: /* AT S1E0R, AT S1E0W */ - mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S1NSE0; + mmu_idx = secure ? ARMMMUIdx_SE10_0 : ARMMMUIdx_Stage1_E0; break; case 4: /* AT S12E1R, AT S12E1W */ - mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S12NSE1; + mmu_idx = secure ? ARMMMUIdx_SE10_1 : ARMMMUIdx_E10_1; break; case 6: /* AT S12E0R, AT S12E0W */ - mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S12NSE0; + mmu_idx = secure ? ARMMMUIdx_SE10_0 : ARMMMUIdx_E10_0; break; default: g_assert_not_reached(); @@ -3510,7 +3763,7 @@ static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) tcr->base_mask = 0xffffc000u; } -static void vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, +static void vmsa_tcr_el12_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); @@ -3533,18 +3786,38 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } +static void vmsa_tcr_ttbr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * If we are running with E2&0 regime, then an ASID is active. + * Flush if that might be changing. Note we're not checking + * TCR_EL2.A1 to know if this is really the TTBRx_EL2 that + * holds the active ASID, only checking the field that might. + */ + if (extract64(raw_read(env, ri) ^ value, 48, 16) && + (arm_hcr_el2_eff(env) & HCR_E2H)) { + tlb_flush_by_mmuidx(env_cpu(env), + ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_0); + } + raw_write(env, ri, value); +} + static void vttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); CPUState *cs = CPU(cpu); - /* Accesses to VTTBR may change the VMID so we must flush the TLB. */ + /* + * A change in VMID to the stage2 page table (Stage2) invalidates + * the combined stage 1&2 tlbs (EL10_1 and EL10_0). + */ if (raw_read(env, ri) != value) { tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0 | - ARMMMUIdxBit_S2NS); + ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2); raw_write(env, ri, value); } } @@ -3586,7 +3859,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { 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, .writefn = vmsa_tcr_el1_write, + .access = PL1_RW, .writefn = vmsa_tcr_el12_write, .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) }, { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, @@ -3870,7 +4143,7 @@ static void aa64_fpsr_write(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) { + if (arm_current_el(env) == 0 && !(arm_sctlr(env, 0) & SCTLR_UMA)) { return CP_ACCESS_TRAP; } return CP_ACCESS_OK; @@ -3889,7 +4162,7 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env, /* Cache invalidate/clean: NOP, but EL0 must UNDEF unless * SCTLR_EL1.UCI is set. */ - if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UCI)) { + if (arm_current_el(env) == 0 && !(arm_sctlr(env, 0) & SCTLR_UCI)) { return CP_ACCESS_TRAP; } return CP_ACCESS_OK; @@ -3899,79 +4172,79 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env, * Page D4-1736 (DDI0487A.b) */ +static int vae1_tlbmask(CPUARMState *env) +{ + /* Since we exclude secure first, we may read HCR_EL2 directly. */ + if (arm_is_secure_below_el3(env)) { + return ARMMMUIdxBit_SE10_1 | ARMMMUIdxBit_SE10_0; + } else if ((env->cp15.hcr_el2 & (HCR_E2H | HCR_TGE)) + == (HCR_E2H | HCR_TGE)) { + return ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_0; + } else { + return ARMMMUIdxBit_E10_1 | ARMMMUIdxBit_E10_0; + } +} + static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { CPUState *cs = env_cpu(env); - bool sec = arm_is_secure_below_el3(env); + int mask = vae1_tlbmask(env); - if (sec) { - tlb_flush_by_mmuidx_all_cpus_synced(cs, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); - } else { - tlb_flush_by_mmuidx_all_cpus_synced(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); - } + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); if (tlb_force_broadcast(env)) { - tlbi_aa64_vmalle1is_write(env, NULL, value); - return; - } - - if (arm_is_secure_below_el3(env)) { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } else { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); + tlb_flush_by_mmuidx(cs, mask); } } -static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static int alle1_tlbmask(CPUARMState *env) { - /* Note that the 'ALL' scope must invalidate both stage 1 and + /* + * Note that the 'ALL' scope must invalidate both stage 1 and * stage 2 translations, whereas most other scopes only invalidate * stage 1 translations. */ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); - if (arm_is_secure_below_el3(env)) { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); + return ARMMMUIdxBit_SE10_1 | ARMMMUIdxBit_SE10_0; + } else if (arm_feature(env, ARM_FEATURE_EL2)) { + return ARMMMUIdxBit_E10_1 | ARMMMUIdxBit_E10_0 | ARMMMUIdxBit_Stage2; } else { - if (arm_feature(env, ARM_FEATURE_EL2)) { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0 | - ARMMMUIdxBit_S2NS); - } else { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); - } + return ARMMMUIdxBit_E10_1 | ARMMMUIdxBit_E10_0; } } +static int e2_tlbmask(CPUARMState *env) +{ + /* TODO: ARMv8.4-SecEL2 */ + return ARMMMUIdxBit_E20_0 | ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E2; +} + +static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = alle1_tlbmask(env); + + tlb_flush_by_mmuidx(cs, mask); +} + static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + CPUState *cs = env_cpu(env); + int mask = e2_tlbmask(env); - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_S1E2); + tlb_flush_by_mmuidx(cs, mask); } static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3980,42 +4253,25 @@ static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = env_archcpu(env); CPUState *cs = CPU(cpu); - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_S1E3); + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_SE3); } static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* Note that the 'ALL' scope must invalidate both stage 1 and - * stage 2 translations, whereas most other scopes only invalidate - * stage 1 translations. - */ CPUState *cs = env_cpu(env); - bool sec = arm_is_secure_below_el3(env); - bool has_el2 = arm_feature(env, ARM_FEATURE_EL2); - - if (sec) { - tlb_flush_by_mmuidx_all_cpus_synced(cs, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); - } else if (has_el2) { - tlb_flush_by_mmuidx_all_cpus_synced(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0 | - ARMMMUIdxBit_S2NS); - } else { - tlb_flush_by_mmuidx_all_cpus_synced(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); - } + int mask = alle1_tlbmask(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { CPUState *cs = env_cpu(env); + int mask = e2_tlbmask(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_S1E2); + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); } static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4023,7 +4279,7 @@ static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_S1E3); + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_SE3); } static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4033,11 +4289,11 @@ static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, * Currently handles both VAE2 and VALE2, since we don't support * flush-last-level-only. */ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + CPUState *cs = env_cpu(env); + int mask = e2_tlbmask(env); uint64_t pageaddr = sextract64(value << 12, 0, 56); - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S1E2); + tlb_flush_page_by_mmuidx(cs, pageaddr, mask); } static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4051,26 +4307,17 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = CPU(cpu); uint64_t pageaddr = sextract64(value << 12, 0, 56); - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S1E3); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_SE3); } static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); - bool sec = arm_is_secure_below_el3(env); + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); uint64_t pageaddr = sextract64(value << 12, 0, 56); - if (sec) { - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); - } else { - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); - } + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); } static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4081,23 +4328,14 @@ static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, * since we don't support flush-for-specific-ASID-only or * flush-last-level-only. */ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); uint64_t pageaddr = sextract64(value << 12, 0, 56); if (tlb_force_broadcast(env)) { - tlbi_aa64_vae1is_write(env, NULL, value); - return; - } - - if (arm_is_secure_below_el3(env)) { - tlb_flush_page_by_mmuidx(cs, pageaddr, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); } else { - tlb_flush_page_by_mmuidx(cs, pageaddr, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); + tlb_flush_page_by_mmuidx(cs, pageaddr, mask); } } @@ -4108,7 +4346,7 @@ static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t pageaddr = sextract64(value << 12, 0, 56); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_S1E2); + ARMMMUIdxBit_E2); } static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4118,7 +4356,7 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t pageaddr = sextract64(value << 12, 0, 56); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_S1E3); + ARMMMUIdxBit_SE3); } static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4140,7 +4378,7 @@ static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 48); - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S2NS); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); } static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4156,17 +4394,33 @@ static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 48); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_S2NS); + ARMMMUIdxBit_Stage2); } static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - /* We don't implement EL2, so the only control on DC ZVA is the - * bit in the SCTLR which can prohibit access for EL0. - */ - if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_DZE)) { - return CP_ACCESS_TRAP; + int cur_el = arm_current_el(env); + + if (cur_el < 2) { + uint64_t hcr = arm_hcr_el2_eff(env); + + if (cur_el == 0) { + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + if (!(env->cp15.sctlr_el[2] & SCTLR_DZE)) { + return CP_ACCESS_TRAP_EL2; + } + } else { + if (!(env->cp15.sctlr_el[1] & SCTLR_DZE)) { + return CP_ACCESS_TRAP; + } + if (hcr & HCR_TDZ) { + return CP_ACCESS_TRAP_EL2; + } + } + } else if (hcr & HCR_TDZ) { + return CP_ACCESS_TRAP_EL2; + } } return CP_ACCESS_OK; } @@ -4721,7 +4975,8 @@ static const ARMCPRegInfo el3_no_el2_v8_cp_reginfo[] = { static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); - uint64_t valid_mask = HCR_MASK; + /* Begin with bits defined in base ARMv8.0. */ + uint64_t valid_mask = MAKE_64BIT_MASK(0, 34); if (arm_feature(env, ARM_FEATURE_EL3)) { valid_mask &= ~HCR_HCD; @@ -4735,6 +4990,9 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) */ valid_mask &= ~HCR_TSC; } + if (cpu_isar_feature(aa64_vh, cpu)) { + valid_mask |= HCR_E2H; + } if (cpu_isar_feature(aa64_lor, cpu)) { valid_mask |= HCR_TLOR; } @@ -4938,10 +5196,8 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .resetvalue = 0 }, { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, - .access = PL2_RW, - /* no .writefn needed as this can't cause an ASID change; - * no .raw_writefn or .resetfn needed as we never use mask/base_mask - */ + .access = PL2_RW, .writefn = vmsa_tcr_el12_write, + /* no .raw_writefn or .resetfn needed as we never use mask/base_mask */ .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) }, { .name = "VTCR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, @@ -4975,7 +5231,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) }, { .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0, - .access = PL2_RW, .resetvalue = 0, + .access = PL2_RW, .resetvalue = 0, .writefn = vmsa_tcr_ttbr_el2_write, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, @@ -5244,14 +5500,182 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { REGINFO_SENTINEL }; +#ifndef CONFIG_USER_ONLY +/* Test if system register redirection is to occur in the current state. */ +static bool redirect_for_e2h(CPUARMState *env) +{ + return arm_current_el(env) == 2 && (arm_hcr_el2_eff(env) & HCR_E2H); +} + +static uint64_t el2_e2h_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + CPReadFn *readfn; + + if (redirect_for_e2h(env)) { + /* Switch to the saved EL2 version of the register. */ + ri = ri->opaque; + readfn = ri->readfn; + } else { + readfn = ri->orig_readfn; + } + if (readfn == NULL) { + readfn = raw_read; + } + return readfn(env, ri); +} + +static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPWriteFn *writefn; + + if (redirect_for_e2h(env)) { + /* Switch to the saved EL2 version of the register. */ + ri = ri->opaque; + writefn = ri->writefn; + } else { + writefn = ri->orig_writefn; + } + if (writefn == NULL) { + writefn = raw_write; + } + writefn(env, ri, value); +} + +static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) +{ + struct E2HAlias { + uint32_t src_key, dst_key, new_key; + const char *src_name, *dst_name, *new_name; + bool (*feature)(const ARMISARegisters *id); + }; + +#define K(op0, op1, crn, crm, op2) \ + ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2) + + static const struct E2HAlias aliases[] = { + { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), + "SCTLR", "SCTLR_EL2", "SCTLR_EL12" }, + { K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2), + "CPACR", "CPTR_EL2", "CPACR_EL12" }, + { K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0), + "TTBR0_EL1", "TTBR0_EL2", "TTBR0_EL12" }, + { K(3, 0, 2, 0, 1), K(3, 4, 2, 0, 1), K(3, 5, 2, 0, 1), + "TTBR1_EL1", "TTBR1_EL2", "TTBR1_EL12" }, + { K(3, 0, 2, 0, 2), K(3, 4, 2, 0, 2), K(3, 5, 2, 0, 2), + "TCR_EL1", "TCR_EL2", "TCR_EL12" }, + { K(3, 0, 4, 0, 0), K(3, 4, 4, 0, 0), K(3, 5, 4, 0, 0), + "SPSR_EL1", "SPSR_EL2", "SPSR_EL12" }, + { K(3, 0, 4, 0, 1), K(3, 4, 4, 0, 1), K(3, 5, 4, 0, 1), + "ELR_EL1", "ELR_EL2", "ELR_EL12" }, + { K(3, 0, 5, 1, 0), K(3, 4, 5, 1, 0), K(3, 5, 5, 1, 0), + "AFSR0_EL1", "AFSR0_EL2", "AFSR0_EL12" }, + { K(3, 0, 5, 1, 1), K(3, 4, 5, 1, 1), K(3, 5, 5, 1, 1), + "AFSR1_EL1", "AFSR1_EL2", "AFSR1_EL12" }, + { K(3, 0, 5, 2, 0), K(3, 4, 5, 2, 0), K(3, 5, 5, 2, 0), + "ESR_EL1", "ESR_EL2", "ESR_EL12" }, + { K(3, 0, 6, 0, 0), K(3, 4, 6, 0, 0), K(3, 5, 6, 0, 0), + "FAR_EL1", "FAR_EL2", "FAR_EL12" }, + { K(3, 0, 10, 2, 0), K(3, 4, 10, 2, 0), K(3, 5, 10, 2, 0), + "MAIR_EL1", "MAIR_EL2", "MAIR_EL12" }, + { K(3, 0, 10, 3, 0), K(3, 4, 10, 3, 0), K(3, 5, 10, 3, 0), + "AMAIR0", "AMAIR_EL2", "AMAIR_EL12" }, + { K(3, 0, 12, 0, 0), K(3, 4, 12, 0, 0), K(3, 5, 12, 0, 0), + "VBAR", "VBAR_EL2", "VBAR_EL12" }, + { K(3, 0, 13, 0, 1), K(3, 4, 13, 0, 1), K(3, 5, 13, 0, 1), + "CONTEXTIDR_EL1", "CONTEXTIDR_EL2", "CONTEXTIDR_EL12" }, + { K(3, 0, 14, 1, 0), K(3, 4, 14, 1, 0), K(3, 5, 14, 1, 0), + "CNTKCTL", "CNTHCTL_EL2", "CNTKCTL_EL12" }, + + /* + * Note that redirection of ZCR is mentioned in the description + * of ZCR_EL2, and aliasing in the description of ZCR_EL1, but + * not in the summary table. + */ + { K(3, 0, 1, 2, 0), K(3, 4, 1, 2, 0), K(3, 5, 1, 2, 0), + "ZCR_EL1", "ZCR_EL2", "ZCR_EL12", isar_feature_aa64_sve }, + + /* TODO: ARMv8.2-SPE -- PMSCR_EL2 */ + /* TODO: ARMv8.4-Trace -- TRFCR_EL2 */ + }; +#undef K + + size_t i; + + for (i = 0; i < ARRAY_SIZE(aliases); i++) { + const struct E2HAlias *a = &aliases[i]; + ARMCPRegInfo *src_reg, *dst_reg; + + if (a->feature && !a->feature(&cpu->isar)) { + continue; + } + + src_reg = g_hash_table_lookup(cpu->cp_regs, &a->src_key); + dst_reg = g_hash_table_lookup(cpu->cp_regs, &a->dst_key); + g_assert(src_reg != NULL); + g_assert(dst_reg != NULL); + + /* Cross-compare names to detect typos in the keys. */ + g_assert(strcmp(src_reg->name, a->src_name) == 0); + g_assert(strcmp(dst_reg->name, a->dst_name) == 0); + + /* None of the core system registers use opaque; we will. */ + g_assert(src_reg->opaque == NULL); + + /* Create alias before redirection so we dup the right data. */ + if (a->new_key) { + ARMCPRegInfo *new_reg = g_memdup(src_reg, sizeof(ARMCPRegInfo)); + uint32_t *new_key = g_memdup(&a->new_key, sizeof(uint32_t)); + bool ok; + + new_reg->name = a->new_name; + new_reg->type |= ARM_CP_ALIAS; + /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ + new_reg->access &= PL2_RW | PL3_RW; + + ok = g_hash_table_insert(cpu->cp_regs, new_key, new_reg); + g_assert(ok); + } + + src_reg->opaque = dst_reg; + src_reg->orig_readfn = src_reg->readfn ?: raw_read; + src_reg->orig_writefn = src_reg->writefn ?: raw_write; + if (!src_reg->raw_readfn) { + src_reg->raw_readfn = raw_read; + } + if (!src_reg->raw_writefn) { + src_reg->raw_writefn = raw_write; + } + src_reg->readfn = el2_e2h_read; + src_reg->writefn = el2_e2h_write; + } +} +#endif + static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - /* Only accessible in EL0 if SCTLR.UCT is set (and only in AArch64, - * but the AArch32 CTR has its own reginfo struct) - */ - if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UCT)) { - return CP_ACCESS_TRAP; + int cur_el = arm_current_el(env); + + if (cur_el < 2) { + uint64_t hcr = arm_hcr_el2_eff(env); + + if (cur_el == 0) { + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + if (!(env->cp15.sctlr_el[2] & SCTLR_UCT)) { + return CP_ACCESS_TRAP_EL2; + } + } else { + if (!(env->cp15.sctlr_el[1] & SCTLR_UCT)) { + return CP_ACCESS_TRAP; + } + if (hcr & HCR_TID2) { + return CP_ACCESS_TRAP_EL2; + } + } + } else if (hcr & HCR_TID2) { + return CP_ACCESS_TRAP_EL2; + } } if (arm_current_el(env) < 2 && arm_hcr_el2_eff(env) & HCR_TID2) { @@ -5367,7 +5791,9 @@ static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { int sve_exception_el(CPUARMState *env, int el) { #ifndef CONFIG_USER_ONLY - if (el <= 1) { + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + + if (el <= 1 && (hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { bool disabled = false; /* The CPACR.ZEN controls traps to EL1: @@ -5382,8 +5808,7 @@ int sve_exception_el(CPUARMState *env, int el) } if (disabled) { /* route_to_el2 */ - return (arm_feature(env, ARM_FEATURE_EL2) - && (arm_hcr_el2_eff(env) & HCR_TGE) ? 2 : 1); + return hcr_el2 & HCR_TGE ? 2 : 1; } /* Check CPACR.FPEN. */ @@ -6122,6 +6547,71 @@ static const ARMCPRegInfo jazelle_regs[] = { REGINFO_SENTINEL }; +static const ARMCPRegInfo vhe_reginfo[] = { + { .name = "CONTEXTIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 1, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[2]) }, + { .name = "TTBR1_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 1, + .access = PL2_RW, .writefn = vmsa_tcr_ttbr_el2_write, + .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el[2]) }, +#ifndef CONFIG_USER_ONLY + { .name = "CNTHV_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 3, .opc2 = 2, + .fieldoffset = + offsetof(CPUARMState, cp15.c14_timer[GTIMER_HYPVIRT].cval), + .type = ARM_CP_IO, .access = PL2_RW, + .writefn = gt_hv_cval_write, .raw_writefn = raw_write }, + { .name = "CNTHV_TVAL_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 3, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .resetfn = gt_hv_timer_reset, + .readfn = gt_hv_tval_read, .writefn = gt_hv_tval_write }, + { .name = "CNTHV_CTL_EL2", .state = ARM_CP_STATE_BOTH, + .type = ARM_CP_IO, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 3, .opc2 = 1, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_HYPVIRT].ctl), + .writefn = gt_hv_ctl_write, .raw_writefn = raw_write }, + { .name = "CNTP_CTL_EL02", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_ALIAS, + .access = PL2_RW, .accessfn = e2h_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), + .writefn = gt_phys_ctl_write, .raw_writefn = raw_write }, + { .name = "CNTV_CTL_EL02", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_ALIAS, + .access = PL2_RW, .accessfn = e2h_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), + .writefn = gt_virt_ctl_write, .raw_writefn = raw_write }, + { .name = "CNTP_TVAL_EL02", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO | ARM_CP_ALIAS, + .access = PL2_RW, .accessfn = e2h_access, + .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write }, + { .name = "CNTV_TVAL_EL02", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO | ARM_CP_ALIAS, + .access = PL2_RW, .accessfn = e2h_access, + .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write }, + { .name = "CNTP_CVAL_EL02", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), + .access = PL2_RW, .accessfn = e2h_access, + .writefn = gt_phys_cval_write, .raw_writefn = raw_write }, + { .name = "CNTV_CVAL_EL02", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), + .access = PL2_RW, .accessfn = e2h_access, + .writefn = gt_virt_cval_write, .raw_writefn = raw_write }, +#endif + REGINFO_SENTINEL +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -7085,6 +7575,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, lor_reginfo); } + if (arm_feature(env, ARM_FEATURE_EL2) && cpu_isar_feature(aa64_vh, cpu)) { + define_arm_cp_regs(cpu, vhe_reginfo); + } + if (cpu_isar_feature(aa64_sve, cpu)) { define_one_arm_cp_reg(cpu, &zcr_el1_reginfo); if (arm_feature(env, ARM_FEATURE_EL2)) { @@ -7126,6 +7620,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) : cpu_isar_feature(aa32_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } + +#ifndef CONFIG_USER_ONLY + /* + * Register redirections and aliases must be done last, + * after the registers from the other extensions have been defined. + */ + if (arm_feature(env, ARM_FEATURE_EL2) && cpu_isar_feature(aa64_vh, cpu)) { + define_arm_vh_e2h_redirects_aliases(cpu); + } +#endif } void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) @@ -7421,13 +7925,10 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, mask = PL0_RW; break; case 4: + case 5: /* min_EL EL2 */ mask = PL2_RW; break; - case 5: - /* unallocated encoding, so not possible */ - assert(false); - break; case 6: /* min_EL EL3 */ mask = PL3_RW; @@ -7946,6 +8447,12 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, break; }; + /* + * For these purposes, TGE and AMO/IMO/FMO both force the + * interrupt to EL2. Fold TGE into the bit extracted above. + */ + hcr |= (hcr_el2 & HCR_TGE) != 0; + /* Perform a table-lookup for the target EL given the current state */ target_el = target_el_table[is64][scr][rw][hcr][secure][cur_el]; @@ -8510,14 +9017,19 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) * immediately lower than the target level is using AArch32 or AArch64 */ bool is_aa64; + uint64_t hcr; switch (new_el) { case 3: is_aa64 = (env->cp15.scr_el3 & SCR_RW) != 0; break; case 2: - is_aa64 = (env->cp15.hcr_el2 & HCR_RW) != 0; - break; + hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + is_aa64 = (hcr & HCR_RW) != 0; + break; + } + /* fall through */ case 1: is_aa64 = is_a64(env); break; @@ -8690,19 +9202,23 @@ void arm_cpu_do_interrupt(CPUState *cs) #endif /* !CONFIG_USER_ONLY */ /* Return the exception level which controls this address translation regime */ -static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) +static uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { - case ARMMMUIdx_S2NS: - case ARMMMUIdx_S1E2: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_Stage2: + case ARMMMUIdx_E2: return 2; - case ARMMMUIdx_S1E3: + case ARMMMUIdx_SE3: return 3; - case ARMMMUIdx_S1SE0: + case ARMMMUIdx_SE10_0: return arm_el_is_aa64(env, 3) ? 1 : 3; - case ARMMMUIdx_S1SE1: - case ARMMMUIdx_S1NSE0: - case ARMMMUIdx_S1NSE1: + case ARMMMUIdx_SE10_1: + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: case ARMMMUIdx_MPrivNegPri: case ARMMMUIdx_MUserNegPri: case ARMMMUIdx_MPriv: @@ -8717,14 +9233,24 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) } } -#ifndef CONFIG_USER_ONLY +uint64_t arm_sctlr(CPUARMState *env, int el) +{ + /* Only EL0 needs to be adjusted for EL1&0 or EL2&0. */ + if (el == 0) { + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 0); + el = (mmu_idx == ARMMMUIdx_E20_0 ? 2 : 1); + } + return env->cp15.sctlr_el[el]; +} /* Return the SCTLR value which controls this address translation regime */ -static inline uint32_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) +static inline uint64_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) { return env->cp15.sctlr_el[regime_el(env, mmu_idx)]; } +#ifndef CONFIG_USER_ONLY + /* Return true if the specified stage of address translation is disabled */ static inline bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx) @@ -8747,7 +9273,7 @@ static inline bool regime_translation_disabled(CPUARMState *env, } } - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { /* HCR.DC means HCR.VM behaves as 1 */ return (env->cp15.hcr_el2 & (HCR_DC | HCR_VM)) == 0; } @@ -8760,7 +9286,7 @@ static inline bool regime_translation_disabled(CPUARMState *env, } if ((env->cp15.hcr_el2 & HCR_DC) && - (mmu_idx == ARMMMUIdx_S1NSE0 || mmu_idx == ARMMMUIdx_S1NSE1)) { + (mmu_idx == ARMMMUIdx_Stage1_E0 || mmu_idx == ARMMMUIdx_Stage1_E1)) { /* HCR.DC means SCTLR_EL1.M behaves as 0 */ return true; } @@ -8778,7 +9304,7 @@ static inline bool regime_translation_big_endian(CPUARMState *env, static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, int ttbrn) { - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { return env->cp15.vttbr_el2; } if (ttbrn == 0) { @@ -8793,7 +9319,7 @@ static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, /* Return the TCR controlling this translation regime */ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) { - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { return &env->cp15.vtcr_el2; } return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; @@ -8804,10 +9330,14 @@ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) */ static inline ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx) { - if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { - mmu_idx += (ARMMMUIdx_S1NSE0 - ARMMMUIdx_S12NSE0); + switch (mmu_idx) { + case ARMMMUIdx_E10_0: + return ARMMMUIdx_Stage1_E0; + case ARMMMUIdx_E10_1: + return ARMMMUIdx_Stage1_E1; + default: + return mmu_idx; } - return mmu_idx; } /* Return true if the translation regime is using LPAE format page tables */ @@ -8839,8 +9369,9 @@ bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { - case ARMMMUIdx_S1SE0: - case ARMMMUIdx_S1NSE0: + case ARMMMUIdx_SE10_0: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_Stage1_E0: case ARMMMUIdx_MUser: case ARMMMUIdx_MSUser: case ARMMMUIdx_MUserNegPri: @@ -8848,8 +9379,8 @@ static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) return true; default: return false; - case ARMMMUIdx_S12NSE0: - case ARMMMUIdx_S12NSE1: + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: g_assert_not_reached(); } } @@ -8980,7 +9511,7 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, bool have_wxn; int wxn = 0; - assert(mmu_idx != ARMMMUIdx_S2NS); + assert(mmu_idx != ARMMMUIdx_Stage2); user_rw = simple_ap_to_rw_prot_is_user(ap, true); if (is_user) { @@ -9005,15 +9536,8 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, } if (is_aa64) { - switch (regime_el(env, mmu_idx)) { - case 1: - if (!is_user) { - xn = pxn || (user_rw & PAGE_WRITE); - } - break; - case 2: - case 3: - break; + if (regime_has_2_ranges(mmu_idx) && !is_user) { + xn = pxn || (user_rw & PAGE_WRITE); } } else if (arm_feature(env, ARM_FEATURE_V7)) { switch (regime_el(env, mmu_idx)) { @@ -9071,8 +9595,8 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, hwaddr addr, MemTxAttrs txattrs, ARMMMUFaultInfo *fi) { - if ((mmu_idx == ARMMMUIdx_S1NSE0 || mmu_idx == ARMMMUIdx_S1NSE1) && - !regime_translation_disabled(env, ARMMMUIdx_S2NS)) { + if ((mmu_idx == ARMMMUIdx_Stage1_E0 || mmu_idx == ARMMMUIdx_Stage1_E1) && + !regime_translation_disabled(env, ARMMMUIdx_Stage2)) { target_ulong s2size; hwaddr s2pa; int s2prot; @@ -9089,7 +9613,7 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, pcacheattrs = &cacheattrs; } - ret = get_phys_addr_lpae(env, addr, 0, ARMMMUIdx_S2NS, &s2pa, + ret = get_phys_addr_lpae(env, addr, 0, ARMMMUIdx_Stage2, &s2pa, &txattrs, &s2prot, &s2size, fi, pcacheattrs); if (ret) { assert(fi->type != ARMFault_None); @@ -9547,7 +10071,6 @@ ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va, ARMMMUIdx mmu_idx) { uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; - uint32_t el = regime_el(env, mmu_idx); bool tbi, tbid, epd, hpd, using16k, using64k; int select, tsz; @@ -9557,11 +10080,11 @@ ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va, */ select = extract64(va, 55, 1); - if (el > 1) { + if (!regime_has_2_ranges(mmu_idx)) { tsz = extract32(tcr, 0, 6); using64k = extract32(tcr, 14, 1); using16k = extract32(tcr, 15, 1); - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { /* VTCR_EL2 */ tbi = tbid = hpd = false; } else { @@ -9622,7 +10145,7 @@ static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, int select, tsz; bool epd, hpd; - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { /* VTCR */ bool sext = extract32(tcr, 4, 1); bool sign = extract32(tcr, 3, 1); @@ -9713,10 +10236,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, param = aa64_va_parameters(env, address, mmu_idx, access_type != MMU_INST_FETCH); level = 0; - /* If we are in 64-bit EL2 or EL3 then there is no TTBR1, so mark it - * invalid. - */ - ttbr1_valid = (el < 2); + ttbr1_valid = regime_has_2_ranges(mmu_idx); addrsize = 64 - 8 * param.tbi; inputsize = 64 - param.tsz; } else { @@ -9724,7 +10244,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, level = 1; /* There is no TTBR1 for EL2 */ ttbr1_valid = (el != 2); - addrsize = (mmu_idx == ARMMMUIdx_S2NS ? 40 : 32); + addrsize = (mmu_idx == ARMMMUIdx_Stage2 ? 40 : 32); inputsize = addrsize - param.tsz; } @@ -9775,7 +10295,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, goto do_fault; } - if (mmu_idx != ARMMMUIdx_S2NS) { + if (mmu_idx != ARMMMUIdx_Stage2) { /* The starting level depends on the virtual address size (which can * be up to 48 bits) and the translation granule size. It indicates * the number of strides (stride bits at a time) needed to @@ -9875,7 +10395,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, attrs = extract64(descriptor, 2, 10) | (extract64(descriptor, 52, 12) << 10); - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { /* Stage 2 table descriptors do not include any attribute fields */ break; } @@ -9906,7 +10426,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, ap = extract32(attrs, 4, 2); xn = extract32(attrs, 12, 1); - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { ns = true; *prot = get_S2prot(env, ap, xn); } else { @@ -9933,7 +10453,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, } if (cacheattrs != NULL) { - if (mmu_idx == ARMMMUIdx_S2NS) { + if (mmu_idx == ARMMMUIdx_Stage2) { cacheattrs->attrs = convert_stage2_attrs(env, extract32(attrs, 0, 4)); } else { @@ -9954,7 +10474,7 @@ do_fault: fi->type = fault_type; fi->level = level; /* Tag the error as S2 for failed S1 PTW at S2 or ordinary S2. */ - fi->stage2 = fi->s1ptw || (mmu_idx == ARMMMUIdx_S2NS); + fi->stage2 = fi->s1ptw || (mmu_idx == ARMMMUIdx_Stage2); return true; } @@ -10753,7 +11273,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, target_ulong *page_size, ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs) { - if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { + if (mmu_idx == ARMMMUIdx_E10_0 || mmu_idx == ARMMMUIdx_E10_1) { /* Call ourselves recursively to do the stage 1 and then stage 2 * translations. */ @@ -10768,13 +11288,13 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, prot, page_size, fi, cacheattrs); /* If S1 fails or S2 is disabled, return early. */ - if (ret || regime_translation_disabled(env, ARMMMUIdx_S2NS)) { + if (ret || regime_translation_disabled(env, ARMMMUIdx_Stage2)) { *phys_ptr = ipa; return ret; } /* S1 is done. Now do S2 translation. */ - ret = get_phys_addr_lpae(env, ipa, access_type, ARMMMUIdx_S2NS, + ret = get_phys_addr_lpae(env, ipa, access_type, ARMMMUIdx_Stage2, phys_ptr, attrs, &s2_prot, page_size, fi, cacheattrs != NULL ? &cacheattrs2 : NULL); @@ -10816,7 +11336,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, /* Fast Context Switch Extension. This doesn't exist at all in v8. * In v7 and earlier it affects all stage 1 translations. */ - if (address < 0x02000000 && mmu_idx != ARMMMUIdx_S2NS + if (address < 0x02000000 && mmu_idx != ARMMMUIdx_Stage2 && !arm_feature(env, ARM_FEATURE_V8)) { if (regime_el(env, mmu_idx) == 3) { address += env->cp15.fcseidr_s; @@ -11177,8 +11697,6 @@ uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) int fp_exception_el(CPUARMState *env, int cur_el) { #ifndef CONFIG_USER_ONLY - int fpen; - /* CPACR and the CPTR registers don't exist before v6, so FP is * always accessible */ @@ -11206,30 +11724,34 @@ int fp_exception_el(CPUARMState *env, int cur_el) * 0, 2 : trap EL0 and EL1/PL1 accesses * 1 : trap only EL0 accesses * 3 : trap no accesses + * This register is ignored if E2H+TGE are both set. */ - fpen = extract32(env->cp15.cpacr_el1, 20, 2); - switch (fpen) { - case 0: - case 2: - if (cur_el == 0 || cur_el == 1) { - /* Trap to PL1, which might be EL1 or EL3 */ - if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { + if ((arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + int fpen = extract32(env->cp15.cpacr_el1, 20, 2); + + switch (fpen) { + case 0: + case 2: + if (cur_el == 0 || cur_el == 1) { + /* Trap to PL1, which might be EL1 or EL3 */ + if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { + return 3; + } + return 1; + } + if (cur_el == 3 && !is_a64(env)) { + /* Secure PL1 running at EL3 */ return 3; } - return 1; - } - if (cur_el == 3 && !is_a64(env)) { - /* Secure PL1 running at EL3 */ - return 3; - } - break; - case 1: - if (cur_el == 0) { - return 1; + break; + case 1: + if (cur_el == 0) { + return 1; + } + break; + case 3: + break; } - break; - case 3: - break; } /* @@ -11265,6 +11787,31 @@ int fp_exception_el(CPUARMState *env, int cur_el) return 0; } +/* Return the exception level we're running at if this is our mmu_idx */ +int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) +{ + if (mmu_idx & ARM_MMU_IDX_M) { + return mmu_idx & ARM_MMU_IDX_M_PRIV; + } + + switch (mmu_idx) { + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_SE10_0: + return 0; + case ARMMMUIdx_E10_1: + case ARMMMUIdx_SE10_1: + return 1; + case ARMMMUIdx_E2: + case ARMMMUIdx_E20_2: + return 2; + case ARMMMUIdx_SE3: + return 3; + default: + g_assert_not_reached(); + } +} + #ifndef CONFIG_TCG ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) { @@ -11278,10 +11825,33 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) return arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure); } - if (el < 2 && arm_is_secure_below_el3(env)) { - return ARMMMUIdx_S1SE0 + el; - } else { - return ARMMMUIdx_S12NSE0 + el; + /* See ARM pseudo-function ELIsInHost. */ + switch (el) { + case 0: + if (arm_is_secure_below_el3(env)) { + return ARMMMUIdx_SE10_0; + } + if ((env->cp15.hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE) + && arm_el_is_aa64(env, 2)) { + return ARMMMUIdx_E20_0; + } + return ARMMMUIdx_E10_0; + case 1: + if (arm_is_secure_below_el3(env)) { + return ARMMMUIdx_SE10_1; + } + return ARMMMUIdx_E10_1; + case 2: + /* TODO: ARMv8.4-SecEL2 */ + /* Note that TGE does not apply at EL2. */ + if ((env->cp15.hcr_el2 & HCR_E2H) && arm_el_is_aa64(env, 2)) { + return ARMMMUIdx_E20_2; + } + return ARMMMUIdx_E2; + case 3: + return ARMMMUIdx_SE3; + default: + g_assert_not_reached(); } } @@ -11336,11 +11906,8 @@ static uint32_t rebuild_hflags_m32(CPUARMState *env, int fp_el, { uint32_t flags = 0; - /* v8M always enables the fpu. */ - flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1); - if (arm_v7m_is_handler_mode(env)) { - flags = FIELD_DP32(flags, TBFLAG_A32, HANDLER, 1); + flags = FIELD_DP32(flags, TBFLAG_M32, HANDLER, 1); } /* @@ -11351,7 +11918,7 @@ static uint32_t rebuild_hflags_m32(CPUARMState *env, int fp_el, if (arm_feature(env, ARM_FEATURE_V8) && !((mmu_idx & ARM_MMU_IDX_M_NEGPRI) && (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKOFHFNMIGN_MASK))) { - flags = FIELD_DP32(flags, TBFLAG_A32, STACKCHECK, 1); + flags = FIELD_DP32(flags, TBFLAG_M32, STACKCHECK, 1); } return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); @@ -11394,8 +11961,8 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1); - /* FIXME: ARMv8.1-VHE S2 translation regime. */ - if (regime_el(env, stage1) < 2) { + /* Get control bits for tagged addresses. */ + if (regime_has_2_ranges(mmu_idx)) { ARMVAParameters p1 = aa64_va_parameters_both(env, -1, stage1); tbid = (p1.tbi << 1) | p0.tbi; tbii = tbid & ~((p1.tbid << 1) | p0.tbid); @@ -11424,7 +11991,7 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len); } - sctlr = arm_sctlr(env, el); + sctlr = regime_sctlr(env, stage1); if (arm_cpu_data_is_big_endian_a64(el, sctlr)) { flags = FIELD_DP32(flags, TBFLAG_ANY, BE_DATA, 1); @@ -11449,6 +12016,28 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, } } + /* Compute the condition for using AccType_UNPRIV for LDTR et al. */ + /* TODO: ARMv8.2-UAO */ + switch (mmu_idx) { + case ARMMMUIdx_E10_1: + case ARMMMUIdx_SE10_1: + /* TODO: ARMv8.3-NV */ + flags = FIELD_DP32(flags, TBFLAG_A64, UNPRIV, 1); + break; + case ARMMMUIdx_E20_2: + /* TODO: ARMv8.4-SecEL2 */ + /* + * Note that E20_2 is gated by HCR_EL2.E2H == 1, but E20_0 is + * gated by HCR_EL2.<E2H,TGE> == '11', and so is LDTR. + */ + if (env->cp15.hcr_el2 & HCR_TGE) { + flags = FIELD_DP32(flags, TBFLAG_A64, UNPRIV, 1); + } + break; + default: + break; + } + return rebuild_hflags_common(env, fp_el, mmu_idx, flags); } @@ -11544,7 +12133,7 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, if (arm_feature(env, ARM_FEATURE_M_SECURITY) && FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S) != env->v7m.secure) { - flags = FIELD_DP32(flags, TBFLAG_A32, FPCCR_S_WRONG, 1); + flags = FIELD_DP32(flags, TBFLAG_M32, FPCCR_S_WRONG, 1); } if ((env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) && @@ -11556,12 +12145,12 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, * active FP context; we must create a new FP context before * executing any FP insn. */ - flags = FIELD_DP32(flags, TBFLAG_A32, NEW_FP_CTXT_NEEDED, 1); + flags = FIELD_DP32(flags, TBFLAG_M32, NEW_FP_CTXT_NEEDED, 1); } bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) { - flags = FIELD_DP32(flags, TBFLAG_A32, LSPACT, 1); + flags = FIELD_DP32(flags, TBFLAG_M32, LSPACT, 1); } } else { /* @@ -11582,8 +12171,8 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, } } - flags = FIELD_DP32(flags, TBFLAG_A32, THUMB, env->thumb); - flags = FIELD_DP32(flags, TBFLAG_A32, CONDEXEC, env->condexec_bits); + flags = FIELD_DP32(flags, TBFLAG_AM32, THUMB, env->thumb); + flags = FIELD_DP32(flags, TBFLAG_AM32, CONDEXEC, env->condexec_bits); pstate_for_ss = env->uncached_cpsr; } diff --git a/target/arm/internals.h b/target/arm/internals.h index f5313dd3d4..6d4a942bde 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -769,6 +769,39 @@ bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +static inline int arm_to_core_mmu_idx(ARMMMUIdx mmu_idx) +{ + return mmu_idx & ARM_MMU_IDX_COREIDX_MASK; +} + +static inline ARMMMUIdx core_to_arm_mmu_idx(CPUARMState *env, int mmu_idx) +{ + if (arm_feature(env, ARM_FEATURE_M)) { + return mmu_idx | ARM_MMU_IDX_M; + } else { + return mmu_idx | ARM_MMU_IDX_A; + } +} + +int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx); + +/* + * Return the MMU index for a v7M CPU with all relevant information + * manually specified. + */ +ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env, + bool secstate, bool priv, bool negpri); + +/* + * Return the MMU index for a v7M CPU in the specified security and + * privilege state. + */ +ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, + bool secstate, bool priv); + +/* Return the MMU index for a v7M CPU in the specified security state */ +ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate); + /* Return true if the stage 1 translation regime is using LPAE format page * tables */ bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx); @@ -804,24 +837,44 @@ static inline void arm_call_el_change_hook(ARMCPU *cpu) } } +/* Return true if this address translation regime has two ranges. */ +static inline bool regime_has_2_ranges(ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_SE10_0: + case ARMMMUIdx_SE10_1: + return true; + default: + return false; + } +} + /* Return true if this address translation regime is secure */ static inline bool regime_is_secure(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { - case ARMMMUIdx_S12NSE0: - case ARMMMUIdx_S12NSE1: - case ARMMMUIdx_S1NSE0: - case ARMMMUIdx_S1NSE1: - case ARMMMUIdx_S1E2: - case ARMMMUIdx_S2NS: + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_E2: + case ARMMMUIdx_Stage2: case ARMMMUIdx_MPrivNegPri: case ARMMMUIdx_MUserNegPri: case ARMMMUIdx_MPriv: case ARMMMUIdx_MUser: return false; - case ARMMMUIdx_S1E3: - case ARMMMUIdx_S1SE0: - case ARMMMUIdx_S1SE1: + case ARMMMUIdx_SE3: + case ARMMMUIdx_SE10_0: + case ARMMMUIdx_SE10_1: case ARMMMUIdx_MSPrivNegPri: case ARMMMUIdx_MSUserNegPri: case ARMMMUIdx_MSPriv: @@ -975,7 +1028,7 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env); #ifdef CONFIG_USER_ONLY static inline ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env) { - return ARMMMUIdx_S1NSE0; + return ARMMMUIdx_Stage1_E0; } #else ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env); diff --git a/target/arm/monitor.c b/target/arm/monitor.c index 9725dfff16..c2dc7908de 100644 --- a/target/arm/monitor.c +++ b/target/arm/monitor.c @@ -137,17 +137,20 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, } if (kvm_enabled()) { - const char *cpu_type = current_machine->cpu_type; - int len = strlen(cpu_type) - strlen(ARM_CPU_TYPE_SUFFIX); bool supported = false; if (!strcmp(model->name, "host") || !strcmp(model->name, "max")) { /* These are kvmarm's recommended cpu types */ supported = true; - } else if (strlen(model->name) == len && - !strncmp(model->name, cpu_type, len)) { - /* KVM is enabled and we're using this type, so it works. */ - supported = true; + } else if (current_machine->cpu_type) { + const char *cpu_type = current_machine->cpu_type; + int len = strlen(cpu_type) - strlen(ARM_CPU_TYPE_SUFFIX); + + if (strlen(model->name) == len && + !strncmp(model->name, cpu_type, len)) { + /* KVM is enabled and we're using this type, so it works. */ + supported = true; + } } if (!supported) { error_setg(errp, "We cannot guarantee the CPU type '%s' works " diff --git a/target/arm/pauth_helper.c b/target/arm/pauth_helper.c index 0a5f41e10c..9746e32bf8 100644 --- a/target/arm/pauth_helper.c +++ b/target/arm/pauth_helper.c @@ -371,7 +371,10 @@ static void pauth_check_trap(CPUARMState *env, int el, uintptr_t ra) if (el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { uint64_t hcr = arm_hcr_el2_eff(env); bool trap = !(hcr & HCR_API); - /* FIXME: ARMv8.1-VHE: trap only applies to EL1&0 regime. */ + if (el == 0) { + /* Trap only applies to EL1&0 regime. */ + trap &= (hcr & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE); + } /* FIXME: ARMv8.3-NV: HCR_NV trap takes precedence for ERETA[AB]. */ if (trap) { pauth_trap(env, 2, ra); @@ -386,14 +389,7 @@ static void pauth_check_trap(CPUARMState *env, int el, uintptr_t ra) static bool pauth_key_enabled(CPUARMState *env, int el, uint32_t bit) { - uint32_t sctlr; - if (el == 0) { - /* FIXME: ARMv8.1-VHE S2 translation regime. */ - sctlr = env->cp15.sctlr_el[1]; - } else { - sctlr = env->cp15.sctlr_el[el]; - } - return (sctlr & bit) != 0; + return (arm_sctlr(env, el) & bit) != 0; } uint64_t HELPER(pacia)(CPUARMState *env, uint64_t x, uint64_t y) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 766a03335b..6e82486884 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -105,25 +105,36 @@ void a64_translate_init(void) offsetof(CPUARMState, exclusive_high), "exclusive_high"); } -static inline int get_a64_user_mem_index(DisasContext *s) +/* + * Return the core mmu_idx to use for A64 "unprivileged load/store" insns + */ +static int get_a64_user_mem_index(DisasContext *s) { - /* Return the core mmu_idx to use for A64 "unprivileged load/store" insns: - * if EL1, access as if EL0; otherwise access at current EL + /* + * If AccType_UNPRIV is not used, the insn uses AccType_NORMAL, + * which is the usual mmu_idx for this cpu state. */ - ARMMMUIdx useridx; + ARMMMUIdx useridx = s->mmu_idx; - switch (s->mmu_idx) { - case ARMMMUIdx_S12NSE1: - useridx = ARMMMUIdx_S12NSE0; - break; - case ARMMMUIdx_S1SE1: - useridx = ARMMMUIdx_S1SE0; - break; - case ARMMMUIdx_S2NS: - g_assert_not_reached(); - default: - useridx = s->mmu_idx; - break; + if (s->unpriv) { + /* + * We have pre-computed the condition for AccType_UNPRIV. + * Therefore we should never get here with a mmu_idx for + * which we do not know the corresponding user mmu_idx. + */ + switch (useridx) { + case ARMMMUIdx_E10_1: + useridx = ARMMMUIdx_E10_0; + break; + case ARMMMUIdx_E20_2: + useridx = ARMMMUIdx_E20_0; + break; + case ARMMMUIdx_SE10_1: + useridx = ARMMMUIdx_SE10_0; + break; + default: + g_assert_not_reached(); + } } return arm_to_core_mmu_idx(useridx); } @@ -175,8 +186,7 @@ static void gen_top_byte_ignore(DisasContext *s, TCGv_i64 dst, if (tbi == 0) { /* Load unmodified address */ tcg_gen_mov_i64(dst, src); - } else if (s->current_el >= 2) { - /* FIXME: ARMv8.1-VHE S2 translation regime. */ + } else if (!regime_has_2_ranges(s->mmu_idx)) { /* Force tag byte to all zero */ tcg_gen_extract_i64(dst, src, 0, 56); } else { @@ -14172,6 +14182,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->pauth_active = FIELD_EX32(tb_flags, TBFLAG_A64, PAUTH_ACTIVE); dc->bt = FIELD_EX32(tb_flags, TBFLAG_A64, BT); dc->btype = FIELD_EX32(tb_flags, TBFLAG_A64, BTYPE); + dc->unpriv = FIELD_EX32(tb_flags, TBFLAG_A64, UNPRIV); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; diff --git a/target/arm/translate.c b/target/arm/translate.c index 2f4aea927f..e11a5871d0 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -152,14 +152,14 @@ static inline int get_a32_user_mem_index(DisasContext *s) * otherwise, access as if at PL0. */ switch (s->mmu_idx) { - case ARMMMUIdx_S1E2: /* this one is UNPREDICTABLE */ - case ARMMMUIdx_S12NSE0: - case ARMMMUIdx_S12NSE1: - return arm_to_core_mmu_idx(ARMMMUIdx_S12NSE0); - case ARMMMUIdx_S1E3: - case ARMMMUIdx_S1SE0: - case ARMMMUIdx_S1SE1: - return arm_to_core_mmu_idx(ARMMMUIdx_S1SE0); + case ARMMMUIdx_E2: /* this one is UNPREDICTABLE */ + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: + return arm_to_core_mmu_idx(ARMMMUIdx_E10_0); + case ARMMMUIdx_SE3: + case ARMMMUIdx_SE10_0: + case ARMMMUIdx_SE10_1: + return arm_to_core_mmu_idx(ARMMMUIdx_SE10_0); case ARMMMUIdx_MUser: case ARMMMUIdx_MPriv: return arm_to_core_mmu_idx(ARMMMUIdx_MUser); @@ -172,7 +172,6 @@ static inline int get_a32_user_mem_index(DisasContext *s) case ARMMMUIdx_MSUserNegPri: case ARMMMUIdx_MSPrivNegPri: return arm_to_core_mmu_idx(ARMMMUIdx_MSUserNegPri); - case ARMMMUIdx_S2NS: default: g_assert_not_reached(); } @@ -10848,38 +10847,48 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) */ dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); - dc->thumb = FIELD_EX32(tb_flags, TBFLAG_A32, THUMB); - dc->sctlr_b = FIELD_EX32(tb_flags, TBFLAG_A32, SCTLR_B); - dc->hstr_active = FIELD_EX32(tb_flags, TBFLAG_A32, HSTR_ACTIVE); + dc->thumb = FIELD_EX32(tb_flags, TBFLAG_AM32, THUMB); dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE; - condexec = FIELD_EX32(tb_flags, TBFLAG_A32, CONDEXEC); + condexec = FIELD_EX32(tb_flags, TBFLAG_AM32, CONDEXEC); dc->condexec_mask = (condexec & 0xf) << 1; dc->condexec_cond = condexec >> 4; + core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX); dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif - dc->ns = FIELD_EX32(tb_flags, TBFLAG_A32, NS); dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL); - dc->vfp_enabled = FIELD_EX32(tb_flags, TBFLAG_A32, VFPEN); - dc->vec_len = FIELD_EX32(tb_flags, TBFLAG_A32, VECLEN); - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - dc->c15_cpar = FIELD_EX32(tb_flags, TBFLAG_A32, XSCALE_CPAR); - dc->vec_stride = 0; + + if (arm_feature(env, ARM_FEATURE_M)) { + dc->vfp_enabled = 1; + dc->be_data = MO_TE; + dc->v7m_handler_mode = FIELD_EX32(tb_flags, TBFLAG_M32, HANDLER); + dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) && + regime_is_secure(env, dc->mmu_idx); + dc->v8m_stackcheck = FIELD_EX32(tb_flags, TBFLAG_M32, STACKCHECK); + dc->v8m_fpccr_s_wrong = + FIELD_EX32(tb_flags, TBFLAG_M32, FPCCR_S_WRONG); + dc->v7m_new_fp_ctxt_needed = + FIELD_EX32(tb_flags, TBFLAG_M32, NEW_FP_CTXT_NEEDED); + dc->v7m_lspact = FIELD_EX32(tb_flags, TBFLAG_M32, LSPACT); } else { - dc->vec_stride = FIELD_EX32(tb_flags, TBFLAG_A32, VECSTRIDE); - dc->c15_cpar = 0; - } - dc->v7m_handler_mode = FIELD_EX32(tb_flags, TBFLAG_A32, HANDLER); - dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) && - regime_is_secure(env, dc->mmu_idx); - dc->v8m_stackcheck = FIELD_EX32(tb_flags, TBFLAG_A32, STACKCHECK); - dc->v8m_fpccr_s_wrong = FIELD_EX32(tb_flags, TBFLAG_A32, FPCCR_S_WRONG); - dc->v7m_new_fp_ctxt_needed = - FIELD_EX32(tb_flags, TBFLAG_A32, NEW_FP_CTXT_NEEDED); - dc->v7m_lspact = FIELD_EX32(tb_flags, TBFLAG_A32, LSPACT); + dc->be_data = + FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE; + dc->debug_target_el = + FIELD_EX32(tb_flags, TBFLAG_ANY, DEBUG_TARGET_EL); + dc->sctlr_b = FIELD_EX32(tb_flags, TBFLAG_A32, SCTLR_B); + dc->hstr_active = FIELD_EX32(tb_flags, TBFLAG_A32, HSTR_ACTIVE); + dc->ns = FIELD_EX32(tb_flags, TBFLAG_A32, NS); + dc->vfp_enabled = FIELD_EX32(tb_flags, TBFLAG_A32, VFPEN); + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + dc->c15_cpar = FIELD_EX32(tb_flags, TBFLAG_A32, XSCALE_CPAR); + } else { + dc->vec_len = FIELD_EX32(tb_flags, TBFLAG_A32, VECLEN); + dc->vec_stride = FIELD_EX32(tb_flags, TBFLAG_A32, VECSTRIDE); + } + } dc->cp_regs = cpu->cp_regs; dc->features = env->features; @@ -10901,9 +10910,6 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->ss_active = FIELD_EX32(tb_flags, TBFLAG_ANY, SS_ACTIVE); dc->pstate_ss = FIELD_EX32(tb_flags, TBFLAG_ANY, PSTATE_SS); dc->is_ldex = false; - if (!arm_feature(env, ARM_FEATURE_M)) { - dc->debug_target_el = FIELD_EX32(tb_flags, TBFLAG_ANY, DEBUG_TARGET_EL); - } dc->page_start = dc->base.pc_first & TARGET_PAGE_MASK; @@ -11340,10 +11346,10 @@ static const TranslatorOps thumb_translator_ops = { /* generate intermediate code for basic block 'tb'. */ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) { - DisasContext dc; + DisasContext dc = { }; const TranslatorOps *ops = &arm_translator_ops; - if (FIELD_EX32(tb->flags, TBFLAG_A32, THUMB)) { + if (FIELD_EX32(tb->flags, TBFLAG_AM32, THUMB)) { ops = &thumb_translator_ops; } #ifdef TARGET_AARCH64 diff --git a/target/arm/translate.h b/target/arm/translate.h index b837b7fcbf..5b167c416a 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -73,6 +73,8 @@ typedef struct DisasContext { * ie A64 LDX*, LDAX*, A32/T32 LDREX*, LDAEX*. */ bool is_ldex; + /* True if AccType_UNPRIV should be used for LDTR et al */ + bool unpriv; /* True if v8.3-PAuth is active. */ bool pauth_active; /* True with v8.5-BTI and SCTLR_ELx.BT* set. */ @@ -126,7 +128,7 @@ static inline int default_exception_el(DisasContext *s) * exceptions can only be routed to ELs above 1, so we target the higher of * 1 or the current EL. */ - return (s->mmu_idx == ARMMMUIdx_S1SE0 && s->secure_routed_to_el3) + return (s->mmu_idx == ARMMMUIdx_SE10_0 && s->secure_routed_to_el3) ? 3 : MAX(1, s->current_el); } diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index 6618ea67c1..d4358eb431 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -55,19 +55,16 @@ def pick_default_qemu_bin(arch=None): return qemu_bin_from_src_dir_path -def wait_for_console_pattern(test, success_message, failure_message=None): - """ - Waits for messages to appear on the console, while logging the content - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.Test` - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - """ +def _console_interaction(test, success_message, failure_message, + send_string, keep_sending=False): + assert not keep_sending or send_string console = test.vm.console_socket.makefile() console_logger = logging.getLogger('console') while True: + if send_string: + test.vm.console_socket.sendall(send_string.encode()) + if not keep_sending: + send_string = None # send only once msg = console.readline().strip() if not msg: continue @@ -79,6 +76,43 @@ def wait_for_console_pattern(test, success_message, failure_message=None): fail = 'Failure message found in console: %s' % failure_message test.fail(fail) +def interrupt_interactive_console_until_pattern(test, success_message, + failure_message=None, + interrupt_string='\r'): + """ + Keep sending a string to interrupt a console prompt, while logging the + console output. Typical use case is to break a boot loader prompt, such: + + Press a key within 5 seconds to interrupt boot process. + 5 + 4 + 3 + 2 + 1 + Booting default image... + + :param test: an Avocado test containing a VM that will have its console + read and probed for a success or failure message + :type test: :class:`avocado_qemu.Test` + :param success_message: if this message appears, test succeeds + :param failure_message: if this message appears, test fails + :param interrupt_string: a string to send to the console before trying + to read a new line + """ + _console_interaction(test, success_message, failure_message, + interrupt_string, True) + +def wait_for_console_pattern(test, success_message, failure_message=None): + """ + Waits for messages to appear on the console, while logging the content + + :param test: an Avocado test containing a VM that will have its console + read and probed for a success or failure message + :type test: :class:`avocado_qemu.Test` + :param success_message: if this message appears, test succeeds + :param failure_message: if this message appears, test fails + """ + _console_interaction(test, success_message, failure_message, None) def exec_command_and_wait_for_pattern(test, command, success_message, failure_message=None): @@ -94,10 +128,7 @@ def exec_command_and_wait_for_pattern(test, command, :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails """ - command += '\r' - test.vm.console_socket.sendall(command.encode()) - wait_for_console_pattern(test, success_message, failure_message) - + _console_interaction(test, success_message, failure_message, command + '\r') class Test(avocado.Test): def _get_unique_tag_val(self, tag_name): diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index e40b84651b..34d37eba3b 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -40,7 +40,7 @@ class BootLinuxConsole(Test): Extracts a file from a deb package into the test workdir :param deb: path to the deb archive - :param file: path within the deb archive of the file to be extracted + :param path: path within the deb archive of the file to be extracted :returns: path of the extracted file """ cwd = os.getcwd() @@ -49,7 +49,28 @@ class BootLinuxConsole(Test): process.run("ar x %s %s" % (deb, file_path)) archive.extract(file_path, self.workdir) os.chdir(cwd) - return self.workdir + path + # Return complete path to extracted file. Because callers to + # extract_from_deb() specify 'path' with a leading slash, it is + # necessary to use os.path.relpath() as otherwise os.path.join() + # interprets it as an absolute path and drops the self.workdir part. + return os.path.normpath(os.path.join(self.workdir, + os.path.relpath(path, '/'))) + + def extract_from_rpm(self, rpm, path): + """ + Extracts a file from an RPM package into the test workdir. + + :param rpm: path to the rpm archive + :param path: path within the rpm archive of the file to be extracted + needs to be a relative path (starting with './') because + cpio(1), which is used to extract the file, expects that. + :returns: path of the extracted file + """ + cwd = os.getcwd() + os.chdir(self.workdir) + process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True) + os.chdir(cwd) + return os.path.normpath(os.path.join(self.workdir, path)) def test_x86_64_pc(self): """ @@ -304,6 +325,7 @@ class BootLinuxConsole(Test): :avocado: tags=arch:arm :avocado: tags=machine:emcraft-sf2 :avocado: tags=endian:little + :avocado: tags=u-boot """ uboot_url = ('https://raw.githubusercontent.com/' 'Subbaraya-Sundeep/qemu-test-binaries/' @@ -519,7 +541,7 @@ class BootLinuxConsole(Test): self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-vga', 'std', + self.vm.add_args('-nodefaults', '-kernel', uncompressed_kernel, '-append', kernel_command_line) self.vm.launch() @@ -568,3 +590,99 @@ class BootLinuxConsole(Test): self.wait_for_console_pattern(console_pattern) console_pattern = 'No filesystem could mount root' self.wait_for_console_pattern(console_pattern) + + def do_test_advcal_2018(self, day, tar_hash, kernel_name): + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day' + day + '.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', + self.workdir + '/day' + day + '/' + kernel_name) + self.vm.launch() + self.wait_for_console_pattern('QEMU advent calendar') + + def test_arm_vexpressa9(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:vexpress-a9 + """ + tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' + self.vm.add_args('-dtb', self.workdir + '/day16/vexpress-v2p-ca9.dtb') + self.do_test_advcal_2018('16', tar_hash, 'winter.zImage') + + def test_m68k_mcf5208evb(self): + """ + :avocado: tags=arch:m68k + :avocado: tags=machine:mcf5208evb + """ + tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' + self.do_test_advcal_2018('07', tar_hash, 'sanity-clause.elf') + + def test_microblaze_s3adsp1800(self): + """ + :avocado: tags=arch:microblaze + :avocado: tags=machine:petalogix-s3adsp1800 + """ + tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' + self.do_test_advcal_2018('17', tar_hash, 'ballerina.bin') + + def test_or1k_sim(self): + """ + :avocado: tags=arch:or1k + :avocado: tags=machine:or1k-sim + """ + tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' + self.do_test_advcal_2018('20', tar_hash, 'vmlinux') + + def test_nios2_10m50(self): + """ + :avocado: tags=arch:nios2 + :avocado: tags=machine:10m50-ghrd + """ + tar_hash = 'e4251141726c412ac0407c5a6bceefbbff018918' + self.do_test_advcal_2018('14', tar_hash, 'vmlinux.elf') + + def test_ppc64_e500(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:ppce500 + """ + tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' + self.vm.add_args('-cpu', 'e5500') + self.do_test_advcal_2018('19', tar_hash, 'uImage') + + def test_ppc_g3beige(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:g3beige + """ + tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' + self.vm.add_args('-M', 'graphics=off') + self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') + + def test_ppc_mac99(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:mac99 + """ + tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' + self.vm.add_args('-M', 'graphics=off') + self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') + + def test_sparc_ss20(self): + """ + :avocado: tags=arch:sparc + :avocado: tags=machine:SS-20 + """ + tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' + self.do_test_advcal_2018('11', tar_hash, 'zImage.elf') + + def test_xtensa_lx60(self): + """ + :avocado: tags=arch:xtensa + :avocado: tags=machine:lx60 + """ + tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' + self.vm.add_args('-cpu', 'dc233c') + self.do_test_advcal_2018('02', tar_hash, 'santas-sleigh-ride.elf') diff --git a/tests/acceptance/migration.py b/tests/acceptance/migration.py index a44c1ae58f..a8367ca023 100644 --- a/tests/acceptance/migration.py +++ b/tests/acceptance/migration.py @@ -10,13 +10,19 @@ # later. See the COPYING file in the top-level directory. +import tempfile from avocado_qemu import Test +from avocado import skipUnless from avocado.utils import network from avocado.utils import wait +from avocado.utils.path import find_command class Migration(Test): + """ + :avocado: tags=migration + """ timeout = 10 @@ -24,6 +30,28 @@ class Migration(Test): def migration_finished(vm): return vm.command('query-migrate')['status'] in ('completed', 'failed') + def assert_migration(self, src_vm, dst_vm): + wait.wait_for(self.migration_finished, + timeout=self.timeout, + step=0.1, + args=(src_vm,)) + self.assertEqual(src_vm.command('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.command('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.command('query-status')['status'], 'running') + self.assertEqual(src_vm.command('query-status')['status'],'postmigrate') + + def do_migrate(self, dest_uri, src_uri=None): + dest_vm = self.get_vm('-incoming', dest_uri) + dest_vm.add_args('-nodefaults') + dest_vm.launch() + if src_uri is None: + src_uri = dest_uri + source_vm = self.get_vm() + source_vm.add_args('-nodefaults') + source_vm.launch() + source_vm.qmp('migrate', uri=src_uri) + self.assert_migration(source_vm, dest_vm) + def _get_free_port(self): port = network.find_free_port() if port is None: @@ -32,19 +60,18 @@ class Migration(Test): def test_migration_with_tcp_localhost(self): - source_vm = self.get_vm() dest_uri = 'tcp:localhost:%u' % self._get_free_port() - dest_vm = self.get_vm('-incoming', dest_uri) - dest_vm.launch() - source_vm.launch() - source_vm.qmp('migrate', uri=dest_uri) - wait.wait_for( - self.migration_finished, - timeout=self.timeout, - step=0.1, - args=(source_vm,) - ) - self.assertEqual(dest_vm.command('query-migrate')['status'], 'completed') - self.assertEqual(source_vm.command('query-migrate')['status'], 'completed') - self.assertEqual(dest_vm.command('query-status')['status'], 'running') - self.assertEqual(source_vm.command('query-status')['status'], 'postmigrate') + self.do_migrate(dest_uri) + + def test_migration_with_unix(self): + with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: + dest_uri = 'unix:%s/qemu-test.sock' % socket_path + self.do_migrate(dest_uri) + + @skipUnless(find_command('nc', default=False), "'nc' command not found") + def test_migration_with_exec(self): + """ + The test works for both netcat-traditional and netcat-openbsd packages + """ + free_port = self._get_free_port() + dest_uri = 'exec:nc -l localhost %u' % free_port diff --git a/tests/acceptance/version.py b/tests/acceptance/version.py index 67c2192c93..79b923d4fc 100644 --- a/tests/acceptance/version.py +++ b/tests/acceptance/version.py @@ -17,6 +17,7 @@ class Version(Test): :avocado: tags=quick """ def test_qmp_human_info_version(self): + self.vm.add_args('-nodefaults') self.vm.launch() res = self.vm.command('human-monitor-command', command_line='info version') diff --git a/tests/acceptance/virtio_seg_max_adjust.py b/tests/acceptance/virtio_check_params.py index 5458573138..87e6c839d1 100755..100644 --- a/tests/acceptance/virtio_seg_max_adjust.py +++ b/tests/acceptance/virtio_check_params.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # # Test virtio-scsi and virtio-blk queue settings for all machine types # @@ -21,10 +20,12 @@ import sys import os import re +import logging sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu.machine import QEMUMachine from avocado_qemu import Test +from avocado import skip #list of machine types and virtqueue properties to test VIRTIO_SCSI_PROPS = {'seg_max_adjust': 'seg_max_adjust'} @@ -73,12 +74,20 @@ class VirtioMaxSegSettingsCheck(Test): return query_ok, props, error def check_mt(self, mt, dev_type_name): + mt['device'] = dev_type_name # Only for the debug() call. + logger = logging.getLogger('machine') + logger.debug(mt) with QEMUMachine(self.qemu_bin) as vm: vm.set_machine(mt["name"]) + vm.add_args('-nodefaults') for s in VM_DEV_PARAMS[dev_type_name]: vm.add_args(s) - vm.launch() - query_ok, props, error = self.query_virtqueue(vm, dev_type_name) + try: + vm.launch() + query_ok, props, error = self.query_virtqueue(vm, dev_type_name) + except: + query_ok = False + error = sys.exc_info()[0] if not query_ok: self.fail('machine type {0}: {1}'.format(mt['name'], error)) @@ -108,6 +117,7 @@ class VirtioMaxSegSettingsCheck(Test): return True return False + @skip("break multi-arch CI") def test_machine_types(self): # collect all machine types except 'none', 'isapc', 'microvm' with QEMUMachine(self.qemu_bin) as vm: diff --git a/tests/acceptance/x86_cpu_model_versions.py b/tests/acceptance/x86_cpu_model_versions.py index 90558d9a71..01ff614ec2 100644 --- a/tests/acceptance/x86_cpu_model_versions.py +++ b/tests/acceptance/x86_cpu_model_versions.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # # Basic validation of x86 versioned CPU models and CPU model aliases # diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 19dbe26169..43a8678688 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -91,19 +91,12 @@ endif # Enforce dependencies for composite images docker-image-debian9-mxe: docker-image-debian9 ifeq ($(HOST_ARCH),x86_64) -docker-image-debian-amd64: docker-image-debian9 +docker-image-debian-amd64: docker-image-debian10 DOCKER_PARTIAL_IMAGES += debian-amd64-cross else docker-image-debian-amd64-cross: docker-image-debian10 DOCKER_PARTIAL_IMAGES += debian-amd64 endif -docker-image-debian-armel-cross: docker-image-debian9 -docker-image-debian-armhf-cross: docker-image-debian9 -docker-image-debian-mips-cross: docker-image-debian9 -docker-image-debian-mipsel-cross: docker-image-debian9 -docker-image-debian-mips64el-cross: docker-image-debian9 -docker-image-debian-ppc64el-cross: docker-image-debian9 -docker-image-debian-s390x-cross: docker-image-debian9 docker-image-debian-win32-cross: docker-image-debian9-mxe docker-image-debian-win64-cross: docker-image-debian9-mxe @@ -118,12 +111,19 @@ endif docker-image-debian-alpha-cross: docker-image-debian10 docker-image-debian-arm64-cross: docker-image-debian10 +docker-image-debian-armel-cross: docker-image-debian10 +docker-image-debian-armhf-cross: docker-image-debian10 docker-image-debian-hppa-cross: docker-image-debian10 docker-image-debian-m68k-cross: docker-image-debian10 +docker-image-debian-mips-cross: docker-image-debian10 docker-image-debian-mips64-cross: docker-image-debian10 +docker-image-debian-mips64el-cross: docker-image-debian10 +docker-image-debian-mipsel-cross: docker-image-debian10 docker-image-debian-powerpc-cross: docker-image-debian10 docker-image-debian-ppc64-cross: docker-image-debian10 +docker-image-debian-ppc64el-cross: docker-image-debian10 docker-image-debian-riscv64-cross: docker-image-debian10 +docker-image-debian-s390x-cross: docker-image-debian10 docker-image-debian-sh4-cross: docker-image-debian10 docker-image-debian-sparc64-cross: docker-image-debian10 diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 31d8adf836..d8268c1111 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -106,18 +106,19 @@ def _get_so_libs(executable): """Return a list of libraries associated with an executable. The paths may be symbolic links which would need to be resolved to - ensure theright data is copied.""" + ensure the right data is copied.""" libs = [] - ldd_re = re.compile(r"(/.*/)(\S*)") + ldd_re = re.compile(r"(?:\S+ => )?(\S*) \(:?0x[0-9a-f]+\)") try: ldd_output = subprocess.check_output(["ldd", executable]).decode('utf-8') for line in ldd_output.split("\n"): search = ldd_re.search(line) - if search and len(search.groups()) == 2: - so_path = search.groups()[0] - so_lib = search.groups()[1] - libs.append("%s/%s" % (so_path, so_lib)) + if search: + try: + libs.append(s.group(1)) + except IndexError: + pass except subprocess.CalledProcessError: print("%s had no associated libraries (static build?)" % (executable)) @@ -145,7 +146,8 @@ def _copy_binary_with_libs(src, bin_dest, dest_dir): if libs: for l in libs: so_path = os.path.dirname(l) - _copy_with_mkdir(l, dest_dir, so_path) + real_l = os.path.realpath(l) + _copy_with_mkdir(real_l, dest_dir, so_path) def _check_binfmt_misc(executable): diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index 431e947ebd..3b860af106 100644 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ b/tests/docker/dockerfiles/debian-amd64.docker @@ -4,7 +4,7 @@ # This docker target builds on the debian Stretch base image. Further # libraries which are not widely available are installed by hand. # -FROM qemu:debian9 +FROM qemu:debian10 MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org> RUN apt update && \ diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index 15378f8ea2..e3794a61c9 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -3,7 +3,7 @@ # # This docker target builds on the debian Stretch base image. # -FROM qemu:debian9 +FROM qemu:debian10 MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org> # Add the foreign architecture we want and install dependencies diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 4a20af6fe1..e163b8b956 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -3,7 +3,7 @@ # # This docker target builds on the debian Stretch base image. # -FROM qemu:debian9 +FROM qemu:debian10 # Add the foreign architecture we want and install dependencies RUN dpkg --add-architecture armhf diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 2fca112405..453b53ef72 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -4,7 +4,7 @@ # This docker target builds on the debian Stretch base image. # -FROM qemu:debian9 +FROM qemu:debian10 MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org> diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 4abf7832ac..3b6e975c68 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -3,7 +3,7 @@ # # This docker target builds on the debian Stretch base image. # -FROM qemu:debian9 +FROM qemu:debian10 MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org> diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 9973df9ff7..cd386f01d9 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -3,7 +3,7 @@ # # This docker target builds on the debian Stretch base image. # -FROM qemu:debian9 +FROM qemu:debian10 # Add the foreign architecture we want and install dependencies RUN dpkg --add-architecture ppc64el && \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index eb73c98855..43fe59836f 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -3,7 +3,7 @@ # # This docker target builds on the debian Stretch base image. # -FROM qemu:debian9 +FROM qemu:debian10 # Add the s390x architecture RUN dpkg --add-architecture s390x diff --git a/tests/docker/travis.py b/tests/docker/travis.py index e1433012bd..37307ac366 100755 --- a/tests/docker/travis.py +++ b/tests/docker/travis.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Travis YAML config parser # @@ -11,7 +11,6 @@ # or (at your option) any later version. See the COPYING file in # the top-level directory. -from __future__ import print_function import sys import yaml import itertools diff --git a/tests/guest-debug/test-gdbstub.py b/tests/guest-debug/test-gdbstub.py index c7e3986a24..98a5df4d42 100644 --- a/tests/guest-debug/test-gdbstub.py +++ b/tests/guest-debug/test-gdbstub.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # This script needs to be run on startup # qemu -kernel ${KERNEL} -s -S diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index 1dd04ce33b..fd63c66601 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Migration test main engine # diff --git a/tests/migration/guestperf/plot.py b/tests/migration/guestperf/plot.py index aa98912a82..34cebd54ba 100644 --- a/tests/migration/guestperf/plot.py +++ b/tests/migration/guestperf/plot.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Migration test graph plotting # diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index 61d2abbaad..5bcc066bb9 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Migration test command line shell integration # diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index bad14edb47..41232c11a3 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # QAPI parser test harness # @@ -11,7 +11,6 @@ # See the COPYING file in the top-level directory. # -from __future__ import print_function import argparse import difflib diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 0990681c1e..aa911d266a 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for image streaming. # diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 74f62c3c4a..2e7ee0e84f 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for image block commit. # diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index c07437fda1..43556b9727 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for image mirroring. # @@ -1134,4 +1134,5 @@ class TestOrphanedSource(iotests.QMPTestCase): if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed'], - supported_protocols=['file']) + supported_protocols=['file'], + supported_platforms=['linux', 'freebsd', 'netbsd', 'openbsd']) diff --git a/tests/qemu-iotests/044 b/tests/qemu-iotests/044 index 8b2afa2a11..7e99ea7c68 100755 --- a/tests/qemu-iotests/044 +++ b/tests/qemu-iotests/044 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests growing a large refcount table. # diff --git a/tests/qemu-iotests/045 b/tests/qemu-iotests/045 index 01cc038884..5acc89099c 100755 --- a/tests/qemu-iotests/045 +++ b/tests/qemu-iotests/045 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for fdsets and getfd. # diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 index c732a112d6..82b9f5f47d 100755 --- a/tests/qemu-iotests/055 +++ b/tests/qemu-iotests/055 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for drive-backup and blockdev-backup # diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056 index f39287c162..f73fc74457 100755 --- a/tests/qemu-iotests/056 +++ b/tests/qemu-iotests/056 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for drive-backup # diff --git a/tests/qemu-iotests/057 b/tests/qemu-iotests/057 index 9fbba759b6..a8b4bb60e0 100755 --- a/tests/qemu-iotests/057 +++ b/tests/qemu-iotests/057 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for internal snapshot. # diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index 5b21eb96bd..6426474271 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test for additional information emitted by qemu-img info on qcow2 # images diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index f03fa24a07..32ded11430 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for IO throttling # diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096 index ab9cb47822..5915f92786 100755 --- a/tests/qemu-iotests/096 +++ b/tests/qemu-iotests/096 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test that snapshots move the throttling configuration to the active # layer diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index e20080e9a6..adc8a848b5 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test case for the QMP 'change' command and all other associated # commands diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index d3e851e1ae..3705cbb6b3 100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for incremental drive-backup # diff --git a/tests/qemu-iotests/127 b/tests/qemu-iotests/127 index b64926ab31..a4fc866038 100755 --- a/tests/qemu-iotests/127 +++ b/tests/qemu-iotests/127 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file +_require_devices virtio-scsi scsi-hd + IMG_SIZE=64K _make_test_img $IMG_SIZE diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129 index cd6b9e9ce7..b0da4a5541 100755 --- a/tests/qemu-iotests/129 +++ b/tests/qemu-iotests/129 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests that "bdrv_drain_all" doesn't drain block jobs # diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132 index 0f2a106c81..39ea43067e 100755 --- a/tests/qemu-iotests/132 +++ b/tests/qemu-iotests/132 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test mirror with unmap # diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index 012ea111ac..d59400c9fc 100755 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for block device statistics # diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index cbb5a76530..6b1a444364 100755 --- a/tests/qemu-iotests/139 +++ b/tests/qemu-iotests/139 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test cases for the QMP 'blockdev-del' command # diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 2b6f859a09..f4b0a11dba 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test case for NBD's blockdev-add interface # diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148 index 8c11c53cba..90931948e3 100755 --- a/tests/qemu-iotests/148 +++ b/tests/qemu-iotests/148 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test the rate limit of QMP events # diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index 8ab42e94c6..b4a21bf7b7 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2016 Red Hat, Inc. # @@ -20,7 +20,6 @@ # Exercise the QEMU 'luks' block driver to validate interoperability # with the Linux dm-crypt + cryptsetup implementation -from __future__ import print_function import subprocess import os import os.path diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 index 76ae265cc1..f2df72c29c 100755 --- a/tests/qemu-iotests/151 +++ b/tests/qemu-iotests/151 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for active mirroring # diff --git a/tests/qemu-iotests/152 b/tests/qemu-iotests/152 index 732bf5f062..cc2ea09654 100755 --- a/tests/qemu-iotests/152 +++ b/tests/qemu-iotests/152 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for drive-mirror with source size unaligned to granularity # diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index e19485911c..e35b1d534b 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test whether the backing BDSs are correct after completion of a # mirror block job; in "existing" modes (drive-mirror with diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163 index d94728e080..5a3cc840a5 100755 --- a/tests/qemu-iotests/163 +++ b/tests/qemu-iotests/163 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for shrinking images # diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index 951ea011a2..fb56a769b4 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for persistent dirty bitmaps. # @@ -18,7 +18,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -from __future__ import print_function import os import re import iotests diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169 index 9656a7f620..2c5a132aa3 100755 --- a/tests/qemu-iotests/169 +++ b/tests/qemu-iotests/169 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for dirty bitmaps migration. # diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 index 64621617f5..acdbefa310 100755 --- a/tests/qemu-iotests/183 +++ b/tests/qemu-iotests/183 @@ -42,6 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter . ./common.qemu +_supported_os Linux FreeBSD NetBSD _supported_fmt qcow2 raw qed quorum _supported_proto file diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index 72e47e8833..9dc1bd3510 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2017 Red Hat, Inc. # diff --git a/tests/qemu-iotests/196 b/tests/qemu-iotests/196 index 92fe9244f8..e8fcf37273 100755 --- a/tests/qemu-iotests/196 +++ b/tests/qemu-iotests/196 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test clearing unknown autoclear_features flag by qcow2 after # migration. This test mimics migration to older qemu. diff --git a/tests/qemu-iotests/199 b/tests/qemu-iotests/199 index a2c8ecab5a..40774eed74 100755 --- a/tests/qemu-iotests/199 +++ b/tests/qemu-iotests/199 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for dirty bitmaps postcopy migration. # diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202 index 581ca34d79..920a8683ef 100755 --- a/tests/qemu-iotests/202 +++ b/tests/qemu-iotests/202 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2017 Red Hat, Inc. # diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203 index 4874a1a0d8..49eff5d405 100755 --- a/tests/qemu-iotests/203 +++ b/tests/qemu-iotests/203 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2017 Red Hat, Inc. # diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 index 4bb2c21e8b..43432cb599 100755 --- a/tests/qemu-iotests/205 +++ b/tests/qemu-iotests/205 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for qmp command nbd-server-remove. # diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index 9f16a7df8d..e2b50ae24d 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test qcow2 and file image creation # diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index 812ab34e47..3d9c1208ca 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test ssh image creation # diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208 index 546eb1de3e..1c3fc8c7fd 100755 --- a/tests/qemu-iotests/208 +++ b/tests/qemu-iotests/208 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2018 Red Hat, Inc. # diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209 index e0f464bcbe..65c1a1e70a 100755 --- a/tests/qemu-iotests/209 +++ b/tests/qemu-iotests/209 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for NBD BLOCK_STATUS extension # diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 index 4ca0fe26ef..e49896e23d 100755 --- a/tests/qemu-iotests/210 +++ b/tests/qemu-iotests/210 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test luks and file image creation # diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index 8834ebfe85..163994d559 100755 --- a/tests/qemu-iotests/211 +++ b/tests/qemu-iotests/211 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test VDI and file image creation # diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index 8f3ccc7b15..800f92dd84 100755 --- a/tests/qemu-iotests/212 +++ b/tests/qemu-iotests/212 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test parallels and file image creation # diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 index 3fc8dc6eaa..1eee45276a 100755 --- a/tests/qemu-iotests/213 +++ b/tests/qemu-iotests/213 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test vhdx and file image creation # diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216 index 3c0ae54b44..372f042d3e 100755 --- a/tests/qemu-iotests/216 +++ b/tests/qemu-iotests/216 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copy-on-read tests using a COR filter node # diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 index 2554d84581..1325ba9eaa 100755 --- a/tests/qemu-iotests/218 +++ b/tests/qemu-iotests/218 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # This test covers what happens when a mirror block job is cancelled # in various phases of its existence. diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219 index 655f54d881..b8774770c4 100755 --- a/tests/qemu-iotests/219 +++ b/tests/qemu-iotests/219 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2018 Red Hat, Inc. # diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 index 3f9f934ad8..bf1718e179 100644..100755 --- a/tests/qemu-iotests/222 +++ b/tests/qemu-iotests/222 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # This test covers the basic fleecing workflow, which provides a # point-in-time snapshot of a node that can be queried over NBD. diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 index ea69cd4b8b..56fbc5fb09 100755 --- a/tests/qemu-iotests/223 +++ b/tests/qemu-iotests/223 @@ -153,7 +153,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", "bitmap":"b3"}}' "error" # Missing bitmap _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "writable":true, - "bitmap":"b2"}}' "return" + "description":"some text", "bitmap":"b2"}}' "return" $QEMU_NBD_PROG -L -k "$SOCK_DIR/nbd" echo diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out index f175598802..80c0cf6509 100644 --- a/tests/qemu-iotests/223.out +++ b/tests/qemu-iotests/223.out @@ -50,7 +50,7 @@ exports available: 0 {"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}} {"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "bitmap":"b3"}} {"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}} -{"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "writable":true, "bitmap":"b2"}} +{"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "writable":true, "description":"some text", "bitmap":"b2"}} {"return": {}} exports available: 2 export: 'n' @@ -63,6 +63,7 @@ exports available: 2 base:allocation qemu:dirty-bitmap:b export: 'n2' + description: some text size: 4194304 flags: 0xced ( flush fua trim zeroes df cache fast-zero ) min block: 1 @@ -130,7 +131,7 @@ exports available: 0 {"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}} {"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "bitmap":"b3"}} {"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}} -{"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "writable":true, "bitmap":"b2"}} +{"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "writable":true, "description":"some text", "bitmap":"b2"}} {"return": {}} exports available: 2 export: 'n' @@ -143,6 +144,7 @@ exports available: 2 base:allocation qemu:dirty-bitmap:b export: 'n2' + description: some text size: 4194304 flags: 0xced ( flush fua trim zeroes df cache fast-zero ) min block: 1 diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224 index b4dfaa639f..e91fb26fd8 100755 --- a/tests/qemu-iotests/224 +++ b/tests/qemu-iotests/224 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test json:{} filenames with qemu-internal BDSs # (the one of commit, to be precise) diff --git a/tests/qemu-iotests/228 b/tests/qemu-iotests/228 index 9a50afd205..64bc82ee23 100755 --- a/tests/qemu-iotests/228 +++ b/tests/qemu-iotests/228 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test for when a backing file is considered overridden (thus, a # json:{} filename is generated for the overlay) and when it is not diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234 index 59a7f949ec..324c1549fd 100755 --- a/tests/qemu-iotests/234 +++ b/tests/qemu-iotests/234 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2018 Red Hat, Inc. # diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235 index 3d7533980d..760826128e 100755 --- a/tests/qemu-iotests/235 +++ b/tests/qemu-iotests/235 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Simple mirror test # diff --git a/tests/qemu-iotests/236 b/tests/qemu-iotests/236 index 79a6381f8e..8ce927a16c 100755 --- a/tests/qemu-iotests/236 +++ b/tests/qemu-iotests/236 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test bitmap merges. # diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237 index a2242a4736..50ba364a3e 100755 --- a/tests/qemu-iotests/237 +++ b/tests/qemu-iotests/237 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test vmdk and file image creation # diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238 index e5ac2b2ff8..d4e060228c 100755 --- a/tests/qemu-iotests/238 +++ b/tests/qemu-iotests/238 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Regression test for throttle group member unregister segfault with iothread # diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242 index c176e92da6..97617876bc 100755 --- a/tests/qemu-iotests/242 +++ b/tests/qemu-iotests/242 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test for qcow2 bitmap printed information # diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index d12b253065..489bf78bd0 100644..100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test cases for the QMP 'x-blockdev-reopen' command # diff --git a/tests/qemu-iotests/246 b/tests/qemu-iotests/246 index b0997a392f..59a216a839 100755 --- a/tests/qemu-iotests/246 +++ b/tests/qemu-iotests/246 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test persistent bitmap resizing. # diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248 index f26b4bb2aa..68c374692e 100755 --- a/tests/qemu-iotests/248 +++ b/tests/qemu-iotests/248 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test resume mirror after auto pause on ENOSPC # diff --git a/tests/qemu-iotests/254 b/tests/qemu-iotests/254 index 09584f3f7d..ee66c986db 100755 --- a/tests/qemu-iotests/254 +++ b/tests/qemu-iotests/254 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test external snapshot with bitmap copying and moving. # diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255 index 0ba03d9e61..4a4818bafb 100755 --- a/tests/qemu-iotests/255 +++ b/tests/qemu-iotests/255 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test commit job graph modifications while requests are active # diff --git a/tests/qemu-iotests/256 b/tests/qemu-iotests/256 index c594a43205..e34074c83e 100755 --- a/tests/qemu-iotests/256 +++ b/tests/qemu-iotests/256 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test incremental/backup across iothread contexts # diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index a9828251cf..004a433b8b 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test bitmap-sync backups (incremental, differential, and partials) # diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258 index b84cf02254..091755a45c 100755 --- a/tests/qemu-iotests/258 +++ b/tests/qemu-iotests/258 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Very specific tests for adjacent commit/stream block jobs # diff --git a/tests/qemu-iotests/260 b/tests/qemu-iotests/260 index 4f6082c9d2..30c0de380d 100755 --- a/tests/qemu-iotests/260 +++ b/tests/qemu-iotests/260 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tests for temporary external snapshot when we have bitmaps. # diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262 index bbcb5260a6..8835dce7be 100755 --- a/tests/qemu-iotests/262 +++ b/tests/qemu-iotests/262 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2019 Red Hat, Inc. # diff --git a/tests/qemu-iotests/264 b/tests/qemu-iotests/264 index 131366422b..879123a343 100755 --- a/tests/qemu-iotests/264 +++ b/tests/qemu-iotests/264 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test nbd reconnect # diff --git a/tests/qemu-iotests/266 b/tests/qemu-iotests/266 index c353cf88ee..91bdf8729e 100755 --- a/tests/qemu-iotests/266 +++ b/tests/qemu-iotests/266 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test VPC and file image creation # diff --git a/tests/qemu-iotests/267 b/tests/qemu-iotests/267 index c296877168..3146273eef 100755 --- a/tests/qemu-iotests/267 +++ b/tests/qemu-iotests/267 @@ -46,6 +46,8 @@ _require_drivers copy-on-read # and generally impossible with external data files _unsupported_imgopts 'refcount_bits=1[^0-9]' data_file +_require_devices virtio-blk + do_run_qemu() { echo Testing: "$@" diff --git a/tests/qemu-iotests/277 b/tests/qemu-iotests/277 index 1f72dca2d4..04aa15a3d5 100755 --- a/tests/qemu-iotests/277 +++ b/tests/qemu-iotests/277 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test NBD client reconnection # diff --git a/tests/qemu-iotests/280 b/tests/qemu-iotests/280 index 85e9114c5e..69288fdd0e 100755 --- a/tests/qemu-iotests/280 +++ b/tests/qemu-iotests/280 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2019 Red Hat, Inc. # diff --git a/tests/qemu-iotests/281 b/tests/qemu-iotests/281 index 269d583b2c..0bf973bca6 100755 --- a/tests/qemu-iotests/281 +++ b/tests/qemu-iotests/281 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Test cases for blockdev + IOThread interactions # diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283 new file mode 100644 index 0000000000..55b7cff953 --- /dev/null +++ b/tests/qemu-iotests/283 @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# +# Test for backup-top filter permission activation failure +# +# Copyright (c) 2019 Virtuozzo International GmbH. +# +# 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/>. +# + +import iotests + +# The test is unrelated to formats, restrict it to qcow2 to avoid extra runs +iotests.verify_image_format(supported_fmts=['qcow2']) + +size = 1024 * 1024 + +""" Test description + +When performing a backup, all writes on the source subtree must go through the +backup-top filter so it can copy all data to the target before it is changed. +backup-top filter is appended above source node, to achieve this thing, so all +parents of source node are handled. A configuration with side parents of source +sub-tree with write permission is unsupported (we'd have append several +backup-top filter like nodes to handle such parents). The test create an +example of such configuration and checks that a backup is then not allowed +(blockdev-backup command should fail). + +The configuration: + + ┌────────┐ target ┌─────────────┐ + │ target │ ◀─────── │ backup_top │ + └────────┘ └─────────────┘ + │ + │ backing + ▼ + ┌─────────────┐ + │ source │ + └─────────────┘ + │ + │ file + ▼ + ┌─────────────┐ write perm ┌───────┐ + │ base │ ◀──────────── │ other │ + └─────────────┘ └───────┘ + +On activation (see .active field of backup-top state in block/backup-top.c), +backup-top is going to unshare write permission on its source child. Write +unsharing will be propagated to the "source->base" link and will conflict with +other node write permission. So permission update will fail and backup job will +not be started. + +Note, that the only thing which prevents backup of running on such +configuration is default permission propagation scheme. It may be altered by +different block drivers, so backup will run in invalid configuration. But +something is better than nothing. Also, before the previous commit (commit +preceding this test creation), starting backup on such configuration led to +crash, so current "something" is a lot better, and this test actual goal is +to check that crash is fixed :) +""" + +vm = iotests.VM() +vm.launch() + +vm.qmp_log('blockdev-add', **{'node-name': 'target', 'driver': 'null-co'}) + +vm.qmp_log('blockdev-add', **{ + 'node-name': 'source', + 'driver': 'blkdebug', + 'image': {'node-name': 'base', 'driver': 'null-co', 'size': size} +}) + +vm.qmp_log('blockdev-add', **{ + 'node-name': 'other', + 'driver': 'blkdebug', + 'image': 'base', + 'take-child-perms': ['write'] +}) + +vm.qmp_log('blockdev-backup', sync='full', device='source', target='target') + +vm.shutdown() diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out new file mode 100644 index 0000000000..daaf5828c1 --- /dev/null +++ b/tests/qemu-iotests/283.out @@ -0,0 +1,8 @@ +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": {"driver": "null-co", "node-name": "base", "size": 1048576}, "node-name": "source"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}} +{"return": {}} +{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}} +{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}} diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 39ed5bc1be..f7a2d3d6c3 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -655,7 +655,15 @@ fi python_usable=false if $PYTHON -c 'import sys; sys.exit(0 if sys.version_info >= (3,6) else 1)' then - python_usable=true + # Our python framework also requires virtio-blk + if "$QEMU_PROG" -M none -device help | grep -q virtio-blk >/dev/null 2>&1 + then + python_usable=true + else + python_unusable_because="Missing virtio-blk in QEMU binary" + fi +else + python_unusable_because="Unsupported Python version" fi default_machine=$($QEMU_PROG -machine help | sed -n '/(default)/ s/ .*//p') @@ -838,12 +846,12 @@ do start=$(_wallclock) - if [ "$(head -n 1 "$source_iotests/$seq")" == "#!/usr/bin/env python" ]; then + if [ "$(head -n 1 "$source_iotests/$seq")" == "#!/usr/bin/env python3" ]; then if $python_usable; then run_command="$PYTHON $seq" else run_command="false" - echo "Unsupported Python version" > $seq.notrun + echo "$python_unusable_because" > $seq.notrun fi else run_command="./$seq" diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 9ccde32634..8a6366c09d 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -713,5 +713,19 @@ _require_large_file() rm "$TEST_IMG" } +# Check that a set of devices is available in the QEMU binary +# +_require_devices() +{ + available=$($QEMU -M none -device help | \ + grep ^name | sed -e 's/^name "//' -e 's/".*$//') + for device + do + if ! echo "$available" | grep -q "$device" ; then + _notrun "$device not available" + fi + done +} + # make sure this script returns success true diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index e041cc1ee3..1904223020 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -51,7 +51,7 @@ 027 rw auto quick 028 rw backing quick 029 rw auto quick -030 rw backing +030 rw auto backing 031 rw auto quick 032 rw auto quick 033 rw auto quick @@ -61,8 +61,8 @@ 037 rw auto backing quick 038 rw auto backing quick 039 rw auto quick -040 rw -041 rw backing +040 rw auto +041 rw auto backing 042 rw auto quick 043 rw auto backing 044 rw @@ -148,7 +148,7 @@ 124 rw backing 125 rw 126 rw auto backing -127 rw backing quick +127 rw auto backing quick 128 rw quick 129 rw quick 130 rw quick @@ -197,7 +197,7 @@ 177 rw auto quick 178 img 179 rw auto quick -181 rw migration +181 rw auto migration 182 rw quick 183 rw migration 184 rw auto quick @@ -218,7 +218,7 @@ 200 rw 201 rw migration 202 rw quick -203 rw migration +203 rw auto migration 204 rw quick 205 rw quick 206 rw @@ -270,7 +270,7 @@ 253 rw quick 254 rw backing quick 255 rw quick -256 rw quick +256 rw auto quick 257 rw 258 rw quick 260 rw quick @@ -289,3 +289,4 @@ 279 rw backing quick 280 rw migration quick 281 rw quick +283 auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 89aa2df2f3..0473e824ed 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -1,4 +1,3 @@ -from __future__ import print_function # Common utilities and Python wrappers for qemu-iotests # # Copyright (C) 2012 IBM Corp. @@ -931,9 +930,14 @@ def verify_protocol(supported=[], unsupported=[]): if not_sup or (imgproto in unsupported): notrun('not suitable for this protocol: %s' % imgproto) -def verify_platform(supported_oses=['linux']): - if True not in [sys.platform.startswith(x) for x in supported_oses]: - notrun('not suitable for this OS: %s' % sys.platform) +def verify_platform(supported=None, unsupported=None): + if unsupported is not None: + if any((sys.platform.startswith(x) for x in unsupported)): + notrun('not suitable for this OS: %s' % sys.platform) + + if supported is not None: + if not any((sys.platform.startswith(x) for x in supported)): + notrun('not suitable for this OS: %s' % sys.platform) def verify_cache_mode(supported_cache_modes=[]): if supported_cache_modes and (cachemode not in supported_cache_modes): @@ -1028,7 +1032,8 @@ def execute_unittest(output, verbosity, debug): sys.stderr.write(out) def execute_test(test_function=None, - supported_fmts=[], supported_oses=['linux'], + supported_fmts=[], + supported_platforms=None, supported_cache_modes=[], supported_aio_modes={}, unsupported_fmts=[], supported_protocols=[], unsupported_protocols=[]): @@ -1046,7 +1051,7 @@ def execute_test(test_function=None, verbosity = 1 verify_image_format(supported_fmts, unsupported_fmts) verify_protocol(supported_protocols, unsupported_protocols) - verify_platform(supported_oses) + verify_platform(supported=supported_platforms) verify_cache_mode(supported_cache_modes) verify_aio_mode(supported_aio_modes) diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py index 7e2dab6ea4..588d62aebf 100755 --- a/tests/qemu-iotests/nbd-fault-injector.py +++ b/tests/qemu-iotests/nbd-fault-injector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # NBD server - fault injection utility # # Configuration file syntax: @@ -43,7 +43,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. -from __future__ import print_function import sys import socket import struct diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index 91e4420b9f..94a07b2f6f 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import print_function import sys import struct import string diff --git a/tests/qemu-iotests/qed.py b/tests/qemu-iotests/qed.py index 8adaaf46c4..d6bec96069 100755 --- a/tests/qemu-iotests/qed.py +++ b/tests/qemu-iotests/qed.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Tool to manipulate QED image files # @@ -10,7 +10,6 @@ # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. -from __future__ import print_function import sys import struct import random diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index e7b58e3a0c..2167322985 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -68,6 +68,11 @@ static void v9fs_memread(P9Req *req, void *addr, size_t len) req->r_off += len; } +static void v9fs_uint8_read(P9Req *req, uint8_t *val) +{ + v9fs_memread(req, val, 1); +} + static void v9fs_uint16_write(P9Req *req, uint16_t val) { uint16_t le_val = cpu_to_le16(val); @@ -101,6 +106,12 @@ static void v9fs_uint32_read(P9Req *req, uint32_t *val) le32_to_cpus(val); } +static void v9fs_uint64_read(P9Req *req, uint64_t *val) +{ + v9fs_memread(req, val, 8); + le64_to_cpus(val); +} + /* len[2] string[len] */ static uint16_t v9fs_string_size(const char *string) { @@ -130,8 +141,9 @@ static void v9fs_string_read(P9Req *req, uint16_t *len, char **string) *len = local_len; } if (string) { - *string = g_malloc(local_len); + *string = g_malloc(local_len + 1); v9fs_memread(req, *string, local_len); + (*string)[local_len] = 0; } else { v9fs_memskip(req, local_len); } @@ -190,6 +202,7 @@ static const char *rmessage_name(uint8_t id) id == P9_RLOPEN ? "RLOPEN" : id == P9_RWRITE ? "RWRITE" : id == P9_RFLUSH ? "RFLUSH" : + id == P9_RREADDIR ? "READDIR" : "<unknown>"; } @@ -347,6 +360,82 @@ static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) v9fs_req_free(req); } +/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */ +static P9Req *v9fs_treaddir(QVirtio9P *v9p, uint32_t fid, uint64_t offset, + uint32_t count, uint16_t tag) +{ + P9Req *req; + + req = v9fs_req_init(v9p, 4 + 8 + 4, P9_TREADDIR, tag); + v9fs_uint32_write(req, fid); + v9fs_uint64_write(req, offset); + v9fs_uint32_write(req, count); + v9fs_req_send(req); + return req; +} + +struct V9fsDirent { + v9fs_qid qid; + uint64_t offset; + uint8_t type; + char *name; + struct V9fsDirent *next; +}; + +/* size[4] Rreaddir tag[2] count[4] data[count] */ +static void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, + struct V9fsDirent **entries) +{ + uint32_t local_count; + struct V9fsDirent *e = NULL; + uint16_t slen; + uint32_t n = 0; + + v9fs_req_recv(req, P9_RREADDIR); + v9fs_uint32_read(req, &local_count); + + if (count) { + *count = local_count; + } + + for (int32_t togo = (int32_t)local_count; + togo >= 13 + 8 + 1 + 2; + togo -= 13 + 8 + 1 + 2 + slen, ++n) + { + if (!e) { + e = g_malloc(sizeof(struct V9fsDirent)); + if (entries) { + *entries = e; + } + } else { + e = e->next = g_malloc(sizeof(struct V9fsDirent)); + } + e->next = NULL; + /* qid[13] offset[8] type[1] name[s] */ + v9fs_memread(req, &e->qid, 13); + v9fs_uint64_read(req, &e->offset); + v9fs_uint8_read(req, &e->type); + v9fs_string_read(req, &slen, &e->name); + } + + if (nentries) { + *nentries = n; + } + + v9fs_req_free(req); +} + +static void v9fs_free_dirents(struct V9fsDirent *e) +{ + struct V9fsDirent *next = NULL; + + for (; e; e = next) { + next = e->next; + g_free(e->name); + g_free(e); + } +} + /* size[4] Tlopen tag[2] fid[4] flags[4] */ static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags, uint16_t tag) @@ -479,6 +568,69 @@ static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc) g_free(wqid); } +static bool fs_dirents_contain_name(struct V9fsDirent *e, const char* name) +{ + for (; e; e = e->next) { + if (!strcmp(e->name, name)) { + return true; + } + } + return false; +} + +static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) }; + uint16_t nqid; + v9fs_qid qid; + uint32_t count, nentries; + struct V9fsDirent *entries = NULL; + P9Req *req; + + fs_attach(v9p, NULL, t_alloc); + req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, &nqid, NULL); + g_assert_cmpint(nqid, ==, 1); + + req = v9fs_tlopen(v9p, 1, O_DIRECTORY, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rlopen(req, &qid, NULL); + + /* + * submit count = msize - 11, because 11 is the header size of Rreaddir + */ + req = v9fs_treaddir(v9p, 1, 0, P9_MAX_SIZE - 11, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rreaddir(req, &count, &nentries, &entries); + + /* + * Assuming msize (P9_MAX_SIZE) is large enough so we can retrieve all + * dir entries with only one readdir request. + */ + g_assert_cmpint( + nentries, ==, + QTEST_V9FS_SYNTH_READDIR_NFILES + 2 /* "." and ".." */ + ); + + /* + * Check all file names exist in returned entries, ignore their order + * though. + */ + g_assert_cmpint(fs_dirents_contain_name(entries, "."), ==, true); + g_assert_cmpint(fs_dirents_contain_name(entries, ".."), ==, true); + for (int i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) { + char *name = g_strdup_printf(QTEST_V9FS_SYNTH_READDIR_FILE, i); + g_assert_cmpint(fs_dirents_contain_name(entries, name), ==, true); + g_free(name); + } + + v9fs_free_dirents(entries); + g_free(wnames[0]); +} + static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; @@ -657,6 +809,7 @@ static void register_virtio_9p_test(void) NULL); qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored, NULL); + qos_add_test("fs/readdir/basic", "virtio-9p", fs_readdir, NULL); } libqos_init(register_virtio_9p_test); diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index f6b5121f5c..d2299b98b7 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -61,7 +61,13 @@ run-memory-replay: memory-replay run-memory-record $(QEMU_OPTS) memory, \ "$< on $(TARGET_NAME)") -run-pauth-3: pauth-3 -pauth-3: CFLAGS += -march=armv8.3-a +EXTRA_TESTS+=memory-record memory-replay -EXTRA_TESTS+=memory-record memory-replay pauth-3 +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_3),) +pauth-3: CFLAGS += -march=armv8.3-a +else +pauth-3: + $(call skip-test, "BUILD of $@", "missing compiler support") +run-pauth-3: + $(call skip-test, "RUN of pauth-3", "not built") +endif diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index efa67cf1e9..8ed477d0d5 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -18,9 +18,11 @@ run-fcvt: fcvt $(call diff-out,$<,$(AARCH64_SRC)/fcvt.ref) # Pauth Tests +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_3),) AARCH64_TESTS += pauth-1 pauth-2 pauth-4 run-pauth-%: QEMU_OPTS += -cpu max pauth-%: CFLAGS += -march=armv8.3-a +endif # Semihosting smoke test for linux-user AARCH64_TESTS += semihosting diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index 210e68396f..9eb6ba3b7e 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -216,6 +216,24 @@ for target in $target_list; do echo "CROSS_CC_GUEST_STATIC=y" >> $config_target_mak fi echo "CROSS_CC_GUEST=$target_compiler" >> $config_target_mak + + # Test for compiler features for optional tests. We only do this + # for cross compilers because ensuring the docker containers based + # compilers is a requirememt for adding a new test that needs a + # compiler feature. + case $target in + aarch64-*) + if do_compiler "$target_compiler" $target_compiler_cflags \ + -march=armv8.1-a+sve -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_SVE=y" >> $config_target_mak + fi + if do_compiler "$target_compiler" $target_compiler_cflags \ + -march=-march=armv8.3-a -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak + fi + ;; + esac + enabled_cross_compilers="$enabled_cross_compilers $target_compiler" got_cross_cc=yes break diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index ed5dd4f3d0..4dee6647e6 100755..100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # # VM testing base class # @@ -12,7 +11,6 @@ # the COPYING file in the top-level directory. # -from __future__ import print_function import os import re import sys diff --git a/tests/vm/centos b/tests/vm/centos index f2f0befd84..a41ff109eb 100755 --- a/tests/vm/centos +++ b/tests/vm/centos @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # CentOS image # diff --git a/tests/vm/fedora b/tests/vm/fedora index 8e270fc0f0..4d7d6049f4 100755 --- a/tests/vm/fedora +++ b/tests/vm/fedora @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Fedora VM image # diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 33a736298a..fb54334696 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # FreeBSD VM image # diff --git a/tests/vm/netbsd b/tests/vm/netbsd index ec6f3563b2..c5069a45f4 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # NetBSD VM image # diff --git a/tests/vm/openbsd b/tests/vm/openbsd index d6173506f7..22cd9513dd 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # OpenBSD VM image # diff --git a/tests/vm/ubuntu.i386 b/tests/vm/ubuntu.i386 index 3834cd7a8d..48e9cb1ad3 100755 --- a/tests/vm/ubuntu.i386 +++ b/tests/vm/ubuntu.i386 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Ubuntu i386 image # diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h index 7a4c713559..aba13fef2d 100644 --- a/tools/virtiofsd/fuse.h +++ b/tools/virtiofsd/fuse.h @@ -1007,26 +1007,6 @@ void fuse_exit(struct fuse *f); struct fuse_context *fuse_get_context(void); /** - * Get the current supplementary group IDs for the current request - * - * Similar to the getgroups(2) system call, except the return value is - * always the total number of group IDs, even if it is larger than the - * specified size. - * - * The current fuse kernel module in linux (as of 2.6.30) doesn't pass - * the group list to userspace, hence this function needs to parse - * "/proc/$TID/task/$TID/status" to get the group IDs. - * - * This feature may not be supported on all operating systems. In - * such a case this function will return -ENOSYS. - * - * @param size size of given array - * @param list array of group IDs to be filled in - * @return the total number of supplementary group IDs or -errno on failure - */ -int fuse_getgroups(int size, gid_t list[]); - -/** * Check if the current request has already been interrupted * * @return 1 if the request has been interrupted, 0 otherwise diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c index de2e2e0c65..704c0369b2 100644 --- a/tools/virtiofsd/fuse_lowlevel.c +++ b/tools/virtiofsd/fuse_lowlevel.c @@ -1116,6 +1116,10 @@ static void do_read(fuse_req_t req, fuse_ino_t nodeid, struct fuse_file_info fi; arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; @@ -2667,83 +2671,6 @@ int fuse_lowlevel_is_virtio(struct fuse_session *se) return !!se->virtio_dev; } -#ifdef linux -int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) -{ - char *buf; - size_t bufsize = 1024; - char path[128]; - int ret; - int fd; - unsigned long pid = req->ctx.pid; - char *s; - - sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); - -retry: - buf = malloc(bufsize); - if (buf == NULL) { - return -ENOMEM; - } - - ret = -EIO; - fd = open(path, O_RDONLY); - if (fd == -1) { - goto out_free; - } - - ret = read(fd, buf, bufsize); - close(fd); - if (ret < 0) { - ret = -EIO; - goto out_free; - } - - if ((size_t)ret == bufsize) { - free(buf); - bufsize *= 4; - goto retry; - } - - ret = -EIO; - s = strstr(buf, "\nGroups:"); - if (s == NULL) { - goto out_free; - } - - s += 8; - ret = 0; - while (1) { - char *end; - unsigned long val = strtoul(s, &end, 0); - if (end == s) { - break; - } - - s = end; - if (ret < size) { - list[ret] = val; - } - ret++; - } - -out_free: - free(buf); - return ret; -} -#else /* linux */ -/* - * This is currently not implemented on other than Linux... - */ -int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) -{ - (void)req; - (void)size; - (void)list; - return -ENOSYS; -} -#endif - void fuse_session_exit(struct fuse_session *se) { se->exited = 1; diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h index 138041e5f1..8f6d705b5c 100644 --- a/tools/virtiofsd/fuse_lowlevel.h +++ b/tools/virtiofsd/fuse_lowlevel.h @@ -1705,27 +1705,6 @@ void *fuse_req_userdata(fuse_req_t req); const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); /** - * Get the current supplementary group IDs for the specified request - * - * Similar to the getgroups(2) system call, except the return value is - * always the total number of group IDs, even if it is larger than the - * specified size. - * - * The current fuse kernel module in linux (as of 2.6.30) doesn't pass - * the group list to userspace, hence this function needs to parse - * "/proc/$TID/task/$TID/status" to get the group IDs. - * - * This feature may not be supported on all operating systems. In - * such a case this function will return -ENOSYS. - * - * @param req request handle - * @param size size of given array - * @param list array of group IDs to be filled in - * @return the total number of supplementary group IDs or -errno on failure - */ -int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]); - -/** * Callback function for an interrupt * * @param req interrupted request diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 80a6e929df..dd1c605dbf 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -916,6 +916,7 @@ static int fv_create_listen_socket(struct fuse_session *se) old_umask = umask(0077); if (bind(listen_sock, (struct sockaddr *)&un, addr_len) == -1) { fuse_log(FUSE_LOG_ERR, "vhost socket bind: %m\n"); + close(listen_sock); umask(old_umask); return -1; } @@ -923,6 +924,7 @@ static int fv_create_listen_socket(struct fuse_session *se) if (listen(listen_sock, 1) == -1) { fuse_log(FUSE_LOG_ERR, "vhost socket listen: %m\n"); + close(listen_sock); return -1; } diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index e6f2399efc..c635fc8820 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -232,6 +232,7 @@ static int load_capng(void) */ cap.saved = capng_save_state(); if (!cap.saved) { + pthread_mutex_unlock(&cap.mutex); fuse_log(FUSE_LOG_ERR, "capng_save_state (thread)\n"); return -EINVAL; } diff --git a/ui/cocoa.m b/ui/cocoa.m index fbb5b1b45f..747a70839a 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -42,60 +42,10 @@ #include <Carbon/Carbon.h> #include "hw/core/cpu.h" -#ifndef MAC_OS_X_VERSION_10_5 -#define MAC_OS_X_VERSION_10_5 1050 -#endif -#ifndef MAC_OS_X_VERSION_10_6 -#define MAC_OS_X_VERSION_10_6 1060 -#endif -#ifndef MAC_OS_X_VERSION_10_9 -#define MAC_OS_X_VERSION_10_9 1090 -#endif -#ifndef MAC_OS_X_VERSION_10_10 -#define MAC_OS_X_VERSION_10_10 101000 -#endif -#ifndef MAC_OS_X_VERSION_10_12 -#define MAC_OS_X_VERSION_10_12 101200 -#endif #ifndef MAC_OS_X_VERSION_10_13 #define MAC_OS_X_VERSION_10_13 101300 #endif -/* macOS 10.12 deprecated many constants, #define the new names for older SDKs */ -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -#define NSEventMaskAny NSAnyEventMask -#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask -#define NSEventModifierFlagShift NSShiftKeyMask -#define NSEventModifierFlagCommand NSCommandKeyMask -#define NSEventModifierFlagControl NSControlKeyMask -#define NSEventModifierFlagOption NSAlternateKeyMask -#define NSEventTypeFlagsChanged NSFlagsChanged -#define NSEventTypeKeyUp NSKeyUp -#define NSEventTypeKeyDown NSKeyDown -#define NSEventTypeMouseMoved NSMouseMoved -#define NSEventTypeLeftMouseDown NSLeftMouseDown -#define NSEventTypeRightMouseDown NSRightMouseDown -#define NSEventTypeOtherMouseDown NSOtherMouseDown -#define NSEventTypeLeftMouseDragged NSLeftMouseDragged -#define NSEventTypeRightMouseDragged NSRightMouseDragged -#define NSEventTypeOtherMouseDragged NSOtherMouseDragged -#define NSEventTypeLeftMouseUp NSLeftMouseUp -#define NSEventTypeRightMouseUp NSRightMouseUp -#define NSEventTypeOtherMouseUp NSOtherMouseUp -#define NSEventTypeScrollWheel NSScrollWheel -#define NSTextAlignmentCenter NSCenterTextAlignment -#define NSWindowStyleMaskBorderless NSBorderlessWindowMask -#define NSWindowStyleMaskClosable NSClosableWindowMask -#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask -#define NSWindowStyleMaskTitled NSTitledWindowMask -#endif -/* 10.13 deprecates NSFileHandlingPanelOKButton in favour of - * NSModalResponseOK, which was introduced in 10.9. Define - * it for older versions. - */ -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 -#define NSModalResponseOK NSFileHandlingPanelOKButton -#endif /* 10.14 deprecates NSOnState and NSOffState in favor of * NSControlStateValueOn/Off, which were introduced in 10.13. * Define for older versions @@ -125,6 +75,7 @@ typedef struct { NSWindow *normalWindow, *about_window; static DisplayChangeListener *dcl; static int last_buttons; +static int cursor_hide = 1; int gArgc; char **gArgv; @@ -465,11 +416,7 @@ QemuCocoaView *cocoaView; COCOA_DEBUG("QemuCocoaView: drawRect\n"); // get CoreGraphic context -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 - CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; -#else CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext]; -#endif CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); CGContextSetShouldAntialias (viewContextRef, NO); @@ -1075,9 +1022,7 @@ QemuCocoaView *cocoaView; ------------------------------------------------------ */ @interface QemuCocoaAppController : NSObject -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) <NSWindowDelegate, NSApplicationDelegate> -#endif { } - (void)doToggleFullScreen:(id)sender; @@ -1126,9 +1071,6 @@ QemuCocoaView *cocoaView; [normalWindow setAcceptsMouseMovedEvents:YES]; [normalWindow setTitle:@"QEMU"]; [normalWindow setContentView:cocoaView]; -#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) - [normalWindow useOptimizedDrawing:YES]; -#endif [normalWindow makeKeyAndOrderFront:self]; [normalWindow center]; [normalWindow setDelegate: self]; @@ -1918,6 +1860,9 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; }); } + if (opts->has_show_cursor && opts->show_cursor) { + cursor_hide = 0; + } dcl = g_malloc0(sizeof(DisplayChangeListener)); @@ -1965,17 +1965,19 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) * If available, return the refresh rate of the display in milli-Hertz, * else return 0. */ -static int gd_refresh_rate_millihz(GtkDisplayState *s) +static int gd_refresh_rate_millihz(GtkWidget *window) { #ifdef GDK_VERSION_3_22 - GdkDisplay *dpy = gtk_widget_get_display(s->window); - GdkWindow *win = gtk_widget_get_window(s->window); - GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); + GdkWindow *win = gtk_widget_get_window(window); - return gdk_monitor_get_refresh_rate(monitor); -#else - return 0; + if (win) { + GdkDisplay *dpy = gtk_widget_get_display(window); + GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); + + return gdk_monitor_get_refresh_rate(monitor); + } #endif + return 0; } static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, @@ -2045,7 +2047,8 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, vc->gfx.kbd = qkbd_state_init(con); vc->gfx.dcl.con = con; - refresh_rate_millihz = gd_refresh_rate_millihz(s); + refresh_rate_millihz = gd_refresh_rate_millihz(vc->window ? + vc->window : s->window); if (refresh_rate_millihz) { vc->gfx.dcl.update_interval = MILLISEC_PER_SEC / refresh_rate_millihz; } @@ -2243,8 +2246,12 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) textdomain("qemu"); window_display = gtk_widget_get_display(s->window); - s->null_cursor = gdk_cursor_new_for_display(window_display, - GDK_BLANK_CURSOR); + if (s->opts->has_show_cursor && s->opts->show_cursor) { + s->null_cursor = NULL; /* default pointer */ + } else { + s->null_cursor = gdk_cursor_new_for_display(window_display, + GDK_BLANK_CURSOR); + } s->mouse_mode_notifier.notify = gd_mouse_mode_change; qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); @@ -161,9 +161,9 @@ static void sdl_update_caption(struct sdl2_console *scon) } } -static void sdl_hide_cursor(void) +static void sdl_hide_cursor(struct sdl2_console *scon) { - if (!cursor_hide) { + if (scon->opts->has_show_cursor && scon->opts->show_cursor) { return; } @@ -175,9 +175,9 @@ static void sdl_hide_cursor(void) } } -static void sdl_show_cursor(void) +static void sdl_show_cursor(struct sdl2_console *scon) { - if (!cursor_hide) { + if (scon->opts->has_show_cursor && scon->opts->show_cursor) { return; } @@ -216,7 +216,7 @@ static void sdl_grab_start(struct sdl2_console *scon) SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); } } else { - sdl_hide_cursor(); + sdl_hide_cursor(scon); } SDL_SetWindowGrab(scon->real_window, SDL_TRUE); gui_grab = 1; @@ -227,7 +227,7 @@ static void sdl_grab_end(struct sdl2_console *scon) { SDL_SetWindowGrab(scon->real_window, SDL_FALSE); gui_grab = 0; - sdl_show_cursor(); + sdl_show_cursor(scon); sdl_update_caption(scon); } @@ -658,7 +658,7 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl, if (on) { if (!guest_cursor) { - sdl_show_cursor(); + sdl_show_cursor(scon); } if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { SDL_SetCursor(guest_sprite); @@ -667,7 +667,7 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl, } } } else if (gui_grab) { - sdl_hide_cursor(); + sdl_hide_cursor(scon); } guest_cursor = on; guest_x = x, guest_y = y; @@ -168,7 +168,6 @@ int no_hpet = 0; int fd_bootchk = 1; static int no_reboot; int no_shutdown = 0; -int cursor_hide = 1; int graphic_rotate = 0; const char *watchdog; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; @@ -1931,6 +1930,16 @@ static void parse_display(const char *p) } else { goto invalid_sdl_args; } + } else if (strstart(opts, ",show-cursor=", &nextopt)) { + opts = nextopt; + dpy.has_show_cursor = true; + if (strstart(opts, "on", &nextopt)) { + dpy.show_cursor = true; + } else if (strstart(opts, "off", &nextopt)) { + dpy.show_cursor = false; + } else { + goto invalid_sdl_args; + } } else if (strstart(opts, ",gl=", &nextopt)) { opts = nextopt; dpy.has_gl = true; @@ -3553,7 +3562,10 @@ int main(int argc, char **argv, char **envp) no_shutdown = 1; break; case QEMU_OPTION_show_cursor: - cursor_hide = 0; + warn_report("The -show-cursor option is deprecated, " + "use -display {sdl,gtk},show-cursor=on instead"); + dpy.has_show_cursor = true; + dpy.show_cursor = true; break; case QEMU_OPTION_uuid: if (qemu_uuid_parse(optarg, &qemu_uuid) < 0) { |